<commit_before>/*jslint forin: true */
var ref = require('ref');
var StructType = require('ref-struct');
var ArrayType = require('ref-array');

var node = StructType({
    edges: "pointer",

    edge_len: "uchar",
    compare_type: "uchar",
    endpoint: "uchar",
    ov_cnt: "uchar",

    edge_cap: "uchar",
    route_len: "uchar",
    route_cap: "uchar",

    pcre_pattern: "pointer",
    pcre_extra: "pointer",

    routes: "pointer",

    combined_pattern: "string",

    data: "pointer"
});

var route = StructType({
    path: "string",
    path_len: "int",

    request_method: "int",

    host: "string",
    host_len: "int",

    data: "pointer",

    remote_addr_pattern: "string",
    remote_addr_pattern_len: "int"
});

var StringArray = ArrayType("string");
var str_array = StructType({
    tokens: "pointer",
    len: "int",
    cap: "int"
});

var match_entry = StructType({
    vars: ref.refType(str_array),
    path: "string",
    path_len: "int",
    request_method: "int",

    data: "pointer",

    host: "pointer",
    host_len: "int",

    remote_addr: "pointer",
    remote_addr_len: "int"
});

// edge = StructType({
    // pattern: "string",
    // child: "pointer",
    // pattern_len: "ushort",
    // opcode: "uchar",
    // has_slug: "uchar"
// });

const METHOD_GET = 2;
const METHOD_POST = 2<<1;
const METHOD_PUT = 2<<2;
const METHOD_DELETE = 2<<3;
const METHOD_PATCH = 2<<4;
const METHOD_HEAD = 2<<5;
const METHOD_OPTIONS = 2<<6;

const METHODS = {
  GET: METHOD_GET,
  POST: METHOD_POST,
  PUT: METHOD_PUT,
  DELETE: METHOD_DELETE,
  PATCH: METHOD_PATCH,
  HEAD: METHOD_HEAD,
  OPTIONS: METHOD_OPTIONS
};

var ffi = require('ffi');

var libr3 = ffi.Library('libr3', {
    "r3_tree_create": ["pointer", ["int"]],

    // node * r3_tree_insert_pathl_ex(node *tree, const char *path, int path_len, route * route, void * data, char ** errstr);
    "r3_tree_insert_pathl_ex": ["pointer", ["pointer", "string", "int", "pointer", "pointer", "pointer"]],

    // route * r3_tree_insert_routel_ex(node *tree, int method, const char *path, int path_len, void *data, char **errstr);
    "r3_tree_insert_routel_ex": ["pointer", ["pointer", "int", "string", "int", "pointer", "pointer"]],

    "r3_tree_compile": ["void", ["pointer"]],
    "r3_tree_dump": ["void", ["pointer", "int"]],

    // node * r3_tree_matchl(const node * n, const char * path, int path_len, match_entry * entry);
    "r3_tree_matchl": [ref.refType(node), ["pointer", "string", "int", "pointer"]],

    // route * r3_tree_match_route(const node *n, match_entry * entry);
    "r3_tree_match_route": [ref.refType(route), ["pointer", "pointer"]],

    "r3_tree_free": ["void", ["pointer"]],

    "match_entry_createl": [ref.refType(match_entry), ["string", "int"]],
});

var r3_tree_insert_path = function (tree, path, data) {
    return libr3.r3_tree_insert_pathl_ex(tree, path, path.length, null, data, null);
};

var r3_tree_insert_route = function (tree, method, path, data) {
    return libr3.r3_tree_insert_routel_ex(tree, method, path, path.length, data, null);
};

var match_entry_create = function (path) {
    return libr3.match_entry_createl(path, path.length);
};

var r3_tree_match = function (tree, path, entry) {
    return libr3.r3_tree_matchl(tree, path, path.length, entry);
};

var Router = function (routes) {
    var route, data, method, route_frag;
    this.tree = libr3.r3_tree_create(10);
    for (route in routes) {
        data = routes[route];
        data = new Buffer(data + '\u0000');
        route = route.trim();
        route_frag = route.split(' ');
        if (route_frag.length > 1) {
          route = route_frag[1];
          method = METHODS[route_frag[0].toUpperCase()];
          if (!method) { throw new Error(route_frag[0] + "method not exist."); }
          r3_tree_insert_route(this.tree, method, route, data);
        } else {
          r3_tree_insert_route(this.tree, 0, route, data);
        }
    }
    libr3.r3_tree_compile(this.tree);
    return this;
};

Router.prototype.dump = function () {
    if (!this.tree) { return; }
    libr3.r3_tree_dump(this.tree, 0);
};

Router.prototype.match = function (path) {
    if (!this.tree) { return; }
    var entry;

    path = path.trim();
    var path_frag = path.split(' ');
    if (path_frag.length > 1) {
      path = path_frag[1];
      entry = match_entry_create(path);
      method = METHODS[path_frag[0].toUpperCase()];
      entry.deref().request_method = method;
    } else {
      entry = match_entry_create(path);
    }

    var node = libr3.r3_tree_match_route(this.tree, entry);

    if (ref.isNull(node)) {
        return;
    }

    var data = ref.readCString(node.deref().data, 0);

    var vars = entry.deref().vars.deref();
    var capturesBuffer = new StringArray(ref.reinterpret(vars.tokens, vars.len * ref.types.CString.size));
    var captures = [], i;
    for (i = 0; i < capturesBuffer.length; i++) {
        captures.push(capturesBuffer[i]);
    }

    return [data, captures];
};

Router.prototype.free = function () {
    if (!this.tree) { return; }
    libr3.r3_tree_free(this.tree);
    this.tree = null;
};

exports.new = function (routes) {
    return new Router(routes);
};

<commit_msg>Save data in JS side, use data array<commit_after>/*jslint forin: true */
var ref = require('ref');
var StructType = require('ref-struct');
var ArrayType = require('ref-array');

var node = StructType({
    edges: "pointer",

    edge_len: "uchar",
    compare_type: "uchar",
    endpoint: "uchar",
    ov_cnt: "uchar",

    edge_cap: "uchar",
    route_len: "uchar",
    route_cap: "uchar",

    pcre_pattern: "pointer",
    pcre_extra: "pointer",

    routes: "pointer",

    combined_pattern: "string",

    data: "pointer"
});

var route = StructType({
    path: "string",
    path_len: "int",

    request_method: "int",

    host: "string",
    host_len: "int",

    data: "pointer",

    remote_addr_pattern: "string",
    remote_addr_pattern_len: "int"
});

var StringArray = ArrayType("string");
var str_array = StructType({
    tokens: "pointer",
    len: "int",
    cap: "int"
});

var match_entry = StructType({
    vars: ref.refType(str_array),
    path: "string",
    path_len: "int",
    request_method: "int",

    data: "pointer",

    host: "pointer",
    host_len: "int",

    remote_addr: "pointer",
    remote_addr_len: "int"
});

// edge = StructType({
    // pattern: "string",
    // child: "pointer",
    // pattern_len: "ushort",
    // opcode: "uchar",
    // has_slug: "uchar"
// });

const METHOD_GET = 2;
const METHOD_POST = 2<<1;
const METHOD_PUT = 2<<2;
const METHOD_DELETE = 2<<3;
const METHOD_PATCH = 2<<4;
const METHOD_HEAD = 2<<5;
const METHOD_OPTIONS = 2<<6;

const METHODS = {
  GET: METHOD_GET,
  POST: METHOD_POST,
  PUT: METHOD_PUT,
  DELETE: METHOD_DELETE,
  PATCH: METHOD_PATCH,
  HEAD: METHOD_HEAD,
  OPTIONS: METHOD_OPTIONS
};

var ffi = require('ffi');

var libr3 = ffi.Library('libr3', {
    "r3_tree_create": ["pointer", ["int"]],

    // node * r3_tree_insert_pathl_ex(node *tree, const char *path, int path_len, route * route, void * data, char ** errstr);
    "r3_tree_insert_pathl_ex": ["pointer", ["pointer", "string", "int", "pointer", "pointer", "pointer"]],

    // route * r3_tree_insert_routel_ex(node *tree, int method, const char *path, int path_len, void *data, char **errstr);
    "r3_tree_insert_routel_ex": ["pointer", ["pointer", "int", "string", "int", "pointer", "pointer"]],

    "r3_tree_compile": ["void", ["pointer"]],
    "r3_tree_dump": ["void", ["pointer", "int"]],

    // node * r3_tree_matchl(const node * n, const char * path, int path_len, match_entry * entry);
    "r3_tree_matchl": [ref.refType(node), ["pointer", "string", "int", "pointer"]],

    // route * r3_tree_match_route(const node *n, match_entry * entry);
    "r3_tree_match_route": [ref.refType(route), ["pointer", "pointer"]],

    "r3_tree_free": ["void", ["pointer"]],

    "match_entry_createl": [ref.refType(match_entry), ["string", "int"]],
});

