<commit_before>import Path from './path.js';
import Loader from './loader.js';
import Events from './events.js';
import Component from './component.js';
import Extend from './utility/extend.js';
import Ensure from './utility/ensure.js';

export default Object.freeze({

    data: [],
    location: {},

    option: {
        mode: 'push',
        target: null,
        contain: false,
        folder: './routes',
        external: null, before: null, after: null
    },

    async setup (option) {
        option = option || {};

        this.option.after = option.after === undefined ? this.option.after : option.after;
        this.option.before = option.before === undefined ? this.option.before : option.before;
        this.option.external = option.external === undefined ? this.option.external : option.external;

        this.option.mode = option.mode === undefined ? this.option.mode : option.mode;
        this.option.folder = option.folder === undefined ? this.option.folder : option.folder;
        this.option.target = option.target === undefined ? this.option.target : option.target;
        this.option.contain = option.contain === undefined ? this.option.contain : option.contain;

        if (!this.option.target || typeof this.option.target === 'string') {
            this.option.target = document.body.querySelector(this.option.target || 'o-router');
        }

        if (this.option.mode !== 'href') {
            window.addEventListener('popstate', this.state.bind(this), true);
            window.document.addEventListener('click', this.click.bind(this), true);
        }

        window.customElements.define('o-router', Extend(function () {}, HTMLElement));

        await this.add(option.routes);
        await this.route(window.location.href, { mode: 'replace' });
    },

    compareParts (routePath, userPath, split) {
        const compareParts = [];

        const routeParts = routePath.split(split);
        const userParts = userPath.split(split);

        if (userParts.length > 1 && userParts[userParts.length - 1] === '') {
            userParts.pop();
        }

        if (routeParts.length > 1 && routeParts[routeParts.length - 1] === '') {
            routeParts.pop();
        }

        for (let i = 0, l = routeParts.length; i < l; i++) {

            if (routeParts[i].slice(0, 1) === '(' && routeParts[i].slice(-1) === ')') {

                if (routeParts[i] === '(~)') {
                    return true;
                } else if (routeParts[i].indexOf('~') !== -1) {
                    if (userParts[i]) {
                        compareParts.push(userParts[i]);
                    }
                } else {
                    compareParts.push(userParts[i]);
                }

            } else if (routeParts[i] !== userParts[i]) {
                return false;
            } else {
                compareParts.push(routeParts[i]);
            }

        }

        if (compareParts.join(split) === userParts.join(split)) {
            return true;
        } else {
            return false;
        }
    },

    compare (routePath, userPath) {
        const base = Path.normalize(Path.base);

        userPath = Path.normalize(userPath);
        routePath = Path.normalize(routePath);

        if (userPath.slice(0, base.length) !== base) {
            userPath = Path.join(base, userPath);
        }

        if (routePath.slice(0, base.length) !== base) {
            routePath = Path.join(base, routePath);
        }

        if (this.compareParts(routePath, userPath, '/')) {
            return true;
        }

        if (this.compareParts(routePath, userPath, '-')) {
            return true;
        }

        return false;
    },

    toParameterObject (routePath, userPath) {
        let result = {};

        if (
            !routePath
            || !userPath
            || routePath === '/'
            || userPath === '/'
        ) return result;

        const userParts = userPath.split(/\/|-/);
        const routeParts = routePath.split(/\/|-/);

        for (let i = 0, l = routeParts.length; i < l; i++) {
            let part = routeParts[i];

            if (part.slice(0, 1) === '(' && part.slice(-1) === ')') {
                const name = part.slice(1, part.length - 1).replace('~', '');
                result[name] = userParts[i];
            }

        }

        return result;
    },

    toQueryString (data) {
        let result = '?';

        for (let key in data) {
            let value = data[key];
            result += key + '=' + value + '&';
        }

        if (result.slice(-1) === '&') {
            result = result.slice(0, -1);
        }

        return result;
    },

    toQueryObject (path) {
        let result = {};

        if (path.indexOf('?') === 0) path = path.slice(1);
        let queries = path.split('&');

        for (let i = 0, l = queries.length; i < l; i++) {
            let query = queries[i].split('=');

            if (query[0] && query[1]) {
                result[query[0]] = query[1];
            }

        }

        return result;
    },

    toLocationObject (href) {
        const location = {};
        const parser = document.createElement('a');

        parser.href = href;

        location.href = parser.href;
        location.host = parser.host;
        location.port = parser.port;
        location.hash = parser.hash;
        location.search = parser.search;
        location.protocol = parser.protocol;
        location.hostname = parser.hostname;
        location.pathname = parser.pathname[0] === '/' ? parser.pathname : '/' + parser.pathname;

        location.path = location.pathname + location.search + location.hash;

        return location;
    },

    scroll (x, y) {
        window.scroll(x, y);
    },

    back () {
        window.history.back();
    },

    forward () {
        window.history.forward();
    },

    redirect (path) {
        window.location.href = path;
    },

    async add (data) {
        if (!data) {
            return;
        } else if (data.constructor === String) {
            let path = data;

            if (path.slice(-3) === '.js') {
                path = path.slice(0, -3);
            }

            let load = path;

            if (path.slice(-5) === 'index') {
                path = path.slice(0, -5);
            }

            if (path.slice(-6) === 'index/') {
                path = path.slice(0, -6);
            }

            if (path.slice(0, 2) === './') {
                path = path.slice(2);
            }

            if (path.slice(0, 1) !== '/') {
                path = '/' + path;
            }

            load = load.replace(/\/?\((\w+)?~\)\/?/ig, '') + '.js';
            load = Path.join(this.option.folder, load);

            this.data.push({ path, load });
        } else if (data.constructor === Object) {

            if (!data.path) {
                throw new Error('Oxe.router.add - route path required');
            }

            if (!data.name && !data.load && !data.component) {
                throw new Error('Oxe.router.add -  route requires name, load, or component property');
            }

            this.data.push(data);
        } else if (data.constructor === Array) {

            for (let i = 0, l = data.length; i < l; i++) {
                await this.add(data[i]);
            }

        }
    },

    async load (route) {

        if (route.load) {
            const load = await Loader.load(route.load);
            route = Object.assign({}, load.default, route);
        }

        if (typeof route.component === 'string') {
            route.load = route.component;
            const load = await Loader.load(route.load);
            route.component = load.default;
        }

        return route;
    },

    async remove (path) {
        for (let i = 0, l = this.data.length; i < l; i++) {
            if (this.data[i].path === path) {
                this.data.splice(i, 1);
            }
        }
    },

    async get (path) {
        for (let i = 0, l = this.data.length; i < l; i++) {
            if (this.data[i].path === path) {
                this.data[i] = await this.load(this.data[i]);
                return this.data[i];
            }
        }
    },

    async filter (path) {
        const result = [];

        for (let i = 0, l = this.data.length; i < l; i++) {
            if (this.compare(this.data[i].path, path)) {
                this.data[i] = await this.load(this.data[i]);
                result.push(this.data[i]);
            }
        }

        return result;
    },

    async find (path) {
        for (let i = 0, l = this.data.length; i < l; i++) {
            if (this.compare(this.data[i].path, path)) {
                this.data[i] = await this.load(this.data[i]);
                return this.data[i];
            }
        }
    },

    async render (route) {

        if (!route) {
            throw new Error('Oxe.render - route argument required. Missing object option.');
        }

        if (route.title) {
            document.title = route.title;
        }

        const ensures = [];

        if (route.keywords) {
            ensures.push({
                name: 'meta',
                query: '[name="keywords"]',
                attributes: [
                    { name: 'name', value: 'keywords' },
                    { name: 'content', value: route.keywords }
                ]
            });
        }

        if (route.description) {
            ensures.push({
                name: 'meta',
                query: '[name="description"]',
                attributes: [
                    { name: 'name', value: 'description' },
                    { name: 'content', value: route.description }
                ]
            });
        }

        if (route.canonical) {
            ensures.push({
                name: 'link',
                query: '[rel="canonical"]',
                attributes: [
                    { name: 'rel', value: 'canonical' },
                    { name: 'href', value: route.canonical }
                ]
            });
        }

        if (ensures.length) {
            Promise.all(ensures.map(function (option) {
                return Promise.resolve().then(function () {
                    option.position = 'afterbegin';
                    option.scope = document.head;
                    return Ensure(option);
                });
            }));
        }

        if (!route.target) {
            if (!route.component) {
                Component.define(route);
                route.target = window.document.createElement(route.name);
            } else if (route.component.constructor === String) {
                route.target = window.document.createElement(route.component);
            } else if (route.component.constructor === Object) {
                Component.define(route.component);
                route.target = window.document.createElement(route.component.name);
            } else {
                throw new Error('Oxe.router.render - route requires name, load, or component property');
            }
        }

        if (this.option.target) {
            while (this.option.target.firstChild) {
                this.option.target.removeChild(this.option.target.firstChild);
            }

            this.option.target.appendChild(route.target);
        }

        this.scroll(0, 0);
    },

    async route (path, options) {
        options = options || {};

        if (options.query) {
            path += this.toQueryString(options.query);
        }

        const mode = options.mode || this.option.mode;
        const location = this.toLocationObject(path);
        const route = await this.find(location.pathname);

        if (!route) {
            throw new Error(`Oxe.router.route - missing route ${location.pathname}`);
        }

        location.route = route;
        location.title = location.route.title;
        location.query = this.toQueryObject(location.search);
        location.parameters = this.toParameterObject(location.route.path, location.pathname);

        if (location.route && location.route.handler) {
            return await location.route.handler(location);
        }

        if (location.route && location.route.redirect) {
            return await this.redirect(location.route.redirect);
        }

        if (typeof this.option.before === 'function') {
            await this.option.before(location);
        }

        Events.emit('route:before', location);

        if (mode === 'href') {
            return window.location.assign(location.path);
        }

        window.history[mode + 'State']({ path: location.path }, '', location.path);

        this.location.href = location.href;
        this.location.host = location.host;
        this.location.port = location.port;
        this.location.hash = location.hash;
        this.location.path = location.path;
        this.location.route = location.route;
        this.location.title = location.title;
        this.location.query = location.query;
        this.location.search = location.search;
        this.location.protocol = location.protocol;
        this.location.hostname = location.hostname;
        this.location.pathname = location.pathname;
        this.location.parameters = location.parameters;

        await this.render(location.route);

        if (typeof this.option.after === 'function') {
            await this.option.after(location);
        }

        Events.emit('route:after', location);
    },

    async state (event) {
        const path = event && event.state ? event.state.path : window.location.href;
        this.route(path, { mode: 'replace' });
    },

    async click (event) {

        // ignore canceled events, modified clicks, and right clicks
        if (
            event.target.type ||
            event.button !== 0 ||
            event.defaultPrevented ||
            event.altKey || event.ctrlKey || event.metaKey || event.shiftKey
        ) {
            return;
        }

        // if shadow dom use
        var target = event.path ? event.path[0] : event.target;
        var parent = target.parentElement;

        if (this.option.contain) {

            while (parent) {

                if (parent.nodeName === 'O-ROUTER') {
                    break;
                } else {
                    parent = parent.parentElement;
                }

            }

            if (parent.nodeName !== 'O-ROUTER') {
                return;
            }

        }

        while (target && 'A' !== target.nodeName) {
            target = target.parentElement;
        }

        if (!target || 'A' !== target.nodeName) {
            return;
        }

        // check non-acceptables
        if (target.hasAttribute('download') ||
            target.hasAttribute('external') ||
            target.hasAttribute('o-external') ||
            target.href.indexOf('tel:') === 0 ||
            target.href.indexOf('ftp:') === 0 ||
            target.href.indexOf('file:') === 0 ||
            target.href.indexOf('mailto:') === 0 ||
            target.href.indexOf(window.location.origin) !== 0 ||
            (target.hash !== '' &&
                target.origin === window.location.origin &&
                target.pathname === window.location.pathname)
        ) return;

        // if external is true then default action
        if (this.option.external &&
            (this.option.external.constructor === RegExp && this.option.external.test(target.href) ||
                this.option.external.constructor === Function && this.option.external(target.href) ||
                this.option.external.constructor === String && this.option.external === target.href)
        ) return;

        event.preventDefault();

        if (this.location.href !== target.href) {
            this.route(target.href);
        }

    }

});
<commit_msg>added comment and note<commit_after>import Path from './path.js';
import Loader from './loader.js';
import Events from './events.js';
import Component from './component.js';
import Extend from './utility/extend.js';
import Ensure from './utility/ensure.js';