var r3_tree_insert_path = function (tree, path, data) {
    return libr3.r3_tree_insert_pathl_ex(tree, path, path.length, null, data, null);
};

var r3_tree_insert_route = function (tree, method, path, data) {
    return libr3.r3_tree_insert_routel_ex(tree, method, path, path.length, data, null);
};

var match_entry_create = function (path) {
    return libr3.match_entry_createl(path, path.length);
};

var r3_tree_match = function (tree, path, entry) {
    return libr3.r3_tree_matchl(tree, path, path.length, entry);
};

var Router = function (routes) {
    var route, data, method, route_frag, i = 0;
    this.tree = libr3.r3_tree_create(10);
    this.data = [];
    for (route in routes) {
        this.data[i] = routes[route];
        data = new Buffer(4);
        data.writeUInt16LE(i, 0);
        route = route.trim();
        route_frag = route.split(' ');
        if (route_frag.length > 1) {
          route = route_frag[1];
          method = METHODS[route_frag[0].toUpperCase()];
          if (!method) { throw new Error(route_frag[0] + "method not exist."); }
          r3_tree_insert_route(this.tree, method, route, data);
        } else {
          r3_tree_insert_route(this.tree, 0, route, data);
        }
        i++;
    }
    libr3.r3_tree_compile(this.tree);
    return this;
};

Router.prototype.dump = function () {
    if (!this.tree) { return; }
    libr3.r3_tree_dump(this.tree, 0);
};

Router.prototype.match = function (path) {
    if (!this.tree) { return; }
    var entry;

    path = path.trim();
    var path_frag = path.split(' ');
    if (path_frag.length > 1) {
      path = path_frag[1];
      entry = match_entry_create(path);
      method = METHODS[path_frag[0].toUpperCase()];
      entry.deref().request_method = method;
    } else {
      entry = match_entry_create(path);
    }

    var node = libr3.r3_tree_match_route(this.tree, entry);

    if (ref.isNull(node)) {
        return;
    }

    var count = node.deref().data.reinterpret(4).readUInt16LE(0);
    var data = this.data[count];

    var vars = entry.deref().vars.deref();
    var capturesBuffer = new StringArray(vars.tokens.reinterpret(vars.len * ref.types.CString.size));
    var captures = [], i;
    for (i = 0; i < capturesBuffer.length; i++) {
        captures.push(capturesBuffer[i]);
    }

    return [data, captures];
};

Router.prototype.free = function () {
    if (!this.tree) { return; }
    libr3.r3_tree_free(this.tree);
    this.tree = null;
};

exports.new = function (routes) {
    return new Router(routes);
};

<|endoftext|>


<commit_before><commit_msg>adds better fallback image for CIP profiles<commit_after><|endoftext|>


<commit_before>var Backbone   = require('backbone')
,   relational = require('backbone-relational')
,   config     = require('mbc-common').config.Mosto.General
,   path       = require('path')
,   uuid       = require('node-uuid')
,   _          = require('underscore')
,   moment     = require('moment')
;

var Mosto = {};

Mosto.Media = Backbone.RelationalModel.extend({
    defaults: {
        playlist_order: undefined,
        actual_order: undefined,
        name: '',
        type: 'default',
        file: undefined,
        length: undefined,
        fps: undefined,
        start: undefined,
        end: undefined,
    },
});

Mosto.BlankClip = new Mosto.Media({
    name: path.basename(config.black).replace(path.extname(config.black, '')),
    file: config.black,
    fps: config.fps,
})

Mosto.MediaCollection = Backbone.Collection.extend({
    /* this is a playlist's list of clips */
    model: Mosto.Media,
    comparator: function(model) {
        return model.get('playlist_order');
    },
});

Mosto.Playlist = Backbone.RelationalModel.extend({
    defaults: {
        name: null,
        start: null,   // moment
        end: null,     // moment
        mode: "snap",
        loaded: false,
    },
    relations:[{
        type: Backbone.HasMany,
        key: 'medias',
        relatedModel: Mosto.Media,
        collectionType: Mosto.MediaCollection,
        reverseRelation: {
            key: 'playlist'
        },
    }],
    initialize: function (attributes, options) {
        console.log ('creating new Mosto.Playlist', attributes, options);
        Backbone.Model.prototype.initialize.apply(this, attributes, options);
        this.set('name', this.get('name') || this.get('_id'));

        if( !attributes.start )
            throw new Error("Must provide a start date");
        this.set('start', moment(this.get('start')));
        if (!attributes.end )
            throw new Error("Must provide an end date");
        this.set('end', moment(this.get('end')));
    },
});

Mosto.PlaylistCollection = Backbone.Collection.extend({
    model: Mosto.Playlist,
    comparator: function(playlist) {
        // remember, comparator is called only on insert, update doesn't re-sort
        //  but .add(.., {merge: true}) DOES re-sort!
        return playlist.get("start");
    },
    initialize: function() {
        this.on('sorted', this.addBlanks.bind(this));
    },
    sort: function(options) {
        this.removeBlanks({ silent: true });
        Backbone.Collection.sort.apply(this);
    },
    removeBlanks: function(options) {
    },
    addBlanks: function(collection, options) {
        for(var i=1 ; i < this.length ; i++) {
            var prev = this.at(i-1);
            var curr = this.at(i);
            var end = prev.get('end');
            var start = curr.get('start');
            if( end < start ) {
                this.addBlankPlaylist(end, start, i);
            }
        }
    },
    addBlankPlaylist: function(from, to, at) {
        var options = at && { at: at } || {};
        var blank = Mosto.BlankClip.clone();
        var duration = to - from;
        var playlist = new Playlist({
            name: blank.get('name'),
            start: from,
            end: to,
        });
        blank.set({'id': uuid.v4()});
        playlist.medias.add(blank);
        this.add(playlist, options);
    },
});

Mosto.LoadedPlaylists = Backbone.RelationalModel.extend({
    relations: [{
        type: Backbone.HasMany,
        key: 'playlists',
        relatedModel: Mosto.Playlist,
        collectionType: Mosto.PlaylistCollection,
    }],
});

exports = module.exports = Mosto;
<commit_msg>remove blanks<commit_after>var Backbone   = require('backbone')
,   relational = require('backbone-relational')
,   config     = require('mbc-common').config.Mosto.General
,   path       = require('path')
,   uuid       = require('node-uuid')
,   _          = require('underscore')
,   moment     = require('moment')
;

var Mosto = {};

Mosto.Media = Backbone.RelationalModel.extend({
    defaults: {
        playlist_order: undefined,
        actual_order: undefined,
        name: '',
        type: 'default',
        file: undefined,
        length: undefined,
        fps: undefined,
        start: undefined,
        end: undefined,
    },
});

Mosto.BlankClip = new Mosto.Media({
    name: path.basename(config.black).replace(path.extname(config.black, '')),
    file: config.black,
    fps: config.fps,
})

Mosto.MediaCollection = Backbone.Collection.extend({
    /* this is a playlist's list of clips */
    model: Mosto.Media,
    comparator: function(model) {
        return model.get('playlist_order');
    },
});

Mosto.Playlist = Backbone.RelationalModel.extend({
    defaults: {
        name: null,
        start: null,   // moment
        end: null,     // moment
        mode: "snap",
        loaded: false,
    },
    relations:[{
        type: Backbone.HasMany,
        key: 'medias',
        relatedModel: Mosto.Media,
        collectionType: Mosto.MediaCollection,
        reverseRelation: {
            key: 'playlist'
        },
    }],
    initialize: function (attributes, options) {
        console.log ('creating new Mosto.Playlist', attributes, options);
        Backbone.Model.prototype.initialize.apply(this, attributes, options);
        this.set('name', this.get('name') || this.get('_id'));

        if( !attributes.start )
            throw new Error("Must provide a start date");
        this.set('start', moment(this.get('start')));
        if (!attributes.end )
            throw new Error("Must provide an end date");
        this.set('end', moment(this.get('end')));
    },
});

Mosto.PlaylistCollection = Backbone.Collection.extend({
    model: Mosto.Playlist,
    comparator: function(playlist) {
        // remember, comparator is called only on insert, update doesn't re-sort
        //  but .add(.., {merge: true}) DOES re-sort!
        return playlist.get("start");
    },
    initialize: function() {
        this.on('sorted', this.addBlanks.bind(this));
    },
    sort: function(options) {
        this.removeBlanks({ silent: true });
        Backbone.Collection.sort.apply(this);
    },
    removeBlanks: function(options) {
        /*
         * forwards `options` to `remove`
         */
        this.remove(this.where({ name: Mosto.BlankClip.get('name') }),
                    options);
    },
    addBlanks: function(collection, options) {
        for(var i=1 ; i < this.length ; i++) {
            var prev = this.at(i-1);
            var curr = this.at(i);
            var end = prev.get('end');
            var start = curr.get('start');
            if( end < start ) {
                this.addBlankPlaylist(end, start, i);
            }
        }
    },
    addBlankPlaylist: function(from, to, at) {
        var options = at && { at: at } || {};
        var blank = Mosto.BlankClip.clone();
        var duration = to - from;
        var playlist = new Playlist({
            name: blank.get('name'),
            start: from,
            end: to,
        });
        blank.set({'id': uuid.v4()});
        playlist.medias.add(blank);
        this.add(playlist, options);
    },
});

Mosto.LoadedPlaylists = Backbone.RelationalModel.extend({
    relations: [{
        type: Backbone.HasMany,
        key: 'playlists',
        relatedModel: Mosto.Playlist,
        collectionType: Mosto.PlaylistCollection,
    }],
});

exports = module.exports = Mosto;
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');

var handlers = {
    NICK: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();

        this.emit('nick', {
            nick: command.nick,
            ident: command.ident,
            hostname: command.hostname,
            new_nick: command.params[0],
            time: time
        });
    },

    ACCOUNT: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();

        var account = command.params[0] === '*' ?
            false :
            command.params[0];

        this.emit('account', {
            nick: command.nick,
            ident: command.ident,
            hostname: command.hostname,
            account: account,
            time: time
        });
    },

    // If the chghost CAP is enabled and 'enable_chghost' option is true
    CHGHOST: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();

        this.emit('user updated', {
            nick: command.nick,
            ident: command.ident,
            hostname: command.hostname,
            new_ident: command.params[0],
            new_host: command.params[1],
            time: time
        });
    },

    AWAY: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();
        const message = command.params[command.params.length - 1];
        if (message === '') { // back
            this.emit('back', {
                nick: command.nick,
                message: '',
                time: time
            });
        } else {
            this.emit('away', {
                nick: command.nick,
                message: message,
                time: time
            });
        }
    },

    RPL_NOWAWAY: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();

        this.emit('away', {
            nick: command.nick,
            message: command.params[command.params.length - 1],
            time: time
        });
    },

    RPL_UNAWAY: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();

        this.emit('back', {
            nick: command.nick,
            message: command.params[command.params.length - 1] || '', // example: "<nick> is now back."
            time: time
        });
    },

    RPL_ISON: function(command) {
        this.emit('users online', {
            nicks: (command.params[command.params.length - 1] || '').split(' '),
        });
    },

    ERR_NICKNAMEINUSE: function(command) {
        this.emit('nick in use', {
            nick: command.params[1],
            reason: command.params[command.params.length - 1]
        });
    },

    ERR_ERRONEUSNICKNAME: function(command) {
        this.emit('nick invalid', {
            nick: command.params[1],
            reason: command.params[command.params.length - 1]
        });
    },

    RPL_ENDOFWHOIS: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);

        if (!cache.nick) {
            cache.nick = command.params[1];
            cache.error = 'not_found';
        }

        this.emit('whois', cache);
        cache.destroy();
    },

    RPL_AWAY: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.away = command.params[command.params.length - 1] || 'is away';
    },

    RPL_WHOISUSER: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.nick = command.params[1];
        cache.user = command.params[2];
        cache.host = command.params[3];
        cache.real_name = command.params[5];
    },

    RPL_WHOISHELPOP: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.helpop = command.params[command.params.length - 1];
    },

    RPL_WHOISBOT: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.bot = command.params[command.params.length - 1];
    },

    RPL_WHOISSERVER: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.server = command.params[2];
        cache.server_info = command.params[command.params.length - 1];
    },

    RPL_WHOISOPERATOR: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.operator = command.params[command.params.length - 1];
    },

    RPL_WHOISCHANNELS:       function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        if (cache.channels) {
            cache.channels += ' ' + command.params[command.params.length - 1];
        } else {
            cache.channels = command.params[command.params.length - 1];
        }
    },

    RPL_WHOISMODES: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.modes = command.params[command.params.length - 1];
    },

    RPL_WHOISIDLE: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.idle = command.params[2];
        if (command.params[3]) {
            cache.logon = command.params[3];
        }
    },

    RPL_WHOISREGNICK: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.registered_nick = command.params[command.params.length - 1];
    },

    RPL_WHOISHOST: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);

        var last_param = command.params[command.params.length - 1];
        // <source> 378 <target> <nick> :is connecting from <user>@<host> <ip>
        var match = last_param.match(/.*@([^ ]+) ([^ ]+).*$/);  // https://regex101.com/r/AQz7RE/2

        if (!match) {
            return;
        }

        cache.actualip = match[2];
        cache.actualhost = match[1];
    },

    RPL_WHOISSECURE: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.secure = true;
    },

    RPL_WHOISACCOUNT: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.account = command.params[2];
    },

    RPL_WHOISSPECIAL: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.special = command.params[command.params.length - 1];
    },

    RPL_WHOISACTUALLY: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);

        // <source> 338 <target> <nick> <user>@<host> <ip> :Actual user@host, Actual IP
        var user_host = command.params[command.params.length - 3];
        var host = user_host.substring(user_host.indexOf("@") + 1);
        var ip = command.params[command.params.length - 2];

        cache.actualip = ip;
        cache.actualhost = host;
    },

    RPL_WHOWASUSER: function(command) {
        this.emit('whowas', {
            nick: command.params[1],
            ident: command.params[2],
            host: command.params[3],
            real_name: command.params[command.params.length - 1]
        });
    },

    RPL_ENDOFWHOWAS: function() {
        // noop
    },

    ERR_WASNOSUCHNICK: function(command) {
        this.emit('whowas', {
            nick: command.params[1],
            error: 'no_such_nick'
        });
    },

    RPL_UMODEIS: function(command) {
        // this.connection.umodes = the modes
        // TODO: this
    },

    RPL_HOSTCLOACKING: function(command) {
        this.emit('displayed host', {
            nick: command.params[0],
            host: command.params[1]
        });
    }
};

module.exports = function AddCommandHandlers(command_controller) {
    _.each(handlers, function(handler, handler_command) {
        command_controller.addHandler(handler_command, handler);
    });
};
<commit_msg>Cover the case where RPL_WHOISACTUALLY is not the expected format #137<commit_after>'use strict';

var _ = require('lodash');