export default Object.freeze({

    data: [],
    location: {},

    option: {
        mode: 'push',
        target: null,
        contain: false,
        folder: './routes',
        external: null, before: null, after: null
    },

    async setup (option) {
        option = option || {};

        this.option.after = option.after === undefined ? this.option.after : option.after;
        this.option.before = option.before === undefined ? this.option.before : option.before;
        this.option.external = option.external === undefined ? this.option.external : option.external;

        this.option.mode = option.mode === undefined ? this.option.mode : option.mode;
        this.option.folder = option.folder === undefined ? this.option.folder : option.folder;
        this.option.target = option.target === undefined ? this.option.target : option.target;
        this.option.contain = option.contain === undefined ? this.option.contain : option.contain;

        if (!this.option.target || typeof this.option.target === 'string') {
            this.option.target = document.body.querySelector(this.option.target || 'o-router');
        }

        if (this.option.mode !== 'href') {
            window.addEventListener('popstate', this.state.bind(this), true);
            window.document.addEventListener('click', this.click.bind(this), true);
        }

        window.customElements.define('o-router', Extend(function () {}, HTMLElement));

        await this.add(option.routes);
        await this.route(window.location.href, { mode: 'replace' });
    },

    compareParts (routePath, userPath, split) {
        const compareParts = [];

        const routeParts = routePath.split(split);
        const userParts = userPath.split(split);

        if (userParts.length > 1 && userParts[userParts.length - 1] === '') {
            userParts.pop();
        }

        if (routeParts.length > 1 && routeParts[routeParts.length - 1] === '') {
            routeParts.pop();
        }

        for (let i = 0, l = routeParts.length; i < l; i++) {

            if (routeParts[i].slice(0, 1) === '(' && routeParts[i].slice(-1) === ')') {

                if (routeParts[i] === '(~)') {
                    return true;
                } else if (routeParts[i].indexOf('~') !== -1) {
                    if (userParts[i]) {
                        compareParts.push(userParts[i]);
                    }
                } else {
                    compareParts.push(userParts[i]);
                }

            } else if (routeParts[i] !== userParts[i]) {
                return false;
            } else {
                compareParts.push(routeParts[i]);
            }

        }

        if (compareParts.join(split) === userParts.join(split)) {
            return true;
        } else {
            return false;
        }
    },

    compare (routePath, userPath) {
        const base = Path.normalize(Path.base);

        userPath = Path.normalize(userPath);
        routePath = Path.normalize(routePath);

        if (userPath.slice(0, base.length) !== base) {
            userPath = Path.join(base, userPath);
        }

        if (routePath.slice(0, base.length) !== base) {
            routePath = Path.join(base, routePath);
        }

        if (this.compareParts(routePath, userPath, '/')) {
            return true;
        }

        if (this.compareParts(routePath, userPath, '-')) {
            return true;
        }

        return false;
    },

    toParameterObject (routePath, userPath) {
        let result = {};

        if (
            !routePath
            || !userPath
            || routePath === '/'
            || userPath === '/'
        ) return result;

        const userParts = userPath.split(/\/|-/);
        const routeParts = routePath.split(/\/|-/);

        for (let i = 0, l = routeParts.length; i < l; i++) {
            let part = routeParts[i];

            if (part.slice(0, 1) === '(' && part.slice(-1) === ')') {
                const name = part.slice(1, part.length - 1).replace('~', '');
                result[name] = userParts[i];
            }

        }

        return result;
    },

    toQueryString (data) {
        let result = '?';

        for (let key in data) {
            let value = data[key];
            result += key + '=' + value + '&';
        }

        if (result.slice(-1) === '&') {
            result = result.slice(0, -1);
        }

        return result;
    },

    toQueryObject (path) {
        let result = {};

        if (path.indexOf('?') === 0) path = path.slice(1);
        let queries = path.split('&');

        for (let i = 0, l = queries.length; i < l; i++) {
            let query = queries[i].split('=');

            if (query[0] && query[1]) {
                result[query[0]] = query[1];
            }

        }

        return result;
    },

    toLocationObject (href) {
        const location = {};
        const parser = document.createElement('a');

        parser.href = href;

        location.href = parser.href;
        location.host = parser.host;
        location.port = parser.port;
        location.hash = parser.hash;
        location.search = parser.search;
        location.protocol = parser.protocol;
        location.hostname = parser.hostname;
        location.pathname = parser.pathname[0] === '/' ? parser.pathname : '/' + parser.pathname;

        location.path = location.pathname + location.search + location.hash;

        return location;
    },

    scroll (x, y) {
        window.scroll(x, y);
    },

    back () {
        window.history.back();
    },

    forward () {
        window.history.forward();
    },

    redirect (path) {
        window.location.href = path;
    },

    async add (data) {
        if (!data) {
            return;
        } else if (data.constructor === String) {
            let path = data;

            if (path.slice(-3) === '.js') {
                path = path.slice(0, -3);
            }

            let load = path;

            if (path.slice(-5) === 'index') {
                path = path.slice(0, -5);
            }

            if (path.slice(-6) === 'index/') {
                path = path.slice(0, -6);
            }

            if (path.slice(0, 2) === './') {
                path = path.slice(2);
            }

            if (path.slice(0, 1) !== '/') {
                path = '/' + path;
            }

            console.warn('need to look at why we had this');
            // load = load.replace(/\/?\((\w+)?~\)\/?/ig, '') + '.js';
            load =  + '.js';
            load = Path.join(this.option.folder, load);

            this.data.push({ path, load });
        } else if (data.constructor === Object) {

            if (!data.path) {
                throw new Error('Oxe.router.add - route path required');
            }

            if (!data.name && !data.load && !data.component) {
                throw new Error('Oxe.router.add -  route requires name, load, or component property');
            }

            this.data.push(data);
        } else if (data.constructor === Array) {

            for (let i = 0, l = data.length; i < l; i++) {
                await this.add(data[i]);
            }

        }
    },

    async load (route) {

        if (route.load) {
            const load = await Loader.load(route.load);
            route = Object.assign({}, load.default, route);
        }

        if (typeof route.component === 'string') {
            route.load = route.component;
            const load = await Loader.load(route.load);
            route.component = load.default;
        }

        return route;
    },

    async remove (path) {
        for (let i = 0, l = this.data.length; i < l; i++) {
            if (this.data[i].path === path) {
                this.data.splice(i, 1);
            }
        }
    },

    async get (path) {
        for (let i = 0, l = this.data.length; i < l; i++) {
            if (this.data[i].path === path) {
                this.data[i] = await this.load(this.data[i]);
                return this.data[i];
            }
        }
    },

    async filter (path) {
        const result = [];

        for (let i = 0, l = this.data.length; i < l; i++) {
            if (this.compare(this.data[i].path, path)) {
                this.data[i] = await this.load(this.data[i]);
                result.push(this.data[i]);
            }
        }

        return result;
    },

    async find (path) {
        for (let i = 0, l = this.data.length; i < l; i++) {
            if (this.compare(this.data[i].path, path)) {
                this.data[i] = await this.load(this.data[i]);
                return this.data[i];
            }
        }
    },

    async render (route) {

        if (!route) {
            throw new Error('Oxe.render - route argument required. Missing object option.');
        }

        if (route.title) {
            document.title = route.title;
        }

        const ensures = [];

        if (route.keywords) {
            ensures.push({
                name: 'meta',
                query: '[name="keywords"]',
                attributes: [
                    { name: 'name', value: 'keywords' },
                    { name: 'content', value: route.keywords }
                ]
            });
        }

        if (route.description) {
            ensures.push({
                name: 'meta',
                query: '[name="description"]',
                attributes: [
                    { name: 'name', value: 'description' },
                    { name: 'content', value: route.description }
                ]
            });
        }

        if (route.canonical) {
            ensures.push({
                name: 'link',
                query: '[rel="canonical"]',
                attributes: [
                    { name: 'rel', value: 'canonical' },
                    { name: 'href', value: route.canonical }
                ]
            });
        }

        if (ensures.length) {
            Promise.all(ensures.map(function (option) {
                return Promise.resolve().then(function () {
                    option.position = 'afterbegin';
                    option.scope = document.head;
                    return Ensure(option);
                });
            }));
        }

        if (!route.target) {
            if (!route.component) {
                Component.define(route);
                route.target = window.document.createElement(route.name);
            } else if (route.component.constructor === String) {
                route.target = window.document.createElement(route.component);
            } else if (route.component.constructor === Object) {
                Component.define(route.component);
                route.target = window.document.createElement(route.component.name);
            } else {
                throw new Error('Oxe.router.render - route requires name, load, or component property');
            }
        }

        if (this.option.target) {
            while (this.option.target.firstChild) {
                this.option.target.removeChild(this.option.target.firstChild);
            }

            this.option.target.appendChild(route.target);
        }

        this.scroll(0, 0);
    },

    async route (path, options) {
        options = options || {};

        if (options.query) {
            path += this.toQueryString(options.query);
        }

        const mode = options.mode || this.option.mode;
        const location = this.toLocationObject(path);
        const route = await this.find(location.pathname);

        if (!route) {
            throw new Error(`Oxe.router.route - missing route ${location.pathname}`);
        }

        location.route = route;
        location.title = location.route.title;
        location.query = this.toQueryObject(location.search);
        location.parameters = this.toParameterObject(location.route.path, location.pathname);

        if (location.route && location.route.handler) {
            return await location.route.handler(location);
        }

        if (location.route && location.route.redirect) {
            return await this.redirect(location.route.redirect);
        }

        if (typeof this.option.before === 'function') {
            await this.option.before(location);
        }

        Events.emit('route:before', location);

        if (mode === 'href') {
            return window.location.assign(location.path);
        }

        window.history[mode + 'State']({ path: location.path }, '', location.path);

        this.location.href = location.href;
        this.location.host = location.host;
        this.location.port = location.port;
        this.location.hash = location.hash;
        this.location.path = location.path;
        this.location.route = location.route;
        this.location.title = location.title;
        this.location.query = location.query;
        this.location.search = location.search;
        this.location.protocol = location.protocol;
        this.location.hostname = location.hostname;
        this.location.pathname = location.pathname;
        this.location.parameters = location.parameters;

        await this.render(location.route);

        if (typeof this.option.after === 'function') {
            await this.option.after(location);
        }

        Events.emit('route:after', location);
    },

    async state (event) {
        const path = event && event.state ? event.state.path : window.location.href;
        this.route(path, { mode: 'replace' });
    },

    async click (event) {

        // ignore canceled events, modified clicks, and right clicks
        if (
            event.target.type ||
            event.button !== 0 ||
            event.defaultPrevented ||
            event.altKey || event.ctrlKey || event.metaKey || event.shiftKey
        ) {
            return;
        }

        // if shadow dom use
        var target = event.path ? event.path[0] : event.target;
        var parent = target.parentElement;

        if (this.option.contain) {

            while (parent) {

                if (parent.nodeName === 'O-ROUTER') {
                    break;
                } else {
                    parent = parent.parentElement;
                }

            }

            if (parent.nodeName !== 'O-ROUTER') {
                return;
            }

        }

        while (target && 'A' !== target.nodeName) {
            target = target.parentElement;
        }

        if (!target || 'A' !== target.nodeName) {
            return;
        }

        // check non-acceptables
        if (target.hasAttribute('download') ||
            target.hasAttribute('external') ||
            target.hasAttribute('o-external') ||
            target.href.indexOf('tel:') === 0 ||
            target.href.indexOf('ftp:') === 0 ||
            target.href.indexOf('file:') === 0 ||
            target.href.indexOf('mailto:') === 0 ||
            target.href.indexOf(window.location.origin) !== 0 ||
            (target.hash !== '' &&
                target.origin === window.location.origin &&
                target.pathname === window.location.pathname)
        ) return;

        // if external is true then default action
        if (this.option.external &&
            (this.option.external.constructor === RegExp && this.option.external.test(target.href) ||
                this.option.external.constructor === Function && this.option.external(target.href) ||
                this.option.external.constructor === String && this.option.external === target.href)
        ) return;

        event.preventDefault();

        if (this.location.href !== target.href) {
            this.route(target.href);
        }

    }

});
<|endoftext|>


<commit_before>import {RouteRecognizer} from 'aurelia-route-recognizer';
import {join} from 'aurelia-path';
import {NavigationContext} from './navigation-context';
import {NavigationInstruction} from './navigation-instruction';
import {RouterConfiguration} from './router-configuration';
import {processPotential} from './util';

export class Router {
  constructor(container, history) {
    this.container = container;
    this.history = history;
    this.viewPorts = {};
    this.reset();
    this.baseUrl = '';
  }

  registerViewPort(viewPort, name) {
    name = name || 'default';
    this.viewPorts[name] = viewPort;
  }

  refreshBaseUrl() {
    if (this.parent) {
      var baseUrl = this.parent.currentInstruction.getBaseUrl();
      this.baseUrl = this.parent.baseUrl + baseUrl;
    }
  }

  refreshNavigation() {
    var nav = this.navigation;

    for(var i = 0, length = nav.length; i < length; i++) {
      var current = nav[i];

      if(!this.history._hasPushState){
        if (this.baseUrl[0] == '/') {
          current.href = '#' + this.baseUrl;
        } else {
          current.href = '#/' + this.baseUrl;
        }
      }

      if (current.href[current.href.length - 1] != '/') {
        current.href += '/';
      }

      current.href += current.relativeHref;
    }
  }

  configure(callbackOrConfig) {
    if (typeof callbackOrConfig == 'function') {
      var config = new RouterConfiguration();
      callbackOrConfig(config);
      config.exportToRouter(this);
    } else {
      callbackOrConfig.exportToRouter(this);
    }

    return this;
  }

  navigate(fragment, options) {
    fragment = join(this.baseUrl, fragment);
    return this.history.navigate(fragment, options);
  }

  navigateBack() {
    this.history.navigateBack();
  }

  createChild(container) {
    var childRouter = new Router(container || this.container.createChild(), this.history);
    childRouter.parent = this;
    return childRouter;
  }

  createNavigationInstruction(url='', parentInstruction=null) {
    var results = this.recognizer.recognize(url);
    var fragment, queryIndex, queryString;

    if (!results || !results.length) {
      results = this.childRecognizer.recognize(url);
    }

    fragment = url
    queryIndex = fragment.indexOf("?");

    if (queryIndex != -1) {
      fragment = url.substr(0, queryIndex);
      queryString = url.substr(queryIndex + 1);
    }

    if((!results || !results.length) && this.catchAllHandler){
      results = [{
        config:{
          navModel:{}
        },
        handler:this.catchAllHandler,
        params:{
          path:fragment
        }
      }];
    }

    if (results && results.length) {
      var first = results[0],
          fragment = url,
          queryIndex = fragment.indexOf('?'),
          queryString;

      if (queryIndex != -1) {
        fragment = url.substr(0, queryIndex);
        queryString = url.substr(queryIndex + 1);
      }

      var instruction = new NavigationInstruction(
        fragment,
        queryString,
        first.params,
        first.queryParams || results.queryParams,
        first.config || first.handler,
        parentInstruction
        );

      if (typeof first.handler == "function") {
        return first.handler(instruction).then(instruction => {
          if (!("viewPorts" in instruction.config)) {
            instruction.config.viewPorts = {
              "default": {
                moduleId: instruction.config.moduleId
              }
            };
          }

          return instruction;
        });
      }

      return Promise.resolve(instruction);
    } else {
      return Promise.reject(new Error(`Route Not Found: ${url}`));
    }
  }

  createNavigationContext(instruction) {
    return new NavigationContext(this, instruction);
  }

  generate(name, params) {
    return this.recognizer.generate(name, params);
  }

  addRoute(config, navModel={}) {
    if (!('viewPorts' in config)) {
      config.viewPorts = {
        'default': {
          moduleId: config.moduleId,
          view: config.view
        }
      };
    }

    navModel.title = navModel.title || config.title;

    this.routes.push(config);
    this.recognizer.add([{path:config.route, handler: config}]);

    if (config.route) {
      var withChild = JSON.parse(JSON.stringify(config));
      withChild.route += "/*childRoute";
      withChild.hasChildRouter = true;
      this.childRecognizer.add([{
        path: withChild.route,
        handler: withChild
      }]);

      withChild.navModel = navModel;
    }

    config.navModel = navModel;

    if ((config.nav || 'order' in navModel) && this.navigation.indexOf(navModel) === -1) {
      navModel.order = navModel.order || config.nav;
      navModel.href = navModel.href || config.href;
      navModel.isActive = false;
      navModel.config = config;

      if (!config.href) {
        navModel.relativeHref = config.route;
        navModel.href = '';
      }

      if (typeof navModel.order != 'number') {
        navModel.order = ++this.fallbackOrder;
      }

      this.navigation.push(navModel);
      this.navigation = this.navigation.sort((a, b) => a.order - b.order);
    }
  }

  handleUnknownRoutes(config) {
    var callback = instruction => new Promise((resolve, reject) => {
      function done(inst){
        inst = inst || instruction;
        inst.config.route = inst.params.path;
        resolve(inst);
      }

      if (!config) {
        instruction.config.moduleId = instruction.fragment;
        done(instruction);
      } else if (typeof config == 'string') {
        instruction.config.moduleId = config;
        done(instruction);
      } else if (typeof config == 'function') {
        processPotential(config(instruction), done, reject);
      } else {
        instruction.config = config;
        done(instruction);
      }
    });

    this.catchAllHandler = callback;
  }

  reset() {
    this.fallbackOrder = 100;
    this.recognizer = new RouteRecognizer();
    this.childRecognizer = new RouteRecognizer();
    this.routes = [];
    this.isNavigating = false;
    this.navigation = [];
  }
}
<commit_msg>fix(router): url fragments duplicated on subsequent navigations between routes when using pushState<commit_after>import {RouteRecognizer} from 'aurelia-route-recognizer';
import {join} from 'aurelia-path';
import {NavigationContext} from './navigation-context';
import {NavigationInstruction} from './navigation-instruction';
import {RouterConfiguration} from './router-configuration';
import {processPotential} from './util';

export class Router {
  constructor(container, history) {
    this.container = container;
    this.history = history;
    this.viewPorts = {};
    this.reset();
    this.baseUrl = '';
  }

  registerViewPort(viewPort, name) {
    name = name || 'default';
    this.viewPorts[name] = viewPort;
  }

  refreshBaseUrl() {
    if (this.parent) {
      var baseUrl = this.parent.currentInstruction.getBaseUrl();
      this.baseUrl = this.parent.baseUrl + baseUrl;
    }
  }

  refreshNavigation() {
    var nav = this.navigation;

    for(var i = 0, length = nav.length; i < length; i++) {
      var current = nav[i];

      if(!this.history._hasPushState){
        if (this.baseUrl[0] == '/') {
          current.href = '#' + this.baseUrl;
        } else {
          current.href = '#/' + this.baseUrl;
        }
      } else {
        current.href = '/' + this.baseUrl;
      }

      if (current.href[current.href.length - 1] != '/') {
        current.href += '/';
      }

      current.href += current.relativeHref;
    }
  }

  configure(callbackOrConfig) {
    if (typeof callbackOrConfig == 'function') {
      var config = new RouterConfiguration();
      callbackOrConfig(config);
      config.exportToRouter(this);
    } else {
      callbackOrConfig.exportToRouter(this);
    }

    return this;
  }

  navigate(fragment, options) {
    fragment = join(this.baseUrl, fragment);
    return this.history.navigate(fragment, options);
  }

  navigateBack() {
    this.history.navigateBack();
  }

  createChild(container) {
    var childRouter = new Router(container || this.container.createChild(), this.history);
    childRouter.parent = this;
    return childRouter;
  }

  createNavigationInstruction(url='', parentInstruction=null) {
    var results = this.recognizer.recognize(url);
    var fragment, queryIndex, queryString;

    if (!results || !results.length) {
      results = this.childRecognizer.recognize(url);
    }

    fragment = url
    queryIndex = fragment.indexOf("?");

    if (queryIndex != -1) {
      fragment = url.substr(0, queryIndex);
      queryString = url.substr(queryIndex + 1);
    }

    if((!results || !results.length) && this.catchAllHandler){
      results = [{
        config:{
          navModel:{}
        },
        handler:this.catchAllHandler,
        params:{
          path:fragment
        }
      }];
    }

    if (results && results.length) {
      var first = results[0],
          fragment = url,
          queryIndex = fragment.indexOf('?'),
          queryString;

      if (queryIndex != -1) {
        fragment = url.substr(0, queryIndex);
        queryString = url.substr(queryIndex + 1);
      }

      var instruction = new NavigationInstruction(
        fragment,
        queryString,
        first.params,
        first.queryParams || results.queryParams,
        first.config || first.handler,
        parentInstruction
        );

      if (typeof first.handler == "function") {
        return first.handler(instruction).then(instruction => {
          if (!("viewPorts" in instruction.config)) {
            instruction.config.viewPorts = {
              "default": {
                moduleId: instruction.config.moduleId
              }
            };
          }

          return instruction;
        });
      }

      return Promise.resolve(instruction);
    } else {
      return Promise.reject(new Error(`Route Not Found: ${url}`));
    }
  }