var handlers = {
    NICK: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();

        this.emit('nick', {
            nick: command.nick,
            ident: command.ident,
            hostname: command.hostname,
            new_nick: command.params[0],
            time: time
        });
    },

    ACCOUNT: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();

        var account = command.params[0] === '*' ?
            false :
            command.params[0];

        this.emit('account', {
            nick: command.nick,
            ident: command.ident,
            hostname: command.hostname,
            account: account,
            time: time
        });
    },

    // If the chghost CAP is enabled and 'enable_chghost' option is true
    CHGHOST: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();

        this.emit('user updated', {
            nick: command.nick,
            ident: command.ident,
            hostname: command.hostname,
            new_ident: command.params[0],
            new_host: command.params[1],
            time: time
        });
    },

    AWAY: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();
        const message = command.params[command.params.length - 1];
        if (message === '') { // back
            this.emit('back', {
                nick: command.nick,
                message: '',
                time: time
            });
        } else {
            this.emit('away', {
                nick: command.nick,
                message: message,
                time: time
            });
        }
    },

    RPL_NOWAWAY: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();

        this.emit('away', {
            nick: command.nick,
            message: command.params[command.params.length - 1],
            time: time
        });
    },

    RPL_UNAWAY: function(command) {
        var time;

        // Check if we have a server-time
        time = command.getServerTime();

        this.emit('back', {
            nick: command.nick,
            message: command.params[command.params.length - 1] || '', // example: "<nick> is now back."
            time: time
        });
    },

    RPL_ISON: function(command) {
        this.emit('users online', {
            nicks: (command.params[command.params.length - 1] || '').split(' '),
        });
    },

    ERR_NICKNAMEINUSE: function(command) {
        this.emit('nick in use', {
            nick: command.params[1],
            reason: command.params[command.params.length - 1]
        });
    },

    ERR_ERRONEUSNICKNAME: function(command) {
        this.emit('nick invalid', {
            nick: command.params[1],
            reason: command.params[command.params.length - 1]
        });
    },

    RPL_ENDOFWHOIS: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);

        if (!cache.nick) {
            cache.nick = command.params[1];
            cache.error = 'not_found';
        }

        this.emit('whois', cache);
        cache.destroy();
    },

    RPL_AWAY: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.away = command.params[command.params.length - 1] || 'is away';
    },

    RPL_WHOISUSER: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.nick = command.params[1];
        cache.user = command.params[2];
        cache.host = command.params[3];
        cache.real_name = command.params[5];
    },

    RPL_WHOISHELPOP: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.helpop = command.params[command.params.length - 1];
    },

    RPL_WHOISBOT: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.bot = command.params[command.params.length - 1];
    },

    RPL_WHOISSERVER: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.server = command.params[2];
        cache.server_info = command.params[command.params.length - 1];
    },

    RPL_WHOISOPERATOR: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.operator = command.params[command.params.length - 1];
    },

    RPL_WHOISCHANNELS:       function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        if (cache.channels) {
            cache.channels += ' ' + command.params[command.params.length - 1];
        } else {
            cache.channels = command.params[command.params.length - 1];
        }
    },

    RPL_WHOISMODES: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.modes = command.params[command.params.length - 1];
    },

    RPL_WHOISIDLE: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.idle = command.params[2];
        if (command.params[3]) {
            cache.logon = command.params[3];
        }
    },

    RPL_WHOISREGNICK: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.registered_nick = command.params[command.params.length - 1];
    },

    RPL_WHOISHOST: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);

        var last_param = command.params[command.params.length - 1];
        // <source> 378 <target> <nick> :is connecting from <user>@<host> <ip>
        var match = last_param.match(/.*@([^ ]+) ([^ ]+).*$/);  // https://regex101.com/r/AQz7RE/2

        if (!match) {
            return;
        }

        cache.actualip = match[2];
        cache.actualhost = match[1];
    },

    RPL_WHOISSECURE: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.secure = true;
    },

    RPL_WHOISACCOUNT: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.account = command.params[2];
    },

    RPL_WHOISSPECIAL: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);
        cache.special = command.params[command.params.length - 1];
    },

    RPL_WHOISACTUALLY: function(command) {
        var cache_key = command.params[1].toLowerCase();
        var cache = this.cache('whois.' + cache_key);

        // <source> 338 <target> <nick> <user>@<host> <ip> :Actual user@host, Actual IP
        var user_host = command.params[command.params.length - 3] || '';
        var host = user_host.substring(user_host.indexOf("@") + 1);
        var ip = command.params[command.params.length - 2];

        // UnrealIRCd uses this numeric for something else resulting in ip+host
        // to be empty, so ignore this is that's the case
        if (ip && host) {
            cache.actualip = ip;
            cache.actualhost = host;
        }
    },

    RPL_WHOWASUSER: function(command) {
        this.emit('whowas', {
            nick: command.params[1],
            ident: command.params[2],
            host: command.params[3],
            real_name: command.params[command.params.length - 1]
        });
    },

    RPL_ENDOFWHOWAS: function() {
        // noop
    },

    ERR_WASNOSUCHNICK: function(command) {
        this.emit('whowas', {
            nick: command.params[1],
            error: 'no_such_nick'
        });
    },

    RPL_UMODEIS: function(command) {
        // this.connection.umodes = the modes
        // TODO: this
    },

    RPL_HOSTCLOACKING: function(command) {
        this.emit('displayed host', {
            nick: command.params[0],
            host: command.params[1]
        });
    }
};

module.exports = function AddCommandHandlers(command_controller) {
    _.each(handlers, function(handler, handler_command) {
        command_controller.addHandler(handler_command, handler);
    });
};
<|endoftext|>


<commit_before>/*jshint onevar:false*/

/*
 * grunt-reduce
 * https://github.com/munter/grunt-reduce
 *
 * Copyright (c) 2012 Peter Müller
 * Licensed under the MIT license.
 */

module.exports = function (grunt) {

    grunt.registerTask('reduce', 'Description', function () {
        var done = this.async();

        var AssetGraph = require('assetgraph-builder'),
            query = AssetGraph.query,
            urlTools = require('urltools'),
            chalk = require('chalk');

        var config = grunt.config(this.name) || {},
            rootUrl = urlTools.fsDirToFileUrl(config.root || 'app'),
            outRoot = urlTools.fsDirToFileUrl(config.outRoot || 'dist'),
            cdnRoot = config.cdnRoot && urlTools.ensureTrailingSlash(config.cdnRoot),
            cdnOutRoot = config.cdnOutRoot && urlTools.fsDirToFileUrl(config.cdnOutRoot),
            canonicalUrl = config.canonicalUrl && urlTools.ensureTrailingSlash(config.canonicalUrl),
            optimizeImages = config.optimizeImages === false ? false : true,
            sourceMaps = config.sourceMaps === false ? false : true,
            less = config.less === false ? false : true,
            scss = config.scss === false ? false : true,
            fileRev = config.fileRev === false ? false : true,
            asyncScripts = config.asyncScripts === false ? false : true,
            sharedBundles = config.sharedBundles === false ? false : true;

        // Support for locales
        var localeIds;
        if (Array.isArray(config.locales)) {
            localeIds = config.locales.map(function (localeId) {
                // Normalize localeId
                return localeId && localeId.replace(/-/g, '_').toLowerCase();
            });
        }

        var loadAssets = [
            '*.html',
            '.htaccess',
            '*.txt',
            '*.ico'
        ];

        if (config.include) {
            loadAssets = config.include;
        }

        var browsers = [
            '> 1%',
            'last 2 versions',
            'Firefox ESR',
            'Opera 12.1'
        ];

        if (config.autoprefix) {
            grunt.log.writeln(chalk.yellow(' ⚠ The "autoprefix" property has been deprecated and replaced with "browsers". Please update your grunt-reduce config.'));
            browsers = config.autoprefix;
        }

        if (config.browsers) {
            browsers = config.browsers;
        }

        new AssetGraph({ root: rootUrl })
            .logEvents()
            .registerRequireJsConfig()
            .loadAssets(loadAssets)
            .buildProduction({
                angular: config.angular,
                recursive: true,
                canonicalUrl: canonicalUrl,
                browsers: browsers,
                less: less,
                scss: scss,
                noFileRev: !fileRev,
                optimizeImages: optimizeImages,
                sourceMaps: sourceMaps,
                inlineSize: config.inlineSize === 0 ? 0 : (config.inlineSize || 4096),
                manifest: config.manifest || false,
                asyncScripts: asyncScripts,
                cdnRoot: cdnRoot,
                noCompress: config.pretty || false,
                sharedBundles: sharedBundles,
                stripDebug: !(config.pretty || false),
                localeIds: localeIds
            })
            .writeAssetsToDisc({url: /^file:/, isLoaded: true}, outRoot)
            .if(cdnRoot)
                .writeAssetsToDisc({url: query.createPrefixMatcher(cdnRoot), isLoaded: true}, cdnOutRoot || outRoot, cdnRoot)
            .endif()
            .writeStatsToStderr()
            .run(done);
    });
};
<commit_msg>Add support for the subResourceIntegrity option, defaults to true.<commit_after>/*jshint onevar:false*/

/*
 * grunt-reduce
 * https://github.com/munter/grunt-reduce
 *
 * Copyright (c) 2012 Peter Müller
 * Licensed under the MIT license.
 */