  createNavigationContext(instruction) {
    return new NavigationContext(this, instruction);
  }

  generate(name, params) {
    return this.recognizer.generate(name, params);
  }

  addRoute(config, navModel={}) {
    if (!('viewPorts' in config)) {
      config.viewPorts = {
        'default': {
          moduleId: config.moduleId,
          view: config.view
        }
      };
    }

    navModel.title = navModel.title || config.title;

    this.routes.push(config);
    this.recognizer.add([{path:config.route, handler: config}]);

    if (config.route) {
      var withChild = JSON.parse(JSON.stringify(config));
      withChild.route += "/*childRoute";
      withChild.hasChildRouter = true;
      this.childRecognizer.add([{
        path: withChild.route,
        handler: withChild
      }]);

      withChild.navModel = navModel;
    }

    config.navModel = navModel;

    if ((config.nav || 'order' in navModel) && this.navigation.indexOf(navModel) === -1) {
      navModel.order = navModel.order || config.nav;
      navModel.href = navModel.href || config.href;
      navModel.isActive = false;
      navModel.config = config;

      if (!config.href) {
        navModel.relativeHref = config.route;
        navModel.href = '';
      }

      if (typeof navModel.order != 'number') {
        navModel.order = ++this.fallbackOrder;
      }

      this.navigation.push(navModel);
      this.navigation = this.navigation.sort((a, b) => a.order - b.order);
    }
  }

  handleUnknownRoutes(config) {
    var callback = instruction => new Promise((resolve, reject) => {
      function done(inst){
        inst = inst || instruction;
        inst.config.route = inst.params.path;
        resolve(inst);
      }

      if (!config) {
        instruction.config.moduleId = instruction.fragment;
        done(instruction);
      } else if (typeof config == 'string') {
        instruction.config.moduleId = config;
        done(instruction);
      } else if (typeof config == 'function') {
        processPotential(config(instruction), done, reject);
      } else {
        instruction.config = config;
        done(instruction);
      }
    });

    this.catchAllHandler = callback;
  }

  reset() {
    this.fallbackOrder = 100;
    this.recognizer = new RouteRecognizer();
    this.childRecognizer = new RouteRecognizer();
    this.routes = [];
    this.isNavigating = false;
    this.navigation = [];
  }
}
<|endoftext|>


<commit_before>var fs = require("fs");