module.exports = function (grunt) {

    grunt.registerTask('reduce', 'Description', function () {
        var done = this.async();

        var AssetGraph = require('assetgraph-builder'),
            query = AssetGraph.query,
            urlTools = require('urltools'),
            chalk = require('chalk');

        var config = grunt.config(this.name) || {},
            rootUrl = urlTools.fsDirToFileUrl(config.root || 'app'),
            outRoot = urlTools.fsDirToFileUrl(config.outRoot || 'dist'),
            cdnRoot = config.cdnRoot && urlTools.ensureTrailingSlash(config.cdnRoot),
            cdnOutRoot = config.cdnOutRoot && urlTools.fsDirToFileUrl(config.cdnOutRoot),
            canonicalUrl = config.canonicalUrl && urlTools.ensureTrailingSlash(config.canonicalUrl),
            optimizeImages = config.optimizeImages === false ? false : true,
            sourceMaps = config.sourceMaps === false ? false : true,
            less = config.less === false ? false : true,
            scss = config.scss === false ? false : true,
            fileRev = config.fileRev === false ? false : true,
            asyncScripts = config.asyncScripts === false ? false : true,
            sharedBundles = config.sharedBundles === false ? false : true,
            subResourceIntegrity = config.subResourceIntegrity === false ? false : true;

        // Support for locales
        var localeIds;
        if (Array.isArray(config.locales)) {
            localeIds = config.locales.map(function (localeId) {
                // Normalize localeId
                return localeId && localeId.replace(/-/g, '_').toLowerCase();
            });
        }

        var loadAssets = [
            '*.html',
            '.htaccess',
            '*.txt',
            '*.ico'
        ];

        if (config.include) {
            loadAssets = config.include;
        }

        var browsers = [
            '> 1%',
            'last 2 versions',
            'Firefox ESR',
            'Opera 12.1'
        ];

        if (config.autoprefix) {
            grunt.log.writeln(chalk.yellow(' ⚠ The "autoprefix" property has been deprecated and replaced with "browsers". Please update your grunt-reduce config.'));
            browsers = config.autoprefix;
        }

        if (config.browsers) {
            browsers = config.browsers;
        }

        new AssetGraph({ root: rootUrl })
            .logEvents()
            .registerRequireJsConfig()
            .loadAssets(loadAssets)
            .buildProduction({
                angular: config.angular,
                recursive: true,
                canonicalUrl: canonicalUrl,
                browsers: browsers,
                less: less,
                scss: scss,
                noFileRev: !fileRev,
                optimizeImages: optimizeImages,
                sourceMaps: sourceMaps,
                inlineSize: config.inlineSize === 0 ? 0 : (config.inlineSize || 4096),
                manifest: config.manifest || false,
                asyncScripts: asyncScripts,
                cdnRoot: cdnRoot,
                noCompress: config.pretty || false,
                sharedBundles: sharedBundles,
                stripDebug: !(config.pretty || false),
                localeIds: localeIds,
                subResourceIntegrity: subResourceIntegrity
            })
            .writeAssetsToDisc({url: /^file:/, isLoaded: true}, outRoot)
            .if(cdnRoot)
                .writeAssetsToDisc({url: query.createPrefixMatcher(cdnRoot), isLoaded: true}, cdnOutRoot || outRoot, cdnRoot)
            .endif()
            .writeStatsToStderr()
            .run(done);
    });
};
<|endoftext|>


<commit_before>/*
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';

import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import { accessSecretStorage } from '../../../CrossSigningManager';
import Modal from '../../../Modal';

export default class CrossSigningPanel extends React.PureComponent {
    constructor(props) {
        super(props);

        this._unmounted = false;

        this.state = {
            error: null,
            crossSigningPublicKeysOnDevice: false,
            crossSigningPrivateKeysInStorage: false,
            secretStorageKeyInAccount: false,
        };
    }

    componentDidMount() {
        const cli = MatrixClientPeg.get();
        cli.on("accountData", this.onAccountData);
        cli.on("userTrustStatusChanged", this.onStatusChanged);
        cli.on("crossSigning.keysChanged", this.onStatusChanged);
        this._getUpdatedStatus();
    }

    componentWillUnmount() {
        this._unmounted = true;
        const cli = MatrixClientPeg.get();
        if (!cli) return;
        cli.removeListener("accountData", this.onAccountData);
        cli.removeListener("userTrustStatusChanged", this.onStatusChanged);
        cli.removeListener("crossSigning.keysChanged", this.onStatusChanged);
    }

    onAccountData = (event) => {
        const type = event.getType();
        if (type.startsWith("m.cross_signing") || type.startsWith("m.secret_storage")) {
            this._getUpdatedStatus();
        }
    };

    onStatusChanged = () => {
        this._getUpdatedStatus();
    };

    async _getUpdatedStatus() {
        // XXX: Add public accessors if we keep this around in production
        const cli = MatrixClientPeg.get();
        const crossSigning = cli._crypto._crossSigningInfo;
        const secretStorage = cli._crypto._secretStorage;
        const crossSigningPublicKeysOnDevice = crossSigning.getId();
        const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage);
        const secretStorageKeyInAccount = await secretStorage.hasKey();
        const homeserverSupportsCrossSigning =
            await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");

        this.setState({
            crossSigningPublicKeysOnDevice,
            crossSigningPrivateKeysInStorage,
            secretStorageKeyInAccount,
            homeserverSupportsCrossSigning,
        });
    }

    /**
     * Bootstrapping secret storage may take one of these paths:
     * 1. Create secret storage from a passphrase and store cross-signing keys
     *    in secret storage.
     * 2. Access existing secret storage by requesting passphrase and accessing
     *    cross-signing keys as needed.
     * 3. All keys are loaded and there's nothing to do.
     * @param {bool} [force] Bootstrap again even if keys already present
     */
    _bootstrapSecureSecretStorage = async (force=false) => {
        this.setState({ error: null });
        try {
            await accessSecretStorage(() => undefined, force);
        } catch (e) {
            this.setState({ error: e });
            console.error("Error bootstrapping secret storage", e);
        }
        if (this._unmounted) return;
        this._getUpdatedStatus();
    }

    onDestroyStorage = (act) => {
        if (!act) return;
        this._bootstrapSecureSecretStorage(true);
    }

    _destroySecureSecretStorage = () => {
        const ConfirmDestoryCrossSigningDialog = sdk.getComponent("dialogs.ConfirmDestroyCrossSigningDialog");
        Modal.createDialog(ConfirmDestoryCrossSigningDialog, {
            onFinished: this.onDestroyStorage,
        });
    }

    render() {
        const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
        const {
            error,
            crossSigningPublicKeysOnDevice,
            crossSigningPrivateKeysInStorage,
            secretStorageKeyInAccount,
            homeserverSupportsCrossSigning,
        } = this.state;

        let errorSection;
        if (error) {
            errorSection =
{error.toString()}
;
        }

        // Whether the various keys exist on your account (but not necessarily
        // on this device).
        const enabledForAccount = (
            crossSigningPrivateKeysInStorage &&
            secretStorageKeyInAccount
        );

        let summarisedStatus;
        if (!homeserverSupportsCrossSigning) {
            summarisedStatus =
{_t(
                "Your homeserver does not support cross-signing.",
            )}
;
        } else if (enabledForAccount && crossSigningPublicKeysOnDevice) {
            summarisedStatus =
✅ {_t(
                "Cross-signing and secret storage are enabled.",
            )}
;
        } else if (crossSigningPrivateKeysInStorage) {
            summarisedStatus =
{_t(
                "Your account has a cross-signing identity in secret storage, but it " +
                "is not yet trusted by this session.",
            )}
;
        } else {
            summarisedStatus =
{_t(
                "Cross-signing and secret storage are not yet set up.",
            )}
;
        }

        let resetButton;
        if (enabledForAccount) {
            resetButton = (

                    <AccessibleButton kind="danger" onClick={this._destroySecureSecretStorage}>
                        {_t("Reset cross-signing and secret storage")}
                    </AccessibleButton>

            );
        }
        let bootstrapButton;
        if (
            (!enabledForAccount || !crossSigningPublicKeysOnDevice) &&
            homeserverSupportsCrossSigning
        ) {
            bootstrapButton = (

                    <AccessibleButton kind="primary" onClick={this._bootstrapSecureSecretStorage}>
                        {_t("Bootstrap cross-signing and secret storage")}
                    </AccessibleButton>

            );
        }

        return (

                {summarisedStatus}

                    {_t("Advanced")}

                            {_t("Cross-signing public keys:")}
                            {crossSigningPublicKeysOnDevice ? _t("in memory") : _t("not found")}

                            {_t("Cross-signing private keys:")}
                            {crossSigningPrivateKeysInStorage ? _t("in secret storage") : _t("not found")}

                            {_t("Secret storage public key:")}
                            {secretStorageKeyInAccount ? _t("in account data") : _t("not found")}

                            {_t("Homeserver feature support:")}
                            {homeserverSupportsCrossSigning ? _t("exists") : _t("not found")}

                {errorSection}
                {bootstrapButton}
                {resetButton}

        );
    }
}
<commit_msg>Fix bug where cross-signing keys would be unintentionally reset<commit_after>/*
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';

import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import { accessSecretStorage } from '../../../CrossSigningManager';
import Modal from '../../../Modal';

export default class CrossSigningPanel extends React.PureComponent {
    constructor(props) {
        super(props);

        this._unmounted = false;

        this.state = {
            error: null,
            crossSigningPublicKeysOnDevice: false,
            crossSigningPrivateKeysInStorage: false,
            secretStorageKeyInAccount: false,
        };
    }

    componentDidMount() {
        const cli = MatrixClientPeg.get();
        cli.on("accountData", this.onAccountData);
        cli.on("userTrustStatusChanged", this.onStatusChanged);
        cli.on("crossSigning.keysChanged", this.onStatusChanged);
        this._getUpdatedStatus();
    }

    componentWillUnmount() {
        this._unmounted = true;
        const cli = MatrixClientPeg.get();
        if (!cli) return;
        cli.removeListener("accountData", this.onAccountData);
        cli.removeListener("userTrustStatusChanged", this.onStatusChanged);
        cli.removeListener("crossSigning.keysChanged", this.onStatusChanged);
    }

    onAccountData = (event) => {
        const type = event.getType();
        if (type.startsWith("m.cross_signing") || type.startsWith("m.secret_storage")) {
            this._getUpdatedStatus();
        }
    };

    _onBootstrapClick = () => {
        this._bootstrapSecureSecretStorage(false);
    };

    onStatusChanged = () => {
        this._getUpdatedStatus();
    };

    async _getUpdatedStatus() {
        // XXX: Add public accessors if we keep this around in production
        const cli = MatrixClientPeg.get();
        const crossSigning = cli._crypto._crossSigningInfo;
        const secretStorage = cli._crypto._secretStorage;
        const crossSigningPublicKeysOnDevice = crossSigning.getId();
        const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage);
        const secretStorageKeyInAccount = await secretStorage.hasKey();
        const homeserverSupportsCrossSigning =
            await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");

        this.setState({
            crossSigningPublicKeysOnDevice,
            crossSigningPrivateKeysInStorage,
            secretStorageKeyInAccount,
            homeserverSupportsCrossSigning,
        });
    }

    /**
     * Bootstrapping secret storage may take one of these paths:
     * 1. Create secret storage from a passphrase and store cross-signing keys
     *    in secret storage.
     * 2. Access existing secret storage by requesting passphrase and accessing
     *    cross-signing keys as needed.
     * 3. All keys are loaded and there's nothing to do.
     * @param {bool} [force] Bootstrap again even if keys already present
     */
    _bootstrapSecureSecretStorage = async (force=false) => {
        this.setState({ error: null });
        try {
            await accessSecretStorage(() => undefined, force);
        } catch (e) {
            this.setState({ error: e });
            console.error("Error bootstrapping secret storage", e);
        }
        if (this._unmounted) return;
        this._getUpdatedStatus();
    }

    onDestroyStorage = (act) => {
        if (!act) return;
        this._bootstrapSecureSecretStorage(true);
    }

    _destroySecureSecretStorage = () => {
        const ConfirmDestoryCrossSigningDialog = sdk.getComponent("dialogs.ConfirmDestroyCrossSigningDialog");
        Modal.createDialog(ConfirmDestoryCrossSigningDialog, {
            onFinished: this.onDestroyStorage,
        });
    }

    render() {
        const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
        const {
            error,
            crossSigningPublicKeysOnDevice,
            crossSigningPrivateKeysInStorage,
            secretStorageKeyInAccount,
            homeserverSupportsCrossSigning,
        } = this.state;

        let errorSection;
        if (error) {
            errorSection =
{error.toString()}
;
        }

        // Whether the various keys exist on your account (but not necessarily
        // on this device).
        const enabledForAccount = (
            crossSigningPrivateKeysInStorage &&
            secretStorageKeyInAccount
        );

        let summarisedStatus;
        if (!homeserverSupportsCrossSigning) {
            summarisedStatus =
{_t(
                "Your homeserver does not support cross-signing.",
            )}
;
        } else if (enabledForAccount && crossSigningPublicKeysOnDevice) {
            summarisedStatus =
✅ {_t(
                "Cross-signing and secret storage are enabled.",
            )}
;
        } else if (crossSigningPrivateKeysInStorage) {
            summarisedStatus =
{_t(
                "Your account has a cross-signing identity in secret storage, but it " +
                "is not yet trusted by this session.",
            )}
;
        } else {
            summarisedStatus =
{_t(
                "Cross-signing and secret storage are not yet set up.",
            )}
;
        }

        let resetButton;
        if (enabledForAccount) {
            resetButton = (

                    <AccessibleButton kind="danger" onClick={this._destroySecureSecretStorage}>
                        {_t("Reset cross-signing and secret storage")}
                    </AccessibleButton>

            );
        }
        let bootstrapButton;
        if (
            (!enabledForAccount || !crossSigningPublicKeysOnDevice) &&
            homeserverSupportsCrossSigning
        ) {
            bootstrapButton = (

                    <AccessibleButton kind="primary" onClick={this._onBootstrapClick}>
                        {_t("Bootstrap cross-signing and secret storage")}
                    </AccessibleButton>

            );
        }

        return (

                {summarisedStatus}

                    {_t("Advanced")}

                            {_t("Cross-signing public keys:")}
                            {crossSigningPublicKeysOnDevice ? _t("in memory") : _t("not found")}

                            {_t("Cross-signing private keys:")}
                            {crossSigningPrivateKeysInStorage ? _t("in secret storage") : _t("not found")}

                            {_t("Secret storage public key:")}
                            {secretStorageKeyInAccount ? _t("in account data") : _t("not found")}

                            {_t("Homeserver feature support:")}
                            {homeserverSupportsCrossSigning ? _t("exists") : _t("not found")}

                {errorSection}
                {bootstrapButton}
                {resetButton}

        );
    }
}
<|endoftext|>


<commit_before>var express = require('express'),
	bodyParser = require('body-parser'),
    authenticate = require('azure-mobile-apps/src/express/middleware/authenticate'),
    authorize = require('azure-mobile-apps/src/express/middleware/authorize');

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

module.exports = function (configuration) {
    var router = express.Router();

	var completeError = function(err, res) {
	    if (err) {
	        console.error(err);
	        if (res) res.sendStatus(500);
	    }
	};

    router.get('/', (req, res, next) => {
		console.log('in GET /tertulias');
		res.send( '' );
		res.send( '' );
		res.send( '' );
		res.send( '' );
		res.send( '
Tertulias
' );
		res.send( '' );
		res.send( '' );
    	next();
    });

    return router;
};
<commit_msg>wip save<commit_after>var express = require('express'),
	bodyParser = require('body-parser'),
    authenticate = require('azure-mobile-apps/src/express/middleware/authenticate'),
    authorize = require('azure-mobile-apps/src/express/middleware/authorize');

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

module.exports = function (configuration) {
    var router = express.Router();

	var completeError = function(err, res) {
	    if (err) {
	        console.error(err);
	        if (res) res.sendStatus(500);
	    }
	};

    router.get('/', (req, res, next) => {
		console.log('in GET /tertulias');
		res.send( '' );
		// res.send( '' );
		// res.send( '' );
		res.send( '' );
		res.send( '
Tertulias
' );
		res.send( '
Tertulias
' );
		res.send( '' );
		res.send( '' );
    	next();
    });

    return router;
};
<|endoftext|>


<commit_before>//* @protected
enyo.$ = {};

enyo.dispatcher = {
	// these events come from document
	events: ["mousedown", "mouseup", "mouseover", "mouseout", "mousemove", "mousewheel", "click", "dblclick", "change", "keydown", "keyup", "keypress", "input"],
	// these events come from window
	windowEvents: ["resize", "load", "unload", "message"],
	// feature plugins (aka filters)
	features: [],
	connect: function() {
		var d = enyo.dispatcher;
		for (var i=0, n; n=d.events[i]; i++) {
			d.listen(document, n);
		}
		for (i=0, n; n=d.windowEvents[i]; i++) {
			d.listen(window, n);
		}
	},
	listen: function(inListener, inEventName) {
		var d = enyo.dispatch;
		if (inListener.addEventListener) {
			this.listen = function(inListener, inEventName) {
				inListener.addEventListener(inEventName, d, false);
			};
		} else {
			//console.log("IE8 COMPAT: using 'attachEvent'");
			this.listen = function(inListener, inEvent, inCb) {
				inListener.attachEvent("on" + inEvent, function(e) {
					e.target = e.srcElement;
					if (!e.preventDefault) {
						e.preventDefault = enyo.iePreventDefault;
					}
					return d(e);
				});
			};
		}
		this.listen(inListener, inEventName);
	},
	//* Fires an event for Enyo to listen for.
	dispatch: function(e) {
		// Find the control who maps to e.target, or the first control that maps to an ancestor of e.target.
		var c = this.findDispatchTarget(e.target) || this.findDefaultTarget(e);
		// Cache the original target
		e.dispatchTarget = c;
		// support pluggable features return true to abort immediately or set e.preventDispatch to avoid processing.
		for (var i=0, fn; fn=this.features[i]; i++) {
			if (fn.call(this, e) === true) {
				return;
			}
		}
		if (c && !e.preventDispatch) {
			this.dispatchBubble(e, c);
		}
	},
	//* Takes an Event.target and finds the corresponding Enyo control.
	findDispatchTarget: function(inNode) {
		var t, n = inNode;
		// FIXME: Mozilla: try/catch is here to squelch "Permission denied to access property xxx from a non-chrome context"
		// which appears to happen for scrollbar nodes in particular. It's unclear why those nodes are valid targets if
		// it is illegal to interrogate them. Would like to trap the bad nodes explicitly rather than using an exception block.
		try {
			while (n) {
				if (t = enyo.$[n.id]) {
					// there could be multiple nodes with this id, the relevant node for this event is n
					// we don't push this directly to t.node because sometimes we are just asking what
					// the target 'would be' (aka, calling findDispatchTarget from handleMouseOverOut)
					t.eventNode = n;
					break;
				}
				n = n.parentNode;
			}
		} catch(x) {
			console.log(x, n);
		}
		return t;
	},
	//* Returns the default Enyo control for events.
	findDefaultTarget: function(e) {
		return enyo.master;
	},
	dispatchBubble: function(e, c) {
		return c.bubble("on" + e.type, e, c);
	}
};

// called in the context of an event
enyo.iePreventDefault = function() {
	this.returnValue = false;
};

enyo.dispatch = function(inEvent) {
	return enyo.dispatcher.dispatch(inEvent);
};

enyo.bubble = function(inEvent) {
	// '|| window.event' clause needed for IE8
	var e = inEvent || window.event;
	if (e) {
		// We depend on e.target existing for event tracking and dispatching.
		if (!e.target) {
			e.target = e.srcElement;
		}
		enyo.dispatch(e);
	}
};

// This string is set on event handlers attributes for DOM elements that
// don't normally bubble (like onscroll) so that they can participate in the
// Enyo event system.
enyo.bubbler = function() {
	enyo.bubble(arguments[0]);
};

/**
 * Makes given events bubble on specified enyo contol
 */
enyo.makeBubble = function() {
	var args = arguments.slice(0),
		control = args.shift();

	if(typeof(control) === "object" && typeof(control.hasNode) === "function") {
		enyo.forEach(args, function(event) {
			if(this.hasNode()) {
				this.node.addEventListener(event, enyo.bubbler);
			}
		}, control);
	}
};

// FIXME: we need to create and initialize dispatcher someplace else to allow overrides
enyo.requiresWindow(enyo.dispatcher.connect);
<commit_msg>Fixed a dumb mistake assuming arguments was an Array<commit_after>//* @protected
enyo.$ = {};

enyo.dispatcher = {
	// these events come from document
	events: ["mousedown", "mouseup", "mouseover", "mouseout", "mousemove", "mousewheel", "click", "dblclick", "change", "keydown", "keyup", "keypress", "input"],
	// these events come from window
	windowEvents: ["resize", "load", "unload", "message"],
	// feature plugins (aka filters)
	features: [],
	connect: function() {
		var d = enyo.dispatcher;
		for (var i=0, n; n=d.events[i]; i++) {
			d.listen(document, n);
		}
		for (i=0, n; n=d.windowEvents[i]; i++) {
			d.listen(window, n);
		}
	},
	listen: function(inListener, inEventName) {
		var d = enyo.dispatch;
		if (inListener.addEventListener) {
			this.listen = function(inListener, inEventName) {
				inListener.addEventListener(inEventName, d, false);
			};
		} else {
			//console.log("IE8 COMPAT: using 'attachEvent'");
			this.listen = function(inListener, inEvent, inCb) {
				inListener.attachEvent("on" + inEvent, function(e) {
					e.target = e.srcElement;
					if (!e.preventDefault) {
						e.preventDefault = enyo.iePreventDefault;
					}
					return d(e);
				});
			};
		}
		this.listen(inListener, inEventName);
	},
	//* Fires an event for Enyo to listen for.
	dispatch: function(e) {
		// Find the control who maps to e.target, or the first control that maps to an ancestor of e.target.
		var c = this.findDispatchTarget(e.target) || this.findDefaultTarget(e);
		// Cache the original target
		e.dispatchTarget = c;
		// support pluggable features return true to abort immediately or set e.preventDispatch to avoid processing.
		for (var i=0, fn; fn=this.features[i]; i++) {
			if (fn.call(this, e) === true) {
				return;
			}
		}
		if (c && !e.preventDispatch) {
			this.dispatchBubble(e, c);
		}
	},
	//* Takes an Event.target and finds the corresponding Enyo control.
	findDispatchTarget: function(inNode) {
		var t, n = inNode;
		// FIXME: Mozilla: try/catch is here to squelch "Permission denied to access property xxx from a non-chrome context"
		// which appears to happen for scrollbar nodes in particular. It's unclear why those nodes are valid targets if
		// it is illegal to interrogate them. Would like to trap the bad nodes explicitly rather than using an exception block.
		try {
			while (n) {
				if (t = enyo.$[n.id]) {
					// there could be multiple nodes with this id, the relevant node for this event is n
					// we don't push this directly to t.node because sometimes we are just asking what
					// the target 'would be' (aka, calling findDispatchTarget from handleMouseOverOut)
					t.eventNode = n;
					break;
				}
				n = n.parentNode;
			}
		} catch(x) {
			console.log(x, n);
		}
		return t;
	},
	//* Returns the default Enyo control for events.
	findDefaultTarget: function(e) {
		return enyo.master;
	},
	dispatchBubble: function(e, c) {
		return c.bubble("on" + e.type, e, c);
	}
};

// called in the context of an event
enyo.iePreventDefault = function() {
	this.returnValue = false;
};

enyo.dispatch = function(inEvent) {
	return enyo.dispatcher.dispatch(inEvent);
};

enyo.bubble = function(inEvent) {
	// '|| window.event' clause needed for IE8
	var e = inEvent || window.event;
	if (e) {
		// We depend on e.target existing for event tracking and dispatching.
		if (!e.target) {
			e.target = e.srcElement;
		}
		enyo.dispatch(e);
	}
};

// This string is set on event handlers attributes for DOM elements that
// don't normally bubble (like onscroll) so that they can participate in the
// Enyo event system.
enyo.bubbler = function() {
	enyo.bubble(arguments[0]);
};

/**
 * Makes given events bubble on specified enyo contol
 */
enyo.makeBubble = function() {
	var args = Array.prototype.slice.call(arguments, 0),
		control = args.shift();

	if(typeof(control) === "object" && typeof(control.hasNode) === "function") {
		enyo.forEach(args, function(event) {
			if(this.hasNode()) {
				this.node.addEventListener(event, enyo.bubbler);
			}
		}, control);
	}
};

// FIXME: we need to create and initialize dispatcher someplace else to allow overrides
enyo.requiresWindow(enyo.dispatcher.connect);
<|endoftext|>


<commit_before>import Ember from 'ember';

const {
  Component,
  run,
  observer,
  $
} = Ember;

export default Component.extend({
  headerTemplate: null,
  footerTemplate: null,
  activeViewport: true,
  scale: undefined,
  minScale: 0.1,
  maxScale: 5.0,
  increment: 0.25,
  originalWidth: undefined,
  originalHeight: undefined,
  width: undefined,
  height: undefined,
  panX: 0,
  panY: 0,
  centerOnFit: true,
  delay: 250,

  didInsertElement() {
    const viewport = this.$('.zoom-viewport');
    const content = viewport.children('.zoom-content');
    const scale = this.get('scale');
    const start = (e) => {
      if(Ember.isPresent(normalizeEvent(e).touches) || e.which === 1) { startPinch(e); }
      if(e.type === 'mousedown') { e.preventDefault(); }
    };

    this._super(...arguments);

    run.scheduleOnce('afterRender', () => {
      this.set('$viewport', content.parent());
      this.set('$content', content);
      this.set('originalWidth', content.width());
      this.set('originalHeight', content.height());

      viewport.on('touchstart mousedown', {zone: this}, (e) => {
        if(this.get('activeViewport')) { start(e); }
      });

      content.on('touchstart mousedown', {zone: this}, start);

      if(scale) { this.zoomTo(scale); }
      else { this.zoomFit(); }
    });
  },

  willDestroyElement() {
    const viewport = this.get('$viewport');
    const content = this.get('$content');

    viewport.off();
    content.off();

    this._super(...arguments);
  },

  matrix: observer('panX', 'panY', 'scale', function () {
    run.throttle(this, applyMatrix, 16, false);
  }),

  zoomFit() {
    const viewport = this.get('$viewport');
    const content = this.get('$content');
    const [ratioX, ratioY] = [
      viewport.width() / content.width(),
      viewport.height() / content.height()
    ];
    const ratio = Math.min(ratioX, ratioY);
    let [dx, dy] = [
      (content.width() / 2) * (ratio - 1),
      (content.height() / 2) * (ratio - 1)
    ];

    if(this.get('centerOnFit')) {
      if(ratioX > ratioY) { dx = (viewport.width() - content.width()) / 2; }
      if(ratioX < ratioY) { dy = (viewport.height() - content.height()) / 2; }
    }

    this.set('panX', dx);
    this.set('panY', dy);
    this.zoomTo(ratio);
  },

  zoomTo(ratio) {
    const [min, max] = [this.get('minScale'), this.get('maxScale')];

    if(ratio > max) { ratio = max; }
    else if(ratio < min) { ratio = min; }

    this.setProperties({
      scale: ratio,
      width: this.get('originalWidth') * ratio,
      height: this.get('originalHeight') * ratio,
    });
  },

  zoomBy(delta) {
    const content = this.get('$content');
    const delay = +this.get('delay');

    if(delay && delta) {
      content.css({transition: `${delay}ms ease`});
      setTimeout(() => content.css({transition: ''}), delay);
    }
    this.zoomTo(this.get('scale') + delta);
  },

  actions: {
    zoomTo(scale=1) { this.zoomTo(scale); },
    zoomIn() { this.zoomBy(this.get('increment')); },
    zoomOut() { this.zoomBy(-this.get('increment')); },
    zoomFit() { this.zoomFit(); }
  }
});

function startPinch(event) {
  const {zone} = event.data;
  const [min, max] = [zone.get('minScale'), zone.get('maxScale')];
  const touch0 = normalizeTouches(event);
  const [scale0, x0, y0] = [
    zone.get('scale'),
    zone.get('panX') - touch0.x,
    zone.get('panY') - touch0.y
  ];

  function move(e) {
    e.preventDefault();
    const {x, y, distance} = normalizeTouches(normalizeEvent(e));
    let ratio = scale0 * distance / touch0.distance;

    if(ratio > max) { ratio = max; }
    else if(ratio < min) { ratio = min; }

    zone.setProperties({
      scale: ratio,
      panX: x0 + x,
      panY: y0 + y,
    });
  }

  $(document).on('mousemove touchmove', move);

  $(document).one('mouseup touchend', () =>
    $(document).off('mousemove touchmove', move)
  );
}

function normalizeEvent(event) {
  if(event.originalEvent) {
    const touches = Array.prototype.slice.call(event.originalEvent.touches || []);
    event.touches = touches;
    if(touches[0]) {
      event.pageX = touches[0].pageX;
      event.pageY = touches[0].pageY;
    } else {
      event.pageX = event.originalEvent.pageX;
      event.pageY = event.originalEvent.pageY;
    }
  }
  return event;
}

function normalizeTouches(event) {
  const {touches} = event;
  if((touches || []).length < 2) {
    return {
      x: event.pageX,
      y: event.pageY,
      distance: 1
    };
  }
  const [first, second] = touches.slice(0, 2);

  return {
    x: (first.pageX + second.pageX) / 2,
    y: (first.pageY + second.pageY) / 2,
    distance: distance(
      [first.clientX, first.clientY],
      [second.clientX, second.clientY]
    ),
  };
}

function distance(p0, p1) {
  return Math.sqrt(
     Math.pow(Math.abs(p0[0] - p1[0]), 2) +
     Math.pow(Math.abs(p0[1] - p1[1]), 2)
  );
}

function applyMatrix() {
  const {scale, panX, panY, $content} = this.getProperties('scale', 'panX', 'panY', '$content');
  $content.css({transform: `matrix(${scale}, 0, 0, ${scale}, ${panX}, ${panY})`});
}
<commit_msg>Move css updating to didRender<commit_after>import Ember from 'ember';

const {
  Component,
  run,
  computed,
  $
} = Ember;

export default Component.extend({
  headerTemplate: null,
  footerTemplate: null,
  activeViewport: true,
  scale: undefined,
  minScale: 0.1,
  maxScale: 5.0,
  increment: 0.25,
  originalWidth: undefined,
  originalHeight: undefined,
  width: undefined,
  height: undefined,
  panX: 0,
  panY: 0,
  centerOnFit: true,
  delay: 250,

  didInsertElement() {
    const viewport = this.$('.zoom-viewport');
    const content = viewport.children('.zoom-content');
    const scale = this.get('scale');
    const start = (e) => {
      if(Ember.isPresent(normalizeEvent(e).touches) || e.which === 1) { startPinch(e); }
      if(e.type === 'mousedown') { e.preventDefault(); }
    };

    this._super(...arguments);

    run.scheduleOnce('afterRender', () => {
      this.set('$viewport', content.parent());
      this.set('$content', content);
      this.set('originalWidth', content.width());
      this.set('originalHeight', content.height());

      viewport.on('touchstart mousedown', {zone: this}, (e) => {
        if(this.get('activeViewport')) { start(e); }
      });

      content.on('touchstart mousedown', {zone: this}, start);

      if(scale) { this.zoomTo(scale); }
      else { this.zoomFit(); }
    });
  },

  willDestroyElement() {
    const viewport = this.get('$viewport');
    const content = this.get('$content');

    viewport.off();
    content.off();

    this._super(...arguments);
  },

  matrix: computed('panX', 'panY', 'scale', function () {
    const {scale, panX, panY} = this.getProperties('scale', 'panX', 'panY');
    return `matrix(${scale}, 0, 0, ${scale}, ${panX}, ${panY})`;
  }),

  didRender() {
    const {$content, matrix} = this.getProperties('$content', 'matrix');
    if($content) { $content.css({transform: matrix}); }
  },

  zoomFit() {
    const viewport = this.get('$viewport');
    const content = this.get('$content');
    const [ratioX, ratioY] = [
      viewport.width() / content.width(),
      viewport.height() / content.height()
    ];
    const ratio = Math.min(ratioX, ratioY);
    let [dx, dy] = [
      (content.width() / 2) * (ratio - 1),
      (content.height() / 2) * (ratio - 1)
    ];

    if(this.get('centerOnFit')) {
      if(ratioX > ratioY) { dx = (viewport.width() - content.width()) / 2; }
      if(ratioX < ratioY) { dy = (viewport.height() - content.height()) / 2; }
    }

    this.set('panX', dx);
    this.set('panY', dy);
    this.zoomTo(ratio);
  },

  zoomTo(ratio) {
    const [min, max] = [this.get('minScale'), this.get('maxScale')];

    if(ratio > max) { ratio = max; }
    else if(ratio < min) { ratio = min; }

    this.setProperties({
      scale: ratio,
      width: this.get('originalWidth') * ratio,
      height: this.get('originalHeight') * ratio,
    });
  },

  zoomBy(delta) {
    const content = this.get('$content');
    const delay = +this.get('delay');

    if(delay && delta) {
      content.css({transition: `${delay}ms ease`});
      setTimeout(() => content.css({transition: ''}), delay);
    }
    this.zoomTo(this.get('scale') + delta);
  },

  actions: {
    zoomTo(scale=1) { this.zoomTo(scale); },
    zoomIn() { this.zoomBy(this.get('increment')); },
    zoomOut() { this.zoomBy(-this.get('increment')); },
    zoomFit() { this.zoomFit(); }
  }
});

function startPinch(event) {
  const {zone} = event.data;
  const [min, max] = [zone.get('minScale'), zone.get('maxScale')];
  const touch0 = normalizeTouches(event);
  const [scale0, x0, y0] = [
    zone.get('scale'),
    zone.get('panX') - touch0.x,
    zone.get('panY') - touch0.y
  ];

  function move(e) {
    e.preventDefault();
    const {x, y, distance} = normalizeTouches(normalizeEvent(e));
    let ratio = scale0 * distance / touch0.distance;

    if(ratio > max) { ratio = max; }
    else if(ratio < min) { ratio = min; }

    zone.setProperties({
      scale: ratio,
      panX: x0 + x,
      panY: y0 + y,
    });
  }

  $(document).on('mousemove touchmove', move);

  $(document).one('mouseup touchend', () =>
    $(document).off('mousemove touchmove', move)
  );
}

function normalizeEvent(event) {
  if(event.originalEvent) {
    const touches = Array.prototype.slice.call(event.originalEvent.touches || []);
    event.touches = touches;
    if(touches[0]) {
      event.pageX = touches[0].pageX;
      event.pageY = touches[0].pageY;
    } else {
      event.pageX = event.originalEvent.pageX;
      event.pageY = event.originalEvent.pageY;
    }
  }
  return event;
}

function normalizeTouches(event) {
  const {touches} = event;
  if((touches || []).length < 2) {
    return {
      x: event.pageX,
      y: event.pageY,
      distance: 1
    };
  }
  const [first, second] = touches.slice(0, 2);

  return {
    x: (first.pageX + second.pageX) / 2,
    y: (first.pageY + second.pageY) / 2,
    distance: distance(
      [first.clientX, first.clientY],
      [second.clientX, second.clientY]
    ),
  };
}

function distance(p0, p1) {
  return Math.sqrt(
     Math.pow(Math.abs(p0[0] - p1[0]), 2) +
     Math.pow(Math.abs(p0[1] - p1[1]), 2)
  );
}
<|endoftext|>