module.exports = function vfs(request) {
    return {
        getFile: function getFile(path, cb) {
            request("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        },
        listFiles: function listFiles(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, JSON.parse(body), response);
            });
        },
        uploadFile: function uploadFile(localPath, destPath, etag, cb) {
            if (typeof etag === "function") {
                cb = etag;
                etag = "*";
            }
            fs.createReadStream(localPath).pipe(request.put({
                uri: "/api/vfs/" + destPath,
                headers: {
                    "If-Match": etag
                }
            }, function(err, response, body) {
                if (err) return cb(err);

                if (response.statusCode >= 400) {
                    cb(JSON.parse(body), response);
                }

                cb(null, response);
            }));
        },
        createDirectory: function createDirectory(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request.put("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        },
        deleteFile: function deleteFile(path, cb) {
            request.del("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        },
        deleteDirectory: function deleteDirectory(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request.del("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        }
    }
};
<commit_msg>Add etag support to delete file operation.<commit_after>var fs = require("fs");

module.exports = function vfs(request) {
    return {
        getFile: function getFile(path, cb) {
            request("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        },
        listFiles: function listFiles(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, JSON.parse(body), response);
            });
        },
        uploadFile: function uploadFile(localPath, destPath, etag, cb) {
            if (typeof etag === "function") {
                cb = etag;
                etag = "*";
            }
            fs.createReadStream(localPath).pipe(request.put({
                uri: "/api/vfs/" + destPath,
                headers: {
                    "If-Match": etag
                }
            }, function(err, response, body) {
                if (err) return cb(err);

                if (response.statusCode >= 400) {
                    cb(JSON.parse(body), response);
                }

                cb(null, response);
            }));
        },
        createDirectory: function createDirectory(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request.put("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        },
        deleteFile: function deleteFile(path, etag, cb) {
            if (typeof etag === "function") {
                cb = etag;
                etag = "*";
            }

            var options = {
                uri: "/api/vfs/" + path,
                headers: {
                    "If-Match": etag
                }
            }

            request.del(options, function (err, response, body) {
                if (err) {
                    return cb(err);
                }

                cb(null, body, response);
            });
        },
        deleteDirectory: function deleteDirectory(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request.del("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        }
    }
};
<|endoftext|>


<commit_before>(function (win, events) {
    var DEFAULT_ORIGIN = '*';
    var xde, eventListeners;
    var onlyStrings;

    function onMessage(evt) {
        var parsedData;

        if (xde.targetOrigin != DEFAULT_ORIGIN && evt.origin !== xde.targetOrigin) {
            return;
        }

        parsedData = evt.data;
        if (typeof evt.data == 'string') {
            try {
                parsedData = JSON.parse(evt.data);
            } catch (e) {}
        }

        if (!parsedData || parsedData.__xde !== true) {
            return;
        }

        parsedData.origin = evt.origin;
        parsedData.source = evt.source;

        var listeners = eventListeners[parsedData.name];
        if (listeners && listeners.length > 0) {
            listeners.forEach(function(fn){
                fn(parsedData);
            });
        }
    }

    function validateName (name) {
        if (!name || typeof name != 'string') { throw new Error('Event name has to be a string'); };
    }

    function validateNameAndFn (name, fn) {
        validateName(name);
        if (typeof fn != "function" ) { throw new Error('Callback function has to be given'); }
    }

    xde = {
        on: function (name, fn) {
            validateNameAndFn(name, fn);

            if (typeof eventListeners[name] == 'undefined') {
                eventListeners[name] = [];
            }
            eventListeners[name].push(fn);
        },

        off: function (name, fn) {
            validateNameAndFn(name, fn);
            var listeners = eventListeners[name];
            if (!listeners) {
                return;
            }
            eventListeners[name] = listeners.filter(function (listener) {
                return listener !== fn;
            });
        },

        sendTo: function (otherWindow, name, data) {
            validateName(name);
            try {
                if (otherWindow && otherWindow.contentWindow) {
                    otherWindow = otherWindow.contentWindow;
                }
            } catch(e) {}
            if ( !otherWindow || !otherWindow.postMessage ) {
                throw new TypeError('otherWindow does not support postMessage');
            }

            var msg = {
                name : name,
                data : data,
                __xde : true
            };
            if (onlyStrings) {
                msg = JSON.stringify(msg);
            }
            otherWindow.postMessage(msg, this.targetOrigin); // TODO fix security issue
        },

        targetOrigin: DEFAULT_ORIGIN,

        _reset: function (forceStringify) {
            eventListeners = {};
            this.targetOrigin = DEFAULT_ORIGIN;
            onlyStrings = forceStringify || false;
            try{win.postMessage({toString:function(){onlyStrings=true;}},"*");}catch(e){}
        }
    };

    xde._reset();
    events.add(window, 'message', onMessage, false);

    if(typeof exports === 'object') {
        module.exports = xde;
    } else {
        win.xde = xde;
    }
})(this, this.eventListener || require('eventlistener'));
<commit_msg>global object consistency<commit_after>(function (win, events) {
    var DEFAULT_ORIGIN = '*';
    var xde, eventListeners;
    var onlyStrings;

    function onMessage(evt) {
        var parsedData;

        if (xde.targetOrigin != DEFAULT_ORIGIN && evt.origin !== xde.targetOrigin) {
            return;
        }

        parsedData = evt.data;
        if (typeof evt.data == 'string') {
            try {
                parsedData = JSON.parse(evt.data);
            } catch (e) {}
        }

        if (!parsedData || parsedData.__xde !== true) {
            return;
        }

        parsedData.origin = evt.origin;
        parsedData.source = evt.source;

        var listeners = eventListeners[parsedData.name];
        if (listeners && listeners.length > 0) {
            listeners.forEach(function(fn){
                fn(parsedData);
            });
        }
    }

    function validateName (name) {
        if (!name || typeof name != 'string') { throw new Error('Event name has to be a string'); };
    }

    function validateNameAndFn (name, fn) {
        validateName(name);
        if (typeof fn != "function" ) { throw new Error('Callback function has to be given'); }
    }

    xde = {
        on: function (name, fn) {
            validateNameAndFn(name, fn);

            if (typeof eventListeners[name] == 'undefined') {
                eventListeners[name] = [];
            }
            eventListeners[name].push(fn);
        },

        off: function (name, fn) {
            validateNameAndFn(name, fn);
            var listeners = eventListeners[name];
            if (!listeners) {
                return;
            }
            eventListeners[name] = listeners.filter(function (listener) {
                return listener !== fn;
            });
        },

        sendTo: function (otherWindow, name, data) {
            validateName(name);
            try {
                if (otherWindow && otherWindow.contentWindow) {
                    otherWindow = otherWindow.contentWindow;
                }
            } catch(e) {}
            if ( !otherWindow || !otherWindow.postMessage ) {
                throw new TypeError('otherWindow does not support postMessage');
            }

            var msg = {
                name : name,
                data : data,
                __xde : true
            };
            if (onlyStrings) {
                msg = JSON.stringify(msg);
            }
            otherWindow.postMessage(msg, this.targetOrigin); // TODO fix security issue
        },

        targetOrigin: DEFAULT_ORIGIN,

        _reset: function (forceStringify) {
            eventListeners = {};
            this.targetOrigin = DEFAULT_ORIGIN;
            onlyStrings = forceStringify || false;
            try{win.postMessage({toString:function(){onlyStrings=true;}},"*");}catch(e){}
        }
    };

    xde._reset();
    events.add(win, 'message', onMessage, false);

    if(typeof exports === 'object') {
        module.exports = xde;
    } else {
        win.xde = xde;
    }
})(this.window || (typeof global !== "undefined" ? global : this), this.eventListener || require('eventlistener'));
<|endoftext|>


<commit_before>'use strict';
/**
 * utils module
 * @module utils
 * @see module:index
 */
const lang = require('zero-lang');
const xmlFormat = require('./xml-format');
const domUtils = require('./dom-utils');

const PARSER_ERROR_TAG = 'parsererror';
const XML_DOCTYPE = 'text/xml';

const xml = {
  domParser: null, // FIXME: to be initialised
  xmlSerializer: null, // FIXME: to be initialised

  getParserErrorNS: () => {
    const errorneousParse = xml.domParser.parseFromString('<', XML_DOCTYPE);
    return errorneousParse.getElementsByTagName(PARSER_ERROR_TAG)[0].namespaceURI;
  },

  isParserError: (parsedDom, str) => {
    // for xmldom: xml with only "text element"
    if (
      parsedDom.childNodes.length === 0 || (
        parsedDom.childNodes.length === 1 &&
        parsedDom.firstChild.nodeType === domUtils.TEXT_NODE &&
        parsedDom.firstChild.data === str
      )
    ) {
      return true;
    }
    try {
      xml.parserErrorNS = xml.parserErrorNS || xml.getParserErrorNS();
      if (xml.parserErrorNS === 'http://www.w3.org/1999/xhtml') {
        // In PhantomJS the parseerror element doesn't seem to have a special namespace, so we are just guessing here :(
        return parsedDom.getElementsByTagName(PARSER_ERROR_TAG).length > 0;
      }
      return parsedDom.getElementsByTagNameNS(xml.parserErrorNS, PARSER_ERROR_TAG).length > 0;
    } catch (e) {
    }
  },

  parseFromString: (str) => {
    const uglifiedStr = xmlFormat.uglify(str, true);
    const dom = xml.domParser.parseFromString(uglifiedStr, XML_DOCTYPE);
    if (xml.isParserError(dom, uglifiedStr)) {
      throw new SyntaxError(`invalid XML: ${str}`);
    }
    return dom;
  },
  serializeToString: (doc, prettyPrint) => {
    const result = xml.xmlSerializer.serializeToString(doc);
    return prettyPrint ? xmlFormat.beautify(result) : result;
  },
};

lang.extend(xml, {
  parse: xml.parseFromString,
  serialize: xml.serializeToString,
}, xmlFormat, domUtils);

module.exports = xml;
<commit_msg>eslint<commit_after>'use strict';
/**
 * utils module
 * @module utils
 * @see module:index
 */
const lang = require('zero-lang');
const xmlFormat = require('./xml-format');
const domUtils = require('./dom-utils');

const PARSER_ERROR_TAG = 'parsererror';
const XML_DOCTYPE = 'text/xml';

const xml = {
  domParser: null, // FIXME: to be initialised
  xmlSerializer: null, // FIXME: to be initialised

  getParserErrorNS: () => {
    const errorneousParse = xml.domParser.parseFromString('<', XML_DOCTYPE);
    return errorneousParse.getElementsByTagName(PARSER_ERROR_TAG)[0].namespaceURI;
  },

  isParserError: (parsedDom, str) => {
    // for xmldom: xml with only "text element"
    if (
      parsedDom.childNodes.length === 0 || (
        parsedDom.childNodes.length === 1 &&
        parsedDom.firstChild.nodeType === domUtils.TEXT_NODE &&
        parsedDom.firstChild.data === str
      )
    ) {
      return true;
    }
    try {
      xml.parserErrorNS = xml.parserErrorNS || xml.getParserErrorNS();
      if (xml.parserErrorNS === 'http://www.w3.org/1999/xhtml') {
        // In PhantomJS the parseerror element doesn't seem to have a special namespace, so we are just guessing here :(
        return parsedDom.getElementsByTagName(PARSER_ERROR_TAG).length > 0;
      }
      return parsedDom.getElementsByTagNameNS(xml.parserErrorNS, PARSER_ERROR_TAG).length > 0;
    } catch (e) {
      return false;
    }
  },

  parseFromString: (str) => {
    const uglifiedStr = xmlFormat.uglify(str, true);
    const dom = xml.domParser.parseFromString(uglifiedStr, XML_DOCTYPE);
    if (xml.isParserError(dom, uglifiedStr)) {
      throw new SyntaxError(`invalid XML: ${str}`);
    }
    return dom;
  },
  serializeToString: (doc, prettyPrint) => {
    const result = xml.xmlSerializer.serializeToString(doc);
    return prettyPrint ? xmlFormat.beautify(result) : result;
  },
};

lang.extend(xml, {
  parse: xml.parseFromString,
  serialize: xml.serializeToString,
}, xmlFormat, domUtils);

module.exports = xml;
<|endoftext|>


<commit_before>"use strict";

var plugin = {},
	async = module.parent.require('async'),
	topics = module.parent.require('./topics'),
	meta = module.parent.require('./meta'),
	privileges = module.parent.require('./privileges'),
	helpers = module.parent.require('./controllers/helpers'),
	db = module.parent.require('./database'),
	SocketPlugins = module.parent.require('./socket.io/plugins');

plugin.init = function(params, callback) {
	var app = params.router,
		middleware = params.middleware,
		controllers = params.controllers;

	app.get('/admin/plugins/question-and-answer', middleware.admin.buildHeader, renderAdmin);
	app.get('/api/admin/plugins/question-and-answer', renderAdmin);
	app.get('/unsolved', middleware.buildHeader, renderUnsolved);
	app.get('/api/unsolved', renderUnsolved);

	handleSocketIO();

	callback();
};

plugin.appendConfig = function(config, callback) {
	meta.settings.get('question-and-answer', function(err, settings) {
		config['question-and-answer'] = settings;
		callback(null, config);
	});
};

plugin.addNavigation = function(menu, callback) {
	menu.push({
		"route": "/unsolved",
		"title": "Unsolved",
		"iconClass": "fa-question-circle",
		"text": "Unsolved"
	});

	callback (null, menu);
};

plugin.addAdminNavigation = function(header, callback) {
	header.plugins.push({
		route: '/plugins/question-and-answer',
		icon: 'fa-question-circle',
		name: 'Q&A'
	});

	callback(null, header);
};

plugin.getTopics = function(data, callback) {
	var topics = data.topics;

	async.map(topics, function(topic, next) {
		if (parseInt(topic.isQuestion, 10)) {
			if (parseInt(topic.isSolved, 10)) {
				topic.title = ' Solved ' + topic.title;
			} else {
				topic.title = ' Unsolved ' + topic.title;
			}
		}

		return next(null, topic);
	}, function(err, topics) {
		return callback(err, data);
	});
};

plugin.addThreadTool = function(data, callback) {
	var isSolved = parseInt(data.topic.isSolved, 10);

	if (parseInt(data.topic.isQuestion, 10)) {
		data.tools = data.tools.concat([
			{
				class: 'toggleSolved ' + (isSolved ? 'alert-warning topic-solved' : 'alert-success topic-unsolved'),
				title: isSolved ? 'Mark as Unsolved' : 'Mark as Solved',
				icon: isSolved ? 'fa-question-circle' : 'fa-check-circle'
			},
			{
				class: 'toggleQuestionStatus',
				title: 'Make this a normal topic',
				icon: 'fa-comments'
			}
		]);
	} else {
		data.tools.push({
			class: 'toggleQuestionStatus alert-warning',
			title: 'Ask as question',
			icon: 'fa-question-circle'
		});
	}

	callback(false, data);
};

function renderAdmin(req, res, next) {
	res.render('admin/plugins/question-and-answer', {});
}

function handleSocketIO() {
	SocketPlugins.QandA = {};

	SocketPlugins.QandA.toggleSolved = function(socket, data, callback) {
		privileges.topics.canEdit(data.tid, socket.uid, function(err, canEdit) {
			if (!canEdit) {
				return callback(new Error('[[error:no-privileges]]'));
			}

			toggleSolved(data.tid, callback);
		});
	};

	SocketPlugins.QandA.toggleQuestionStatus = function(socket, data, callback) {
		privileges.topics.canEdit(data.tid, socket.uid, function(err, canEdit) {
			if (!canEdit) {
				return callback(new Error('[[error:no-privileges]]'));
			}

			toggleQuestionStatus(data.tid, callback);
		});
	};
}

function toggleSolved(tid, callback) {
	topics.getTopicField(tid, 'isSolved', function(err, isSolved) {
		isSolved = parseInt(isSolved, 10) === 1;

		async.parallel([
			function(next) {
				topics.setTopicField(tid, 'isSolved', isSolved ? 0 : 1, next);
			},
			function(next) {
				if (!isSolved) {
					db.sortedSetRemove('topics:unsolved', tid, next);
				} else {
					db.sortedSetAdd('topics:unsolved', Date.now(), tid, next);
				}
			}
		], function(err) {
			callback(err, {isSolved: !isSolved});
		});
	});
}

function toggleQuestionStatus(tid, callback) {
	topics.getTopicField(tid, 'isQuestion', function(err, isQuestion) {
		isQuestion = parseInt(isQuestion, 10) === 1;

		async.parallel([
			function(next) {
				topics.setTopicField(tid, 'isQuestion', isQuestion ? 0 : 1, next);
			},
			function(next) {
				if (!isQuestion) {
					db.sortedSetAdd('topics:unsolved', Date.now(), tid, next);
				} else {
					db.sortedSetRemove('topics:unsolved', tid, next);
				}
			}
		], function(err) {
			callback(err, {isQuestion: !isQuestion});
		});
	});
}

function renderUnsolved(req, res, next) {
	var stop = (parseInt(meta.config.topicsPerList, 10) || 20) - 1;
	topics.getTopicsFromSet('topics:unsolved', req.uid, 0, stop, function(err, data) {
		if (err) {
			return next(err);
		}

		data['feeds:disableRSS'] = true;
		data.breadcrumbs = helpers.buildBreadcrumbs([{text: 'Unsolved'}]);
		res.render('recent', data);
	});
}

module.exports = plugin;<commit_msg>closes #1<commit_after>"use strict";

var plugin = {},
	async = module.parent.require('async'),
	topics = module.parent.require('./topics'),
	meta = module.parent.require('./meta'),
	privileges = module.parent.require('./privileges'),
	helpers = module.parent.require('./controllers/helpers'),
	db = module.parent.require('./database'),
	SocketPlugins = module.parent.require('./socket.io/plugins');

plugin.init = function(params, callback) {
	var app = params.router,
		middleware = params.middleware,
		controllers = params.controllers;

	app.get('/admin/plugins/question-and-answer', middleware.admin.buildHeader, renderAdmin);
	app.get('/api/admin/plugins/question-and-answer', renderAdmin);

	app.get('/unsolved', middleware.buildHeader, renderUnsolved);
	app.get('/api/unsolved', renderUnsolved);

	app.get('/solved', middleware.buildHeader, renderSolved);
	app.get('/api/solved', renderSolved);

	handleSocketIO();

	callback();
};

plugin.appendConfig = function(config, callback) {
	meta.settings.get('question-and-answer', function(err, settings) {
		config['question-and-answer'] = settings;
		callback(null, config);
	});
};

plugin.addNavigation = function(menu, callback) {
	menu = menu.concat(
		[
			{
				"route": "/unsolved",
				"title": "Unsolved",
				"iconClass": "fa-question-circle",
				"text": "Unsolved"
			},
			{
				"route": "/solved",
				"title": "Solved",
				"iconClass": "fa-check-circle",
				"text": "Solved"
			}
		]
	);

	callback (null, menu);
};

plugin.addAdminNavigation = function(header, callback) {
	header.plugins.push({
		route: '/plugins/question-and-answer',
		icon: 'fa-question-circle',
		name: 'Q&A'
	});

	callback(null, header);
};

plugin.getTopics = function(data, callback) {
	var topics = data.topics;

	async.map(topics, function(topic, next) {
		if (parseInt(topic.isQuestion, 10)) {
			if (parseInt(topic.isSolved, 10)) {
				topic.title = ' Solved ' + topic.title;
			} else {
				topic.title = ' Unsolved ' + topic.title;
			}
		}

		return next(null, topic);
	}, function(err, topics) {
		return callback(err, data);
	});
};

plugin.addThreadTool = function(data, callback) {
	var isSolved = parseInt(data.topic.isSolved, 10);

	if (parseInt(data.topic.isQuestion, 10)) {
		data.tools = data.tools.concat([
			{
				class: 'toggleSolved ' + (isSolved ? 'alert-warning topic-solved' : 'alert-success topic-unsolved'),
				title: isSolved ? 'Mark as Unsolved' : 'Mark as Solved',
				icon: isSolved ? 'fa-question-circle' : 'fa-check-circle'
			},
			{
				class: 'toggleQuestionStatus',
				title: 'Make this a normal topic',
				icon: 'fa-comments'
			}
		]);
	} else {
		data.tools.push({
			class: 'toggleQuestionStatus alert-warning',
			title: 'Ask as question',
			icon: 'fa-question-circle'
		});
	}

	callback(false, data);
};

function renderAdmin(req, res, next) {
	res.render('admin/plugins/question-and-answer', {});
}

function handleSocketIO() {
	SocketPlugins.QandA = {};

	SocketPlugins.QandA.toggleSolved = function(socket, data, callback) {
		privileges.topics.canEdit(data.tid, socket.uid, function(err, canEdit) {
			if (!canEdit) {
				return callback(new Error('[[error:no-privileges]]'));
			}

			toggleSolved(data.tid, callback);
		});
	};

	SocketPlugins.QandA.toggleQuestionStatus = function(socket, data, callback) {
		privileges.topics.canEdit(data.tid, socket.uid, function(err, canEdit) {
			if (!canEdit) {
				return callback(new Error('[[error:no-privileges]]'));
			}

			toggleQuestionStatus(data.tid, callback);
		});
	};
}

function toggleSolved(tid, callback) {
	topics.getTopicField(tid, 'isSolved', function(err, isSolved) {
		isSolved = parseInt(isSolved, 10) === 1;

		async.parallel([
			function(next) {
				topics.setTopicField(tid, 'isSolved', isSolved ? 0 : 1, next);
			},
			function(next) {
				if (!isSolved) {
					db.sortedSetRemove('topics:unsolved', tid, function() {
						db.sortedSetAdd('topics:solved', Date.now(), tid, next);
					});
				} else {
					db.sortedSetAdd('topics:unsolved', Date.now(), tid, function() {
						db.sortedSetRemove('topics:solved', tid, next);
					});
				}
			}
		], function(err) {
			callback(err, {isSolved: !isSolved});
		});
	});
}

function toggleQuestionStatus(tid, callback) {
	topics.getTopicField(tid, 'isQuestion', function(err, isQuestion) {
		isQuestion = parseInt(isQuestion, 10) === 1;

		async.parallel([
			function(next) {
				topics.setTopicField(tid, 'isQuestion', isQuestion ? 0 : 1, next);
			},
			function(next) {
				if (!isQuestion) {
					async.parallel([
						function(next) {
							topics.setTopicField(tid, 'isSolved', 0, next);
						},
						function(next) {
							db.sortedSetAdd('topics:unsolved', Date.now(), tid, next);
						},
						function(next) {
							db.sortedSetRemove('topics:solved', tid, next);
						}
					], next);
				} else {
					db.sortedSetRemove('topics:unsolved', tid, function() {
						db.sortedSetRemove('topics:solved', tid, next);
					});
				}
			}
		], function(err) {
			callback(err, {isQuestion: !isQuestion});
		});
	});
}

function renderUnsolved(req, res, next) {
	var stop = (parseInt(meta.config.topicsPerList, 10) || 20) - 1;
	topics.getTopicsFromSet('topics:unsolved', req.uid, 0, stop, function(err, data) {
		if (err) {
			return next(err);
		}

		data['feeds:disableRSS'] = true;
		data.breadcrumbs = helpers.buildBreadcrumbs([{text: 'Unsolved'}]);
		res.render('recent', data);
	});
}

function renderSolved(req, res, next) {
	var stop = (parseInt(meta.config.topicsPerList, 10) || 20) - 1;
	topics.getTopicsFromSet('topics:solved', req.uid, 0, stop, function(err, data) {
		if (err) {
			return next(err);
		}

		data['feeds:disableRSS'] = true;
		data.breadcrumbs = helpers.buildBreadcrumbs([{text: 'Solved'}]);
		res.render('recent', data);
	});
}

module.exports = plugin;<|endoftext|>


<commit_before>(function(module) {
  "use strict";

  var	passport = module.parent.require('passport'),
  	passportLocal = module.parent.require('passport-local').Strategy,
  	User = module.parent.require('./user'),
    Groups = module.parent.require('./groups'),
    meta = module.parent.require('./meta'),
    db = module.parent.require('../src/database'),
    fs = module.parent.require('fs'),
    path = module.parent.require('path'),
    nconf = module.parent.require('nconf'),
    winston = module.parent.require('winston'),
    async = module.parent.require('async'),
    cookie = require('cookie-signature'),
    request = require('request'),
    constants = Object.freeze({
      name: 'contentblvd'
    }),
  	plugin = {};

  plugin.login = function() {
  	winston.info('[login] Registering new local login strategy');
  	passport.use(new passportLocal({passReqToCallback: true}, plugin.continueLogin));
  };

  plugin.continueLogin = function(req, username, password, next) {
      // Do your stuff here (query API or SQL db, etc...)

      //console.log(req.body);
      winston.info('[login] email is ' + req.query.email);
      winston.info('[login] username is ' + username);
      winston.info('[login] ready to start session login');

      var val = cookie.sign('1', 'ruttabegga');
      winston.info('signed is ' + val);
      var val_encoded = new Buffer(val).toString('base64');
      winston.info('encoded is ' + val_encoded);

      var val_decoded = new Buffer(username,'base64').toString('ascii');
      winston.info('decoded is ' + val_decoded);
      var externalID = cookie.unsign(val_decoded, 'ruttabegga');
      winston.info('unsigned is ' + externalID);

      request('https://app.contentblvd.com/v1/users/' + externalID, function (err, response, body) {
  			if (err) {
  				return next(null, null);
  			}
  			if (response.statusCode === 200) {
  				var data = JSON.parse(body);
  				console.log(data);
  				var profile = {};
          profile.id = data.id;
          profile.displayName = data.first_name + ' ' + data.last_name;
          profile.email = data.email;
          profile.picture = data.avatar;
          if (data.default_role == 'admin') {
            profile.isAdmin = 1;
          }
          plugin.moreLogin({
            CBid: profile.id,
            handle: profile.displayName,
            email: profile.email,
            isAdmin: profile.isAdmin,
            picture: profile.picture
          }, function(err, user) {
            if (err) {
              return next(err);
            }
            next(null, user);
          });
  			}
  			//next(null, user);
  		});

      //var uid = 1;

      //if (uid) {

      //  next(null, {
    	//	  uid: uid
    	//  }, '[[success:authentication-successful]]');

    	//} else{

      	// But if the login was unsuccessful, pass an error back, like so:
      //	next(new Error('[[error:invalid-username-or-password]]'));

      //}
  };

  plugin.getUidByCBid = function(CBid, callback) {
    db.getObjectField(constants.name + 'Id:uid', CBid, function(err, uid) {
      if (err) {
        return callback(err);
      }
      callback(null, uid);
    });
  };

  plugin.moreLogin = function(payload, callback) {
    if (payload) {
      winston.info('[payload]' + payload);
      plugin.getUidByCBid(payload.CBid, function(err, uid) {
        if (err) {
          return callback(err);
        }

        if (uid !== null) {
          // Existing User
          callback(null, {
            uid: uid
          });
        } else {
          // New User
          var success = function(uid) {
              // Save provider-specific information to the user
              User.setUserField(uid, constants.name + 'Id', payload.CBid);
              db.setObjectField(constants.name + 'Id:uid', payload.CBid, uid);

              if (payload.picture) {
                picture = payload.picture;
                if ( picture.match(/^http/i) ) {
                  // already has domain
                } else {
                  picture = 'https://d2m2amo0drgja.cloudfront.net/w400/' + picture;
                }
  							User.setUserField(uid, 'uploadedpicture', picture);
  							User.setUserField(uid, 'picture', picture);
              }

              if (payload.isAdmin) {
                Groups.join('administrators', uid, function(err) {
                  callback(null, {
                    uid: uid
                  });
                });
              } else {
                callback(null, {
                  uid: uid
                });
              }
              };

          User.getUidByEmail(payload.email, function(err, uid) {
            if (err) {
              return callback(err);
            }

            if (!uid) {
              User.create({
                username: payload.handle,
                email: payload.email
              }, function(err, uid) {
                if (err) {
                  return callback(err);
                }

                success(uid);
              });
            } else {
              success(uid); // Existing account -- merge
            }
          });
        }
      });
    } else {
      winston.error('[missing payload]');
      if (callback) {
        callback(new Error('[[error:missing-payload]]'));
      }
    }
  };

  module.exports = plugin;
}(module));<commit_msg>test another user with avatar<commit_after>(function(module) {
  "use strict";

  var	passport = module.parent.require('passport'),
  	passportLocal = module.parent.require('passport-local').Strategy,
  	User = module.parent.require('./user'),
    Groups = module.parent.require('./groups'),
    meta = module.parent.require('./meta'),
    db = module.parent.require('../src/database'),
    fs = module.parent.require('fs'),
    path = module.parent.require('path'),
    nconf = module.parent.require('nconf'),
    winston = module.parent.require('winston'),
    async = module.parent.require('async'),
    cookie = require('cookie-signature'),
    request = require('request'),
    constants = Object.freeze({
      name: 'contentblvd'
    }),
  	plugin = {};

  plugin.login = function() {
  	winston.info('[login] Registering new local login strategy');
  	passport.use(new passportLocal({passReqToCallback: true}, plugin.continueLogin));
  };

  plugin.continueLogin = function(req, username, password, next) {
      // Do your stuff here (query API or SQL db, etc...)

      //console.log(req.body);
      winston.info('[login] email is ' + req.query.email);
      winston.info('[login] username is ' + username);
      winston.info('[login] ready to start session login');

      var val = cookie.sign('3069', 'ruttabegga');
      winston.info('signed is ' + val);
      var val_encoded = new Buffer(val).toString('base64');
      winston.info('encoded is ' + val_encoded);

      var val_decoded = new Buffer(username,'base64').toString('ascii');
      winston.info('decoded is ' + val_decoded);
      var externalID = cookie.unsign(val_decoded, 'ruttabegga');
      winston.info('unsigned is ' + externalID);

      request('https://app.contentblvd.com/v1/users/' + externalID, function (err, response, body) {
  			if (err) {
  				return next(null, null);
  			}
  			if (response.statusCode === 200) {
  				var data = JSON.parse(body);
  				console.log(data);
  				var profile = {};
          profile.id = data.id;
          profile.displayName = data.first_name + ' ' + data.last_name;
          profile.email = data.email;
          profile.picture = data.avatar;
          if (data.default_role == 'admin') {
            profile.isAdmin = 1;
          }
          plugin.moreLogin({
            CBid: profile.id,
            handle: profile.displayName,
            email: profile.email,
            isAdmin: profile.isAdmin,
            picture: profile.picture
          }, function(err, user) {
            if (err) {
              return next(err);
            }
            next(null, user);
          });
  			}
  			//next(null, user);
  		});

      //var uid = 1;

      //if (uid) {

      //  next(null, {
    	//	  uid: uid
    	//  }, '[[success:authentication-successful]]');

    	//} else{

      	// But if the login was unsuccessful, pass an error back, like so:
      //	next(new Error('[[error:invalid-username-or-password]]'));

      //}
  };

  plugin.getUidByCBid = function(CBid, callback) {
    db.getObjectField(constants.name + 'Id:uid', CBid, function(err, uid) {
      if (err) {
        return callback(err);
      }
      callback(null, uid);
    });
  };

  plugin.moreLogin = function(payload, callback) {
    if (payload) {
      winston.info('[payload]' + payload);
      plugin.getUidByCBid(payload.CBid, function(err, uid) {
        if (err) {
          return callback(err);
        }

        if (uid !== null) {
          // Existing User
          callback(null, {
            uid: uid
          });
        } else {
          // New User
          var success = function(uid) {
              // Save provider-specific information to the user
              User.setUserField(uid, constants.name + 'Id', payload.CBid);
              db.setObjectField(constants.name + 'Id:uid', payload.CBid, uid);

              if (payload.picture) {
                picture = payload.picture;
                if ( picture.match(/^http/i) ) {
                  // already has domain
                } else {
                  picture = 'https://d2m2amo0drgja.cloudfront.net/w400/' + picture;
                }
  							User.setUserField(uid, 'uploadedpicture', picture);
  							User.setUserField(uid, 'picture', picture);
              }

              if (payload.isAdmin) {
                Groups.join('administrators', uid, function(err) {
                  callback(null, {
                    uid: uid
                  });
                });
              } else {
                callback(null, {
                  uid: uid
                });
              }
              };

          User.getUidByEmail(payload.email, function(err, uid) {
            if (err) {
              return callback(err);
            }

            if (!uid) {
              User.create({
                username: payload.handle,
                email: payload.email
              }, function(err, uid) {
                if (err) {
                  return callback(err);
                }

                success(uid);
              });
            } else {
              success(uid); // Existing account -- merge
            }
          });
        }
      });
    } else {
      winston.error('[missing payload]');
      if (callback) {
        callback(new Error('[[error:missing-payload]]'));
      }
    }
  };

  module.exports = plugin;
}(module));<|endoftext|>


<commit_before>var	async = require('async'),
XRegExp = require('xregexp').XRegExp,

nconf = module.parent.require('nconf'),
Topics = module.parent.require('./topics'),
User = module.parent.require('./user'),
Notifications = module.parent.require('./notifications'),
Utils = module.parent.require('../public/src/utils'),
websockets = module.parent.require('./socket.io'),
ModulesSockets = module.parent.require('./socket.io/modules');

var regex = XRegExp('(@[\\p{L}\\d\\-_]+)', 'g'),
	isLatinMention = /@[\w]+$/;
Mentions = {};

Mentions.notify = function(postData) {
	var	_self = this,
		cleanedContent = postData.content.replace(/
[\s\S]+?<\/blockquote>/g, ''),	// Removing blockquoted content from the checked string
		matches = postData.content.match(regex);

	if (matches) {
		// Eliminate duplicates
		matches = matches.filter(function(cur, idx) {
			return idx === matches.indexOf(cur);
		});

		async.filter(matches, function(match, next) {
			var	slug = Utils.slugify(match.slice(1));
			User.exists(slug, function(err, exists) {
				next(exists);
			});
		}, function(matches) {
			async.parallel({
				title: function(next) {
					Topics.getTopicField(postData.tid, 'title', next);
				},
				author: function(next) {
					User.getUserField(postData.uid, 'username', next);
				},
				uids: function(next) {
					async.map(matches, function(match, next) {
						var	slug = Utils.slugify(match.slice(1));
						User.getUidByUserslug(slug, next);
					}, next);
				}
			}, function(err, results) {
				var	recipients = results.uids.filter(function(uid) {
						return parseInt(uid, 10) !== postData.uid;
					});

				if (!err && recipients.length > 0) {
					Notifications.create('' + results.author + ' mentioned you in "' + results.title + '"', '/topic/' + postData.tid, 'topic:' + postData.tid, function(nid) {
						Notifications.push(nid, recipients);
					});
				}
			});
		});
	}
};

Mentions.addMentions = function(postContent, callback) {
	var	_self = this,
	relativeUrl = nconf.get('relative_url') || '',
	matches = postContent.match(regex);

	if (matches) {
		// Eliminate duplicates
		matches = matches.filter(function(cur, idx) {
			return idx === matches.indexOf(cur);
		});

		async.each(matches, function(match, next) {
			var userslug = Utils.slugify(match.slice(1));
			User.getUidByUserslug(userslug, function(err, uid) {
				if(uid) {
					if (isLatinMention.test(match)) {
						postContent = postContent.replace(new RegExp(match + '\\b', 'g'), ' ' + match + '');
					} else {
						postContent = postContent.replace(new RegExp(match, 'g'), ' ' + match + '');
					}
				}
				next();
			});
		}, function(err) {
			callback(null, postContent);
		});
	} else callback(null, postContent);
};

Mentions.addScripts = function(scripts) {
	return scripts.concat([
		'plugins/nodebb-plugin-mentions/autofill.js',
		'plugins/nodebb-plugin-mentions/jquery.textcomplete.js'
	]);
}

Mentions.addSockets = function() {
	ModulesSockets.composer.autofill = Mentions.sockets.autofill;
}

Mentions.sockets = {
	'autofill': function(socket, data, callback) {
		Mentions.autoFill(data, callback);
	}
}

Mentions.autoFill = function (data, callback) {
	User.search(data.term, function(err, userdata) {
		if (err) {
			return callback(null, []);
		}

		callback(null, userdata.map(function(user) {
			return user.username;
		}));
	});
};

module.exports = Mentions;
<commit_msg>fixed issue where User.search in NodeBB core was returning an object instead of an array - #862<commit_after>var	async = require('async'),
XRegExp = require('xregexp').XRegExp,

nconf = module.parent.require('nconf'),
Topics = module.parent.require('./topics'),
User = module.parent.require('./user'),
Notifications = module.parent.require('./notifications'),
Utils = module.parent.require('../public/src/utils'),
websockets = module.parent.require('./socket.io'),
ModulesSockets = module.parent.require('./socket.io/modules');

var regex = XRegExp('(@[\\p{L}\\d\\-_]+)', 'g'),
	isLatinMention = /@[\w]+$/;
Mentions = {};

Mentions.notify = function(postData) {
	var	_self = this,
		cleanedContent = postData.content.replace(/
[\s\S]+?<\/blockquote>/g, ''),	// Removing blockquoted content from the checked string
		matches = postData.content.match(regex);

	if (matches) {
		// Eliminate duplicates
		matches = matches.filter(function(cur, idx) {
			return idx === matches.indexOf(cur);
		});

		async.filter(matches, function(match, next) {
			var	slug = Utils.slugify(match.slice(1));
			User.exists(slug, function(err, exists) {
				next(exists);
			});
		}, function(matches) {
			async.parallel({
				title: function(next) {
					Topics.getTopicField(postData.tid, 'title', next);
				},
				author: function(next) {
					User.getUserField(postData.uid, 'username', next);
				},
				uids: function(next) {
					async.map(matches, function(match, next) {
						var	slug = Utils.slugify(match.slice(1));
						User.getUidByUserslug(slug, next);
					}, next);
				}
			}, function(err, results) {
				var	recipients = results.uids.filter(function(uid) {
						return parseInt(uid, 10) !== postData.uid;
					});

				if (!err && recipients.length > 0) {
					Notifications.create('' + results.author + ' mentioned you in "' + results.title + '"', '/topic/' + postData.tid, 'topic:' + postData.tid, function(nid) {
						Notifications.push(nid, recipients);
					});
				}
			});
		});
	}
};

Mentions.addMentions = function(postContent, callback) {
	var	_self = this,
	relativeUrl = nconf.get('relative_url') || '',
	matches = postContent.match(regex);

	if (matches) {
		// Eliminate duplicates
		matches = matches.filter(function(cur, idx) {
			return idx === matches.indexOf(cur);
		});

		async.each(matches, function(match, next) {
			var userslug = Utils.slugify(match.slice(1));
			User.getUidByUserslug(userslug, function(err, uid) {
				if(uid) {
					if (isLatinMention.test(match)) {
						postContent = postContent.replace(new RegExp(match + '\\b', 'g'), ' ' + match + '');
					} else {
						postContent = postContent.replace(new RegExp(match, 'g'), ' ' + match + '');
					}
				}
				next();
			});
		}, function(err) {
			callback(null, postContent);
		});
	} else callback(null, postContent);
};

Mentions.addScripts = function(scripts) {
	return scripts.concat([
		'plugins/nodebb-plugin-mentions/autofill.js',
		'plugins/nodebb-plugin-mentions/jquery.textcomplete.js'
	]);
}

Mentions.addSockets = function() {
	ModulesSockets.composer.autofill = Mentions.sockets.autofill;
}

Mentions.sockets = {
	'autofill': function(socket, data, callback) {
		Mentions.autoFill(data, callback);
	}
}

Mentions.autoFill = function (data, callback) {
	User.search(data.term, function(err, data) {
		userdata = data.users;

		if (err) {
			return callback(null, []);
		}

		callback(null, userdata.map(function(user) {
			return user.username;
		}));
	});
};

module.exports = Mentions;
<|endoftext|>


<commit_before>window.onload = function() {
	var canvas = document.getElementById('canvas');
	var ctx = canvas.getContext('2d');
	var canvasWidth = canvas.width;
	var canvasHeight = canvas.height;
	var game_loop;
	var cellSize = 40; //determines cell size. cell is always a square.
	var boardSize = 8; //determines board size. at the moment the board is always square.
	//var board = [];
	var board = [
	[1, 2, 0, 1, 2, 2, 2, 0],
	[2, 0, 0, 2, 1, 2, 2, 0],
	[1, 0, 1, 0, 2, 2, 1, 2],
	[1, 2, 1, 1, 0, 0, 2, 1],
	[1, 2, 2, 1, 2, 2, 0, 0],
	[0, 0, 1, 2, 2, 2, 1, 0],
	[1, 2, 0, 0, 2, 2, 2, 0],
	[0, 1, 0, 0, 1, 1, 2, 2]
	];
	var score;

	function generateJewel() {
		return Math.floor(Math.random() * 3);
	}

	/* Start Game */
	function init() {
		/*for(var y = 0; y < boardSize; ++y) {
			var temp = [];
			for(var x = 0; x < boardSize; ++x) {
				temp.push(generateJewel());
			}
			board.push(temp);
		}*/
		console.log(board);
		score = 0;
		if(typeof game_loop !== 'undefined') clearInterval(game_loop);
		game_loop = setInterval(draw, 60);
	}

	init();

	function clearMatchesXAxis(startX, startY, length, boardClearMarked) {
		//console.log(startX, startY, length);
		for(var i = startX; i < startX + length; ++i) {
			boardClearMarked[startY][i] = 1;
		}
		return boardClearMarked;
	}

	function clearMatchesYAxis(startX, startY, length, boardClearMarked) {
		//console.log(startX, startY, length);
		for(var i = startY; i < startY + length; ++i) {
			boardClearMarked[i][startX] = 1;
		}
		return boardClearMarked;
	}

	function match() {
		var count = 1;
		var prevVal = -1;
		var matchFound = 0;

		//initialize boardClearMarked which tells game which tiles should be deleted
		var boardClearMarked = [];
		for(var y = 0; y < boardSize; ++y) {
			var temp = [];
			for(var x = 0; x < boardSize; ++x) {
				temp.push(0);
			}
			boardClearMarked.push(temp);
		}

		//check matches in each row on x-axis
		for(var y = 0; y < boardSize; ++y) {
			for(var x = 0; x < boardSize; ++x) {
				var currVal = board[y][x];
				if(currVal === prevVal) {
					++count;
				} else {
					if(count >= 3) {
						//console.log('x:' + x + ', ' + 'y:' + x + ', ' + 'count:' + count);
						matchFound = 1;
						boardClearMarked = clearMatchesXAxis(x - count, y, count, boardClearMarked);
					}
					count = 1;
				}
				prevVal = currVal;
			}
			if(count >= 3) {
				//console.log('x:' + i + ', ' + 'y:' + x + ', ' + 'count:' + count);
				matchFound = 1;
				boardClearMarked = clearMatchesXAxis(boardSize - count, y, count, boardClearMarked);
			}
			count = 1;
			prevVal = -1;
		}

		//check matches in each row on y-axis
		for(var x = 0; x < boardSize; ++x) {
			for(var y = 0; y < boardSize; ++y) {
				var currVal = board[y][x];
				if(currVal === prevVal) {
					++count;
				} else {
					if(count >= 3) {
						matchFound = 1;
						boardClearMarked = clearMatchesYAxis(x, y - count, count, boardClearMarked);
					}
					count = 1;
				}
				prevVal = currVal;
			}
			if(count >= 3) {
				matchFound = 1;
				boardClearMarked = clearMatchesYAxis(x, boardSize - count, count, boardClearMarked);
			}
			count = 1;
			prevVal = -1;
		}

		//delete matched tilesets from board
		for(var y = 0; y < boardSize; ++y) {
			for(var x = 0; x < boardSize; ++x) {
				if(boardClearMarked[y][x] === 1) {
					board[y][x] = 3;
				}
			}
		}

		return matchFound;
	}

	function jewelSlideDown() {
		for(var x = 0; x < boardSize; ++x) {
			var emptyCells = [];
			for(var y = boardSize - 1; y >= 0; --y) {
				if(board[y][x] === 3) {
					emptyCells.unshift({'x': x, 'y': y});
				} else if(emptyCells.length) {
					var deepestEmptyCell = emptyCells.pop();
					board[deepestEmptyCell.y][deepestEmptyCell.x] = board[y][x];
					board[y][x] = 3;
					emptyCells.unshift({'x': x, 'y': y});
				}
			}
			emptyCells = [];
		}
	}

	function drawJewel(x, y, val) {
		if(val === 0) {
			ctx.fillStyle = '#ff0000';
		} else if(val === 1) {
			ctx.fillStyle = '#00ff00';
		} else if(val === 2){
			ctx.fillStyle = '#0000ff';
		} else if(val === 4) {
			ctx.fillStyle = '#fff';
		} else {
			ctx.fillStyle = '#000';
		}
		ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
		ctx.strokeStyle = '#000';
		ctx.strokeRect(x * cellSize, y * cellSize, cellSize, cellSize);
	}

	function fillGaps() {
		for(var y = 0; y < board.length; ++y) {
			for(var x = 0; x < board.length; ++x) {
				if(board[y][x] === 3) {
					board[y][x] = generateJewel();
				}
			}
		}
	}

	function draw() {
		//draw the background to clear previous frame
		ctx.fillStyle = '#fff';
		ctx.fillRect(0, 0, canvasWidth, canvasHeight);

		for(var y = 0; y < boardSize; ++y) {
			for(var x = 0; x < boardSize; ++x) {
				drawJewel(x, y, board[y][x]);
			}
		}

		var score_text = 'Score: ' + score;
		ctx.fillStyle = '#000';
		ctx.fillText(score_text, canvasWidth - 155, 15);
	}

	var matchFound = match();
	jewelSlideDown();
	//fillGaps();
	console.log(matchFound);

	/* Input */
	window.onmousedown = function(evt) {
		var key = evt.which;
		//console.log(key);
	}
};<commit_msg>Got initial random generation working<commit_after>window.onload = function() {
	var canvas = document.getElementById('canvas');
	var ctx = canvas.getContext('2d');
	var canvasWidth = canvas.width;
	var canvasHeight = canvas.height;
	var game_loop;
	var cellSize = 40; //determines cell size. cell is always a square.
	var boardSize = 8; //determines board size. at the moment the board is always square.
	//var board = [];
	var board = [
	[1, 2, 0, 1, 2, 2, 2, 0],
	[2, 0, 0, 2, 1, 2, 2, 0],
	[1, 0, 1, 0, 2, 2, 1, 2],
	[1, 2, 1, 1, 0, 0, 2, 1],
	[1, 2, 2, 1, 2, 2, 0, 0],
	[0, 0, 1, 2, 2, 2, 1, 0],
	[1, 2, 0, 0, 2, 2, 2, 0],
	[0, 1, 0, 0, 1, 1, 2, 2]
	];
	var score;

	function generateJewel() {
		return Math.floor(Math.random() * 3);
	}

	/* Start Game */
	function init() {
		//initialize board with randomly generated jewels
		/*for(var y = 0; y < boardSize; ++y) {
			var temp = [];
			for(var x = 0; x < boardSize; ++x) {
				temp.push(generateJewel());
			}
			board.push(temp);
		}*/

		console.log(board);
		score = 0;
		if(typeof game_loop !== 'undefined') clearInterval(game_loop);
		game_loop = setInterval(draw, 60);
	}

	init();

	function clearMatchesXAxis(startX, startY, length, boardClearMarked) {
		//console.log(startX, startY, length);
		for(var i = startX; i < startX + length; ++i) {
			boardClearMarked[startY][i] = 1;
		}
		return boardClearMarked;
	}

	function clearMatchesYAxis(startX, startY, length, boardClearMarked) {
		//console.log(startX, startY, length);
		for(var i = startY; i < startY + length; ++i) {
			boardClearMarked[i][startX] = 1;
		}
		return boardClearMarked;
	}

	function match() {
		var count = 1;
		var prevVal = -1;
		var matchFound = 0;

		//initialize boardClearMarked which tells game which tiles should be deleted
		var boardClearMarked = [];
		for(var y = 0; y < boardSize; ++y) {
			var temp = [];
			for(var x = 0; x < boardSize; ++x) {
				temp.push(0);
			}
			boardClearMarked.push(temp);
		}

		//check matches in each row on x-axis
		for(var y = 0; y < boardSize; ++y) {
			for(var x = 0; x < boardSize; ++x) {
				var currVal = board[y][x];
				if((currVal === prevVal) && (currVal != 3)) {
					++count;
				} else {
					if(count >= 3) {
						//console.log('x:' + x + ', ' + 'y:' + x + ', ' + 'count:' + count);
						matchFound = 1;
						boardClearMarked = clearMatchesXAxis(x - count, y, count, boardClearMarked);
					}
					count = 1;
				}
				prevVal = currVal;
			}
			if(count >= 3) {
				//console.log('x:' + i + ', ' + 'y:' + x + ', ' + 'count:' + count);
				matchFound = 1;
				boardClearMarked = clearMatchesXAxis(boardSize - count, y, count, boardClearMarked);
			}
			count = 1;
			prevVal = -1;
		}

		//check matches in each row on y-axis
		for(var x = 0; x < boardSize; ++x) {
			for(var y = 0; y < boardSize; ++y) {
				var currVal = board[y][x];
				if((currVal === prevVal) && (currVal != 3)) {
					++count;
				} else {
					if(count >= 3) {
						matchFound = 1;
						boardClearMarked = clearMatchesYAxis(x, y - count, count, boardClearMarked);
					}
					count = 1;
				}
				prevVal = currVal;
			}
			if(count >= 3) {
				matchFound = 1;
				boardClearMarked = clearMatchesYAxis(x, boardSize - count, count, boardClearMarked);
			}
			count = 1;
			prevVal = -1;
		}

		//delete matched tilesets from board
		for(var y = 0; y < boardSize; ++y) {
			for(var x = 0; x < boardSize; ++x) {
				if(boardClearMarked[y][x] === 1) {
					board[y][x] = 3;
				}
			}
		}

		return matchFound;
	}

	function jewelSlideDown() {
		for(var x = 0; x < boardSize; ++x) {
			var emptyCells = [];
			for(var y = boardSize - 1; y >= 0; --y) {
				if(board[y][x] === 3) {
					emptyCells.unshift({'x': x, 'y': y});
				} else if(emptyCells.length) {
					var deepestEmptyCell = emptyCells.pop();
					board[deepestEmptyCell.y][deepestEmptyCell.x] = board[y][x];
					board[y][x] = 3;
					emptyCells.unshift({'x': x, 'y': y});
				}
			}
			emptyCells = [];
		}
	}

	function drawJewel(x, y, val) {
		if(val === 0) {
			ctx.fillStyle = '#ff0000';
		} else if(val === 1) {
			ctx.fillStyle = '#00ff00';
		} else if(val === 2){
			ctx.fillStyle = '#0000ff';
		} else if(val === 4) {
			ctx.fillStyle = '#fff';
		} else {
			ctx.fillStyle = '#000';
		}
		ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
		ctx.strokeStyle = '#000';
		ctx.strokeRect(x * cellSize, y * cellSize, cellSize, cellSize);
	}

	function fillGaps() {
		for(var y = 0; y < board.length; ++y) {
			for(var x = 0; x < board.length; ++x) {
				if(board[y][x] === 3) {
					board[y][x] = generateJewel();
				}
			}
		}
	}

	function draw() {
		//draw the background to clear previous frame
		ctx.fillStyle = '#fff';
		ctx.fillRect(0, 0, canvasWidth, canvasHeight);

		for(var y = 0; y < boardSize; ++y) {
			for(var x = 0; x < boardSize; ++x) {
				drawJewel(x, y, board[y][x]);
			}
		}

		var score_text = 'Score: ' + score;
		ctx.fillStyle = '#000';
		ctx.fillText(score_text, canvasWidth - 155, 15);
	}

	var matchFound = -1;
	while(matchFound = match()){
		jewelSlideDown();
		fillGaps();
	}
	console.log(matchFound);

	/* Input */
	window.onmousedown = function(evt) {
		var key = evt.which;
		//console.log(key);
	}
};<|endoftext|>