<commit_before>(function(Editor, markdownit, WebUploader){

    function _replaceSelection(cm, active, start, end) {
        var text;
        var startPoint = cm.getCursor('start');
        var endPoint = cm.getCursor('end');
        var end = end || '';
        if (active) {
            text = cm.getLine(startPoint.line);
            start = text.slice(0, startPoint.ch);
            end = text.slice(startPoint.ch);
            cm.setLine(startPoint.line, start + end);
        } else {
            text = cm.getSelection();
            cm.replaceSelection(start + text + end);

            startPoint.ch += start.length;
            endPoint.ch += start.length;
        }
        cm.setSelection(startPoint, endPoint);
        cm.focus();
    }

    /**
     * The state of CodeMirror at the given position.
     */
    function getState(cm, pos) {
        pos = pos || cm.getCursor('start');
        var stat = cm.getTokenAt(pos);
        if (!stat.type) return {};

        var types = stat.type.split(' ');

        var ret = {}, data, text;
        for (var i = 0; i < types.length; i++) {
            data = types[i];
            if (data === 'strong') {
            ret.bold = true;
            } else if (data === 'variable-2') {
            text = cm.getLine(pos.line);
            if (/^\s*\d+\.\s/.test(text)) {
                ret['ordered-list'] = true;
            } else {
                ret['unordered-list'] = true;
            }
            } else if (data === 'atom') {
            ret.quote = true;
            } else if (data === 'em') {
            ret.italic = true;
            }
        }
        return ret;
    }

    // Set default options
    var md = new markdownit();

    md.set({
      html:         false,        // Enable HTML tags in source
      xhtmlOut:     false,        // Use '/' to close single tags (
)
      breaks:       true,        // Convert '\n' in paragraphs into

      langPrefix:   'language-',  // CSS language prefix for fenced blocks
      linkify:      false,        // Autoconvert URL-like text to links
      typographer:  false,        // Enable smartypants and other sweet transforms
    });

    window.markdowniter = md;

    var toolbar = Editor.toolbar;

    var replaceTool = function(name, callback){
        for(var i=0, len=toolbar.length; i<len; i++){
            var v = toolbar[i];
            if(typeof(v) !== 'string' && v.name === name){
                v.action = callback;
                break;
            }
        }
    };

    var $body = $('body');

    //添加连接工具
    var ToolLink = function(){
        var self = this;
        this.$win = $([
            '
',
                '
',
                    '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>',
                    '
添加连接
',
                '
',
                '
',
                    '<form class="form-horizontal">',
                        '
',
                            '标题',
                            '
',
                                '<input type="text" name="title" placeholder="Title">',
                            '
',
                        '
',
                        '
',
                            '连接',
                            '
',
                                '<input type="text" name="link" value="http://" placeholder="Link">',
                            '
',
                        '
',
                    '</form>',
                '
',
                '
',
                    '<button class="btn btn-primary" role="save">确定</button>',
                '
',
            '
'
        ].join('')).appendTo($body);

        this.$win.on('click', '[role=save]', function(){
            self.$win.find('form').submit();
        }).on('submit', 'form', function(){
            var $el = $(this);
            var title = $el.find('[name=title]').val();
            var link = $el.find('[name=link]').val();

            self.$win.modal('hide');

            var cm = self.editor.codemirror;
            var stat = getState(cm);
            _replaceSelection(cm, stat.link, '!['+ title +']('+ link +')');

            $el.find('[name=title]').val('');
            $el.find('[name=link]').val('http://');

            return false;
        });
    };

    ToolLink.prototype.bind = function(editor){
        this.editor = editor;
        this.$win.modal('show');
    };

    //图片上传工具
    var ToolImage = function(){
        var self = this;
        this.$win = $([
            '
',
                '
',
                    '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>',
                    '
图片
',
                '
',
                '
',
                    '
',
                        '
上传图片
',
                        '',
                        '

',
                    '
',
                '
',
            '
'
        ].join('')).appendTo($body);

        this.$upload = this.$win.find('.upload-img').css({
            height: 50,
            padding: '60px 0',
            textAlign: 'center',
            border: '4px dashed#ddd'
        });

        this.$uploadBtn = this.$upload.find('.button').css({
            width: 86,
            height: 40,
            margin: '0 auto'
        });

        this.$uploadTip = this.$upload.find('.tip').hide();

        this.file = false;
        var _csrf = $('[name=_csrf]').val();

        this.uploader = WebUploader.create({
            swf: '/public/libs/webuploader/Uploader.swf',
            server: '/upload?_csrf=' + _csrf,
            pick: this.$uploadBtn[0],
            paste: document.body,
            dnd: this.$upload[0],
            auto: true,
            fileSingleSizeLimit: 2 * 1024 * 1024,
            //sendAsBinary: true,
            // 只允许选择图片文件。
            accept: {
                title: 'Images',
                extensions: 'gif,jpg,jpeg,bmp,png',
                mimeTypes: 'image/*'
            }
        });

        this.uploader.on('beforeFileQueued', function(file){
            if(self.file !== false || !self.editor){
                return false;
            }
            self.showFile(file);
        });

        this.uploader.on('uploadProgress', function(file, percentage){
            // console.log(percentage);
            self.showProgress(file, percentage * 100);
        });

        this.uploader.on('uploadSuccess', function(file, res){
            if(res.success){
                self.$win.modal('hide');

                var cm = self.editor.codemirror;
                var stat = getState(cm);
                _replaceSelection(cm, stat.image, '!['+ file.name +']('+ res.url +')');

            }
            else{
                self.removeFile();
                self.showError(res.msg || '服务器走神了，上传失败');
            }
        });

        this.uploader.on('uploadComplete', function(file){
            self.uploader.removeFile(file);
            self.removeFile();
        });

        this.uploader.on('error', function(type){
            self.removeFile();
            switch(type){
                case 'Q_EXCEED_SIZE_LIMIT':
                case 'F_EXCEED_SIZE':
                    self.showError('文件太大了, 不能超过2M');
                    break;
                case 'Q_TYPE_DENIED':
                    self.showError('只能上传图片');
                    break;
                default:
                    self.showError('发生未知错误');
            }
        });

        this.uploader.on('uploadError', function(){
            self.removeFile();
            self.showError('服务器走神了，上传失败');
        });
    };

    ToolImage.prototype.removeFile = function(){
        //var self = this;
        this.file = false;
        this.$uploadBtn.show();
        this.$uploadTip.hide();
    };

    ToolImage.prototype.showFile = function(file){
        //var self = this;
        this.file = file;
        this.$uploadBtn.hide();
        this.$uploadTip.html('正在上传: ' + file.name).show();
        this.hideError();
    };

    ToolImage.prototype.showError = function(error){
        this.$upload.find('.alert-error').html(error).show();
    };

    ToolImage.prototype.hideError = function(error){
        this.$upload.find('.alert-error').hide();
    };

    ToolImage.prototype.showProgress = function(file, percentage){
        this.$uploadTip
            .html('正在上传: ' + file.name + ' ' + percentage + '%')
            .show();
    };

    ToolImage.prototype.bind = function(editor){
        this.editor = editor;
        this.$win.modal('show');
    };

    var toolImage = new ToolImage();
    var toolLink = new ToolLink();

    replaceTool('image', function(editor){
        toolImage.bind(editor);
    });
    replaceTool('link', function(editor){
        toolLink.bind(editor);
    });

    //当编辑器取得焦点时，绑定 toolImage；
    var createToolbar = Editor.prototype.createToolbar;
    Editor.prototype.createToolbar = function(items){
        createToolbar.call(this, items);
        var self = this;
        $(self.codemirror.display.input).on('focus', function(){
            toolImage.editor = self;
        });
    };

    //追加内容
    Editor.prototype.push = function(txt){
        var cm = this.codemirror;
        var line = cm.lastLine();
        cm.setLine(line, cm.getLine(line) + txt);
    };
})(window.Editor, window.markdownit, window.WebUploader);
<commit_msg>修复插入链接时多了 ! 符号的问题<commit_after>(function(Editor, markdownit, WebUploader){

    function _replaceSelection(cm, active, start, end) {
        var text;
        var startPoint = cm.getCursor('start');
        var endPoint = cm.getCursor('end');
        var end = end || '';
        if (active) {
            text = cm.getLine(startPoint.line);
            start = text.slice(0, startPoint.ch);
            end = text.slice(startPoint.ch);
            cm.setLine(startPoint.line, start + end);
        } else {
            text = cm.getSelection();
            cm.replaceSelection(start + text + end);

            startPoint.ch += start.length;
            endPoint.ch += start.length;
        }
        cm.setSelection(startPoint, endPoint);
        cm.focus();
    }

    /**
     * The state of CodeMirror at the given position.
     */
    function getState(cm, pos) {
        pos = pos || cm.getCursor('start');
        var stat = cm.getTokenAt(pos);
        if (!stat.type) return {};

        var types = stat.type.split(' ');

        var ret = {}, data, text;
        for (var i = 0; i < types.length; i++) {
            data = types[i];
            if (data === 'strong') {
            ret.bold = true;
            } else if (data === 'variable-2') {
            text = cm.getLine(pos.line);
            if (/^\s*\d+\.\s/.test(text)) {
                ret['ordered-list'] = true;
            } else {
                ret['unordered-list'] = true;
            }
            } else if (data === 'atom') {
            ret.quote = true;
            } else if (data === 'em') {
            ret.italic = true;
            }
        }
        return ret;
    }

    // Set default options
    var md = new markdownit();

    md.set({
      html:         false,        // Enable HTML tags in source
      xhtmlOut:     false,        // Use '/' to close single tags (
)
      breaks:       true,        // Convert '\n' in paragraphs into

      langPrefix:   'language-',  // CSS language prefix for fenced blocks
      linkify:      false,        // Autoconvert URL-like text to links
      typographer:  false,        // Enable smartypants and other sweet transforms
    });

    window.markdowniter = md;

    var toolbar = Editor.toolbar;

    var replaceTool = function(name, callback){
        for(var i=0, len=toolbar.length; i<len; i++){
            var v = toolbar[i];
            if(typeof(v) !== 'string' && v.name === name){
                v.action = callback;
                break;
            }
        }
    };

    var $body = $('body');

    //添加链接工具
    var ToolLink = function(){
        var self = this;
        this.$win = $([
            '
',
                '
',
                    '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>',
                    '
添加链接
',
                '
',
                '
',
                    '<form class="form-horizontal">',
                        '
',
                            '标题',
                            '
',
                                '<input type="text" name="title" placeholder="Title">',
                            '
',
                        '
',
                        '
',
                            '链接',
                            '
',
                                '<input type="text" name="link" value="http://" placeholder="Link">',
                            '
',
                        '
',
                    '</form>',
                '
',
                '
',
                    '<button class="btn btn-primary" role="save">确定</button>',
                '
',
            '
'
        ].join('')).appendTo($body);

        this.$win.on('click', '[role=save]', function(){
            self.$win.find('form').submit();
        }).on('submit', 'form', function(){
            var $el = $(this);
            var title = $el.find('[name=title]').val();
            var link = $el.find('[name=link]').val();

            self.$win.modal('hide');

            var cm = self.editor.codemirror;
            var stat = getState(cm);
            _replaceSelection(cm, stat.link, '['+ title +']('+ link +')');

            $el.find('[name=title]').val('');
            $el.find('[name=link]').val('http://');

            return false;
        });
    };

    ToolLink.prototype.bind = function(editor){
        this.editor = editor;
        this.$win.modal('show');
    };

    //图片上传工具
    var ToolImage = function(){
        var self = this;
        this.$win = $([
            '
',
                '
',
                    '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>',
                    '
图片
',
                '
',
                '
',
                    '
',
                        '
上传图片
',
                        '',
                        '

',
                    '
',
                '
',
            '
'
        ].join('')).appendTo($body);

        this.$upload = this.$win.find('.upload-img').css({
            height: 50,
            padding: '60px 0',
            textAlign: 'center',
            border: '4px dashed#ddd'
        });

        this.$uploadBtn = this.$upload.find('.button').css({
            width: 86,
            height: 40,
            margin: '0 auto'
        });

        this.$uploadTip = this.$upload.find('.tip').hide();

        this.file = false;
        var _csrf = $('[name=_csrf]').val();

        this.uploader = WebUploader.create({
            swf: '/public/libs/webuploader/Uploader.swf',
            server: '/upload?_csrf=' + _csrf,
            pick: this.$uploadBtn[0],
            paste: document.body,
            dnd: this.$upload[0],
            auto: true,
            fileSingleSizeLimit: 2 * 1024 * 1024,
            //sendAsBinary: true,
            // 只允许选择图片文件。
            accept: {
                title: 'Images',
                extensions: 'gif,jpg,jpeg,bmp,png',
                mimeTypes: 'image/*'
            }
        });

        this.uploader.on('beforeFileQueued', function(file){
            if(self.file !== false || !self.editor){
                return false;
            }
            self.showFile(file);
        });

        this.uploader.on('uploadProgress', function(file, percentage){
            // console.log(percentage);
            self.showProgress(file, percentage * 100);
        });

        this.uploader.on('uploadSuccess', function(file, res){
            if(res.success){
                self.$win.modal('hide');

                var cm = self.editor.codemirror;
                var stat = getState(cm);
                _replaceSelection(cm, stat.image, '!['+ file.name +']('+ res.url +')');

            }
            else{
                self.removeFile();
                self.showError(res.msg || '服务器走神了，上传失败');
            }
        });

        this.uploader.on('uploadComplete', function(file){
            self.uploader.removeFile(file);
            self.removeFile();
        });

        this.uploader.on('error', function(type){
            self.removeFile();
            switch(type){
                case 'Q_EXCEED_SIZE_LIMIT':
                case 'F_EXCEED_SIZE':
                    self.showError('文件太大了, 不能超过2M');
                    break;
                case 'Q_TYPE_DENIED':
                    self.showError('只能上传图片');
                    break;
                default:
                    self.showError('发生未知错误');
            }
        });

        this.uploader.on('uploadError', function(){
            self.removeFile();
            self.showError('服务器走神了，上传失败');
        });
    };

    ToolImage.prototype.removeFile = function(){
        //var self = this;
        this.file = false;
        this.$uploadBtn.show();
        this.$uploadTip.hide();
    };

    ToolImage.prototype.showFile = function(file){
        //var self = this;
        this.file = file;
        this.$uploadBtn.hide();
        this.$uploadTip.html('正在上传: ' + file.name).show();
        this.hideError();
    };

    ToolImage.prototype.showError = function(error){
        this.$upload.find('.alert-error').html(error).show();
    };

    ToolImage.prototype.hideError = function(error){
        this.$upload.find('.alert-error').hide();
    };

    ToolImage.prototype.showProgress = function(file, percentage){
        this.$uploadTip
            .html('正在上传: ' + file.name + ' ' + percentage + '%')
            .show();
    };

    ToolImage.prototype.bind = function(editor){
        this.editor = editor;
        this.$win.modal('show');
    };

    var toolImage = new ToolImage();
    var toolLink = new ToolLink();

    replaceTool('image', function(editor){
        toolImage.bind(editor);
    });
    replaceTool('link', function(editor){
        toolLink.bind(editor);
    });

    //当编辑器取得焦点时，绑定 toolImage；
    var createToolbar = Editor.prototype.createToolbar;
    Editor.prototype.createToolbar = function(items){
        createToolbar.call(this, items);
        var self = this;
        $(self.codemirror.display.input).on('focus', function(){
            toolImage.editor = self;
        });
    };

    //追加内容
    Editor.prototype.push = function(txt){
        var cm = this.codemirror;
        var line = cm.lastLine();
        cm.setLine(line, cm.getLine(line) + txt);
    };
})(window.Editor, window.markdownit, window.WebUploader);
<|endoftext|>


<commit_before>/**
 * Dispatch a stanza to a controller action
 * @param {type} $event
 * @param {type} controllerLoader contains information about all controllers and actions
 * controllerLoader is not a service in IoC controller, it is created by start/servers.js
 */

var Input = require("../lib/Input.js");

var Response = require("../lib/Response.js");

var Q = require("q");

module.exports = function (userService, sessionService, $event, $logger, controllerLoader, $invokeFx) {

    var route = new (require("../lib/route.js"))();
    require("../start/routes.js")(route);

    //verify actionPath in routing rules
    var routingRules = route.getAllRules();
    routingRules.forEach(function (routingRule) {
        var actionFx = controllerLoader.getAction(routingRule.actionPath);
        if (actionFx == null) {
            throw new Error("Action path not found, re-check following routing rule: " + JSON.stringify({
                action: routingRule.action,
                actionPath: routingRule.actionPath
            }));
        }
    });

    $event.listen("dispatch.stanza", dispatchStanza);

    function dispatchStanza(stanza, channel) {
        var userId = sessionService.getUserIdByChannel(channel);
        var action = stanza.action;
        var actionPath = route.getActionPath(action);
        var controller = controllerLoader.getController(actionPath);
        var actionFx = controllerLoader.getAction(actionPath);
        var errorResponseStanza = {
            id: stanza.id,
            action: action,
            type: "error"
        };
        if (actionPath == null || controller == null || actionFx == null) {
            var message = "Action not found: " + action;
            errorResponseStanza.body = message;

            channel.emit(errorResponseStanza);

            $logger.debug(message);
            return;
        }
        //ELSE:
        var group = route.getGroup(action);
        var beforeFx = group.getBeforeFx();
        var afterFx = group.getAfterFx();

        $input = new Input(userId, stanza, channel);
        $session = sessionService.get(userId);
        $response = new Response(userService, sessionService, channel, stanza);

        Q(beforeFx != null ? $invokeFx(beforeFx, null, buildActionArgumentsResolver($input, $session, $response)) : null)
                .then(function () {
                    if (!$response.isEnded()) {
                        return $invokeFx(actionFx, controller, buildActionArgumentsResolver($input, $session, $response));
                    }
                })
                .then(function () {
                    return afterFx != null ? $invokeFx(afterFx, null, buildActionArgumentsResolver($input, $session, $response)) : null;
                })
                .fail(function (error) {
                    var message = "Error occurs on processing request: " + actionPath + ": " + error.stack;
                    $logger.debug(message);
                    errorResponseStanza.body = message;
                    channel.emit(errorResponseStanza);
                });
    }
};

function buildActionArgumentsResolver($input, $session, $response) {
    return function (parameter) {
        var retVal = null;
        switch (parameter) {
            case "$input":
            {
                retVal = $input;
                break;
            }
            case "$session":
            {
                retVal = $session;
                break;
            }
            case "$response":
            {
                retVal = $response;
                break;
            }
        }
        return retVal;
    };
}<commit_msg>File name issue, input.js instead of Input.js<commit_after>/**
 * Dispatch a stanza to a controller action
 * @param {type} $event
 * @param {type} controllerLoader contains information about all controllers and actions
 * controllerLoader is not a service in IoC controller, it is created by start/servers.js
 */

var Input = require("../lib/input.js");

var Response = require("../lib/response.js");

var Q = require("q");

module.exports = function (userService, sessionService, $event, $logger, controllerLoader, $invokeFx) {

    var route = new (require("../lib/route.js"))();
    require("../start/routes.js")(route);

    //verify actionPath in routing rules
    var routingRules = route.getAllRules();
    routingRules.forEach(function (routingRule) {
        var actionFx = controllerLoader.getAction(routingRule.actionPath);
        if (actionFx == null) {
            throw new Error("Action path not found, re-check following routing rule: " + JSON.stringify({
                action: routingRule.action,
                actionPath: routingRule.actionPath
            }));
        }
    });

    $event.listen("dispatch.stanza", dispatchStanza);

    function dispatchStanza(stanza, channel) {
        var userId = sessionService.getUserIdByChannel(channel);
        var action = stanza.action;
        var actionPath = route.getActionPath(action);
        var controller = controllerLoader.getController(actionPath);
        var actionFx = controllerLoader.getAction(actionPath);
        var errorResponseStanza = {
            id: stanza.id,
            action: action,
            type: "error"
        };
        if (actionPath == null || controller == null || actionFx == null) {
            var message = "Action not found: " + action;
            errorResponseStanza.body = message;

            channel.emit(errorResponseStanza);

            $logger.debug(message);
            return;
        }
        //ELSE:
        var group = route.getGroup(action);
        var beforeFx = group.getBeforeFx();
        var afterFx = group.getAfterFx();

        $input = new Input(userId, stanza, channel);
        $session = sessionService.get(userId);
        $response = new Response(userService, sessionService, channel, stanza);

        Q(beforeFx != null ? $invokeFx(beforeFx, null, buildActionArgumentsResolver($input, $session, $response)) : null)
                .then(function () {
                    if (!$response.isEnded()) {
                        return $invokeFx(actionFx, controller, buildActionArgumentsResolver($input, $session, $response));
                    }
                })
                .then(function () {
                    return afterFx != null ? $invokeFx(afterFx, null, buildActionArgumentsResolver($input, $session, $response)) : null;
                })
                .fail(function (error) {
                    var message = "Error occurs on processing request: " + actionPath + ": " + error.stack;
                    $logger.debug(message);
                    errorResponseStanza.body = message;
                    channel.emit(errorResponseStanza);
                });
    }
};

function buildActionArgumentsResolver($input, $session, $response) {
    return function (parameter) {
        var retVal = null;
        switch (parameter) {
            case "$input":
            {
                retVal = $input;
                break;
            }
            case "$session":
            {
                retVal = $session;
                break;
            }
            case "$response":
            {
                retVal = $response;
                break;
            }
        }
        return retVal;
    };
}<|endoftext|>


<commit_before>/*
Copyright 2016 Aviral Dasgupta
Copyright 2017 New Vector Ltd

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

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

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

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import flatMap from 'lodash/flatMap';
import isEqual from 'lodash/isEqual';
import sdk from '../../../index';
import type {Completion} from '../../../autocomplete/Autocompleter';
import Promise from 'bluebird';
import UserSettingsStore from '../../../UserSettingsStore';

import Autocompleter from '../../../autocomplete/Autocompleter';

const COMPOSER_SELECTED = 0;

export default class Autocomplete extends React.Component {

    constructor(props) {
        super(props);

        this.autocompleter = new Autocompleter(props.room);
        this.completionPromise = null;
        this.hide = this.hide.bind(this);
        this.onCompletionClicked = this.onCompletionClicked.bind(this);

        this.state = {
            // list of completionResults, each containing completions
            completions: [],

            // array of completions, so we can look up current selection by offset quickly
            completionList: [],

            // how far down the completion list we are (THIS IS 1-INDEXED!)
            selectionOffset: COMPOSER_SELECTED,

            // whether we should show completions if they're available
            shouldShowCompletions: true,

            hide: false,

            forceComplete: false,
        };
    }

    componentWillReceiveProps(newProps, state) {
        if (this.props.room.roomId !== newProps.room.roomId) {
            this.autocompleter.destroy();
            this.autocompleter = new Autocompleter();
        }

        // Query hasn't changed so don't try to complete it
        if (newProps.query === this.props.query) {
            return;
        }

        this.complete(newProps.query, newProps.selection);
    }

    componentWillUnmount() {
        this.autocompleter.destroy();
    }

    complete(query, selection) {
        this.queryRequested = query;
        if (this.debounceCompletionsRequest) {
            clearTimeout(this.debounceCompletionsRequest);
        }
        if (query === "") {
            this.setState({
                // Clear displayed completions
                completions: [],
                completionList: [],
                // Reset selected completion
                selectionOffset: COMPOSER_SELECTED,
                // Hide the autocomplete box
                hide: true,
            });
            return Promise.resolve(null);
        }
        let autocompleteDelay = UserSettingsStore.getLocalSetting('autocompleteDelay', 200);

        // Don't debounce if we are already showing completions
        if (this.state.completions.length > 0 || this.state.forceComplete) {
            autocompleteDelay = 0;
        }

        const deferred = Promise.defer();
        this.debounceCompletionsRequest = setTimeout(() => {
            this.processQuery(query, selection).then(() => {
                deferred.resolve();
            });
        }, autocompleteDelay);
        return deferred.promise;
    }

    processQuery(query, selection) {
        return this.autocompleter.getCompletions(
            query, selection, this.state.forceComplete,
        ).then((completions) => {
            // Only ever process the completions for the most recent query being processed
            if (query !== this.queryRequested) {
                return;
            }
            this.processCompletions(completions);
        });
    }

    processCompletions(completions) {
        const completionList = flatMap(completions, (provider) => provider.completions);

        // Reset selection when completion list becomes empty.
        let selectionOffset = COMPOSER_SELECTED;
        if (completionList.length > 0) {
            /* If the currently selected completion is still in the completion list,
             try to find it and jump to it. If not, select composer.
             */
            const currentSelection = this.state.selectionOffset === 0 ? null :
                this.state.completionList[this.state.selectionOffset - 1].completion;
            selectionOffset = completionList.findIndex(
                (completion) => completion.completion === currentSelection);
            if (selectionOffset === -1) {
                selectionOffset = COMPOSER_SELECTED;
            } else {
                selectionOffset++; // selectionOffset is 1-indexed!
            }
        }

        let hide = this.state.hide;
        // If `completion.command.command` is truthy, then a provider has matched with the query
        const anyMatches = completions.some((completion) => !!completion.command.command);
        hide = !anyMatches;

        this.setState({
            completions,
            completionList,
            selectionOffset,
            hide,
            // Force complete is turned off each time since we can't edit the query in that case
            forceComplete: false,
        });
    }

    countCompletions(): number {
        return this.state.completionList.length;
    }

    // called from MessageComposerInput
    onUpArrow(): ?Completion {
        const completionCount = this.countCompletions();
        // completionCount + 1, since 0 means composer is selected
        const selectionOffset = (completionCount + 1 + this.state.selectionOffset - 1)
            % (completionCount + 1);
        if (!completionCount) {
            return null;
        }
        this.setSelection(selectionOffset);
    }

    // called from MessageComposerInput
    onDownArrow(): ?Completion {
        const completionCount = this.countCompletions();
        // completionCount + 1, since 0 means composer is selected
        const selectionOffset = (this.state.selectionOffset + 1) % (completionCount + 1);
        if (!completionCount) {
            return null;
        }
        this.setSelection(selectionOffset);
    }

    onEscape(e): boolean {
        const completionCount = this.countCompletions();
        if (completionCount === 0) {
            // autocomplete is already empty, so don't preventDefault
            return;
        }

        e.preventDefault();

        // selectionOffset = 0, so we don't end up completing when autocomplete is hidden
        this.hide();
    }

    hide() {
        this.setState({hide: true, selectionOffset: 0, completions: [], completionList: []});
    }

    forceComplete() {
        const done = Promise.defer();
        this.setState({
            forceComplete: true,
            hide: false,
        }, () => {
            this.complete(this.props.query, this.props.selection).then(() => {
                done.resolve(this.countCompletions());
            });
        });
        return done.promise;
    }

    onCompletionClicked(): boolean {
        if (this.countCompletions() === 0 || this.state.selectionOffset === COMPOSER_SELECTED) {
            return false;
        }

        this.props.onConfirm(this.state.completionList[this.state.selectionOffset - 1]);
        this.hide();

        return true;
    }

    setSelection(selectionOffset: number) {
        this.setState({selectionOffset, hide: false});
        if (this.props.onSelectionChange) {
            this.props.onSelectionChange(this.state.completionList[selectionOffset - 1]);
        }
    }

    componentDidUpdate() {
        // this is the selected completion, so scroll it into view if needed
        const selectedCompletion = this.refs[`completion${this.state.selectionOffset}`];
        if (selectedCompletion && this.container) {
            const domNode = ReactDOM.findDOMNode(selectedCompletion);
            const offsetTop = domNode && domNode.offsetTop;
            if (offsetTop > this.container.scrollTop + this.container.offsetHeight ||
                offsetTop < this.container.scrollTop) {
                this.container.scrollTop = offsetTop - this.container.offsetTop;
            }
        }
    }

    setState(state, func) {
        super.setState(state, func);
    }

    render() {
        const EmojiText = sdk.getComponent('views.elements.EmojiText');

        let position = 1;
        const renderedCompletions = this.state.completions.map((completionResult, i) => {
            const completions = completionResult.completions.map((completion, i) => {
                const className = classNames('mx_Autocomplete_Completion', {
                    'selected': position === this.state.selectionOffset,
                });
                const componentPosition = position;
                position++;

                const onMouseMove = () => this.setSelection(componentPosition);
                const onClick = () => {
                    this.setSelection(componentPosition);
                    this.onCompletionClicked();
                };

                return React.cloneElement(completion.component, {
                    key: i,
                    ref: `completion${position - 1}`,
                    className,
                    onMouseMove,
                    onClick,
                });
            });

            return completions.length > 0 ? (

                    <EmojiText element="div" className="mx_Autocomplete_provider_name">{ completionResult.provider.getName() }</EmojiText>
                    { completionResult.provider.renderCompletions(completions) }

            ) : null;
        }).filter((completion) => !!completion);

        return !this.state.hide && renderedCompletions.length > 0 ? (

 this.container = e}>
                { renderedCompletions }

        ) : null;
    }
}

Autocomplete.propTypes = {
    // the query string for which to show autocomplete suggestions
    query: PropTypes.string.isRequired,

    // method invoked with range and text content when completion is confirmed
    onConfirm: PropTypes.func.isRequired,

    // The room in which we're autocompleting
    room: PropTypes.object,
};
<commit_msg>Pass room into Autocompleter<commit_after>/*
Copyright 2016 Aviral Dasgupta
Copyright 2017 New Vector Ltd

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

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

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

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import flatMap from 'lodash/flatMap';
import isEqual from 'lodash/isEqual';
import sdk from '../../../index';
import type {Completion} from '../../../autocomplete/Autocompleter';
import Promise from 'bluebird';
import UserSettingsStore from '../../../UserSettingsStore';

import Autocompleter from '../../../autocomplete/Autocompleter';

const COMPOSER_SELECTED = 0;

export default class Autocomplete extends React.Component {

    constructor(props) {
        super(props);

        this.autocompleter = new Autocompleter(props.room);
        this.completionPromise = null;
        this.hide = this.hide.bind(this);
        this.onCompletionClicked = this.onCompletionClicked.bind(this);

        this.state = {
            // list of completionResults, each containing completions
            completions: [],

            // array of completions, so we can look up current selection by offset quickly
            completionList: [],

            // how far down the completion list we are (THIS IS 1-INDEXED!)
            selectionOffset: COMPOSER_SELECTED,

            // whether we should show completions if they're available
            shouldShowCompletions: true,

            hide: false,

            forceComplete: false,
        };
    }

    componentWillReceiveProps(newProps, state) {
        if (this.props.room.roomId !== newProps.room.roomId) {
            this.autocompleter.destroy();
            this.autocompleter = new Autocompleter(newProps.room);
        }

        // Query hasn't changed so don't try to complete it
        if (newProps.query === this.props.query) {
            return;
        }

        this.complete(newProps.query, newProps.selection);
    }

    componentWillUnmount() {
        this.autocompleter.destroy();
    }

    complete(query, selection) {
        this.queryRequested = query;
        if (this.debounceCompletionsRequest) {
            clearTimeout(this.debounceCompletionsRequest);
        }
        if (query === "") {
            this.setState({
                // Clear displayed completions
                completions: [],
                completionList: [],
                // Reset selected completion
                selectionOffset: COMPOSER_SELECTED,
                // Hide the autocomplete box
                hide: true,
            });
            return Promise.resolve(null);
        }
        let autocompleteDelay = UserSettingsStore.getLocalSetting('autocompleteDelay', 200);

        // Don't debounce if we are already showing completions
        if (this.state.completions.length > 0 || this.state.forceComplete) {
            autocompleteDelay = 0;
        }

        const deferred = Promise.defer();
        this.debounceCompletionsRequest = setTimeout(() => {
            this.processQuery(query, selection).then(() => {
                deferred.resolve();
            });
        }, autocompleteDelay);
        return deferred.promise;
    }

    processQuery(query, selection) {
        return this.autocompleter.getCompletions(
            query, selection, this.state.forceComplete,
        ).then((completions) => {
            // Only ever process the completions for the most recent query being processed
            if (query !== this.queryRequested) {
                return;
            }
            this.processCompletions(completions);
        });
    }

    processCompletions(completions) {
        const completionList = flatMap(completions, (provider) => provider.completions);

        // Reset selection when completion list becomes empty.
        let selectionOffset = COMPOSER_SELECTED;
        if (completionList.length > 0) {
            /* If the currently selected completion is still in the completion list,
             try to find it and jump to it. If not, select composer.
             */
            const currentSelection = this.state.selectionOffset === 0 ? null :
                this.state.completionList[this.state.selectionOffset - 1].completion;
            selectionOffset = completionList.findIndex(
                (completion) => completion.completion === currentSelection);
            if (selectionOffset === -1) {
                selectionOffset = COMPOSER_SELECTED;
            } else {
                selectionOffset++; // selectionOffset is 1-indexed!
            }
        }

        let hide = this.state.hide;
        // If `completion.command.command` is truthy, then a provider has matched with the query
        const anyMatches = completions.some((completion) => !!completion.command.command);
        hide = !anyMatches;

        this.setState({
            completions,
            completionList,
            selectionOffset,
            hide,
            // Force complete is turned off each time since we can't edit the query in that case
            forceComplete: false,
        });
    }

    countCompletions(): number {
        return this.state.completionList.length;
    }

    // called from MessageComposerInput
    onUpArrow(): ?Completion {
        const completionCount = this.countCompletions();
        // completionCount + 1, since 0 means composer is selected
        const selectionOffset = (completionCount + 1 + this.state.selectionOffset - 1)
            % (completionCount + 1);
        if (!completionCount) {
            return null;
        }
        this.setSelection(selectionOffset);
    }

    // called from MessageComposerInput
    onDownArrow(): ?Completion {
        const completionCount = this.countCompletions();
        // completionCount + 1, since 0 means composer is selected
        const selectionOffset = (this.state.selectionOffset + 1) % (completionCount + 1);
        if (!completionCount) {
            return null;
        }
        this.setSelection(selectionOffset);
    }

    onEscape(e): boolean {
        const completionCount = this.countCompletions();
        if (completionCount === 0) {
            // autocomplete is already empty, so don't preventDefault
            return;
        }

        e.preventDefault();

        // selectionOffset = 0, so we don't end up completing when autocomplete is hidden
        this.hide();
    }

    hide() {
        this.setState({hide: true, selectionOffset: 0, completions: [], completionList: []});
    }

    forceComplete() {
        const done = Promise.defer();
        this.setState({
            forceComplete: true,
            hide: false,
        }, () => {
            this.complete(this.props.query, this.props.selection).then(() => {
                done.resolve(this.countCompletions());
            });
        });
        return done.promise;
    }

    onCompletionClicked(): boolean {
        if (this.countCompletions() === 0 || this.state.selectionOffset === COMPOSER_SELECTED) {
            return false;
        }

        this.props.onConfirm(this.state.completionList[this.state.selectionOffset - 1]);
        this.hide();

        return true;
    }

    setSelection(selectionOffset: number) {
        this.setState({selectionOffset, hide: false});
        if (this.props.onSelectionChange) {
            this.props.onSelectionChange(this.state.completionList[selectionOffset - 1]);
        }
    }

    componentDidUpdate() {
        // this is the selected completion, so scroll it into view if needed
        const selectedCompletion = this.refs[`completion${this.state.selectionOffset}`];
        if (selectedCompletion && this.container) {
            const domNode = ReactDOM.findDOMNode(selectedCompletion);
            const offsetTop = domNode && domNode.offsetTop;
            if (offsetTop > this.container.scrollTop + this.container.offsetHeight ||
                offsetTop < this.container.scrollTop) {
                this.container.scrollTop = offsetTop - this.container.offsetTop;
            }
        }
    }

    setState(state, func) {
        super.setState(state, func);
    }

    render() {
        const EmojiText = sdk.getComponent('views.elements.EmojiText');

        let position = 1;
        const renderedCompletions = this.state.completions.map((completionResult, i) => {
            const completions = completionResult.completions.map((completion, i) => {
                const className = classNames('mx_Autocomplete_Completion', {
                    'selected': position === this.state.selectionOffset,
                });
                const componentPosition = position;
                position++;

                const onMouseMove = () => this.setSelection(componentPosition);
                const onClick = () => {
                    this.setSelection(componentPosition);
                    this.onCompletionClicked();
                };

                return React.cloneElement(completion.component, {
                    key: i,
                    ref: `completion${position - 1}`,
                    className,
                    onMouseMove,
                    onClick,
                });
            });

            return completions.length > 0 ? (

                    <EmojiText element="div" className="mx_Autocomplete_provider_name">{ completionResult.provider.getName() }</EmojiText>
                    { completionResult.provider.renderCompletions(completions) }

            ) : null;
        }).filter((completion) => !!completion);

        return !this.state.hide && renderedCompletions.length > 0 ? (

 this.container = e}>
                { renderedCompletions }

        ) : null;
    }
}

Autocomplete.propTypes = {
    // the query string for which to show autocomplete suggestions
    query: PropTypes.string.isRequired,

    // method invoked with range and text content when completion is confirmed
    onConfirm: PropTypes.func.isRequired,

    // The room in which we're autocompleting
    room: PropTypes.object,
};
<|endoftext|>


<commit_before>(function () {
    function config($stateProvider, $locationProvider) {
        $locationProvider
            .html5Mode({
                enabled: true,
                requireBase: false
            });

        $stateProvider
            .state('landing', {
                url: '/',
                controller: 'LandingCtrl as landing',
                templateUrl: '/templates/landing.html'
            })
            .state('album', {
                url: '/album',
                controller: 'AlbumCtrl as album',
                templateUrl: '/templates/album.html'
            })
            .state('collection', {
                url: '/collection',
                controller: 'CollectionCtrl as collection',
                templateUrl: '/templates/collection.html'
            });
    }

    angular
        .module('blocJams', ['ui.router'])
        .config(config);

})();
<commit_msg>SourceTree Test<commit_after>(function () {
    function config($stateProvider, $locationProvider) {
        $locationProvider
            .html5Mode({
                enabled: true,
                requireBase: false
            });

        $stateProvider
            .state('landing', {
                url: '/',
                controller: 'LandingCtrl as landing',
                templateUrl: '/templates/landing.html'
            })
            .state('album', {
                url: '/album',
                controller: 'AlbumCtrl as album',
                templateUrl: '/templates/album.html'
            })
            .state('collection', {
                url: '/collection',
                controller: 'CollectionCtrl as collection',
                templateUrl: '/templates/collection.html'
            });
    }

    angular
        .module('blocJams', ['ui.router'])
        .config(config);

})();
<|endoftext|>


<commit_before>/* global angular */
var templateApp = angular.module("templateApp", ["ngRoute"]);

templateApp.config(function ($routeProvider) {
	$routeProvider
		.when("/", {
			templateUrl: "main.html",
			controller: "mainCtrl as ctrl"
		})
		.when("/other", {
			templateUrl: "other.html",
			controller: "otherCtrl as ctrl"
		})
		.otherwise({redirectTo: "/"});
});<commit_msg>Using ng-href<commit_after>/* global angular */
var templateApp = angular.module("templateApp", ["ngRoute"]);

templateApp.config(function ($routeProvider) {
	$routeProvider
		.when("/", {
			templateUrl: "partials/main.html",
			controller: "mainCtrl as ctrl"
		})
		.when("/other", {
			templateUrl: "other.html",
			controller: "otherCtrl as ctrl"
		})
		.otherwise({redirectTo: "/"});
});<|endoftext|>


<commit_before>/*jshint multistr:true */

this.recline = this.recline || {};
this.recline.View = this.recline.View || {};

(function($, my) {
  "use strict";

my.FilterEditor = Backbone.View.extend({
  className: 'recline-filter-editor well',
  template: ' \

 \

Filters
 \
      Add filter \
      <form class="form-stacked js-add" style="display: none;"> \
        <fieldset> \
          Field \
          <select class="fields"> \
            {{#fields}} \
            <option value="{{id}}">{{label}}</option> \
            {{/fields}} \
          </select> \
          Filter type \
          <select class="filterType"> \
            <option value="term">Value</option> \
            <option value="range">Range</option> \
            <option value="geo_distance">Geo distance</option> \
          </select> \
          <button type="submit" class="btn">Add</button> \
        </fieldset> \
      </form> \
      <form class="form-stacked js-edit"> \
        {{#filters}} \
          {{{filterRender}}} \
        {{/filters}} \
        {{#filters.length}} \
        <button type="submit" class="btn">Update</button> \
        {{/filters.length}} \
      </form> \

 \
  ',
  filterTemplates: {
    term: ' \

 \
        <fieldset> \
          <legend> \
            {{field}} {{type}} \
            × \
          </legend> \
          <input type="text" value="{{term}}" name="term" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
        </fieldset> \

 \
    ',
    range: ' \

 \
        <fieldset> \
          <legend> \
            {{field}} {{type}} \
            × \
          </legend> \
          From \
          <input type="text" value="{{from}}" name="from" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
          To \
          <input type="text" value="{{to}}" name="to" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
        </fieldset> \

 \
    ',
    geo_distance: ' \

 \
        <fieldset> \
          <legend> \
            {{field}} {{type}} \
            × \
          </legend> \
          Longitude \
          <input type="text" value="{{point.lon}}" name="lon" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
          Latitude \
          <input type="text" value="{{point.lat}}" name="lat" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
          Distance (km) \
          <input type="text" value="{{distance}}" name="distance" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
        </fieldset> \

 \
    '
  },
  events: {
    'click .js-remove-filter': 'onRemoveFilter',
    'click .js-add-filter': 'onAddFilterShow',
    'submit form.js-edit': 'onTermFiltersUpdate',
    'submit form.js-add': 'onAddFilter'
  },
  initialize: function() {
    _.bindAll(this, 'render');
    this.listenTo(this.model.fields, 'all', this.render);
    this.listenTo(this.model.queryState, 'change change:filters:new-blank', this.render);
    this.render();
  },
  render: function() {
    var self = this;
    var tmplData = $.extend(true, {}, this.model.queryState.toJSON());
    // we will use idx in list as there id ...
    tmplData.filters = _.map(tmplData.filters, function(filter, idx) {
      filter.id = idx;
      return filter;
    });
    tmplData.fields = this.model.fields.toJSON();
    tmplData.filterRender = function() {
      return Mustache.render(self.filterTemplates[this.type], this);
    };
    var out = Mustache.render(this.template, tmplData);
    this.$el.html(out);
  },
  onAddFilterShow: function(e) {
    e.preventDefault();
    var $target = $(e.target);
    $target.hide();
    this.$el.find('form.js-add').show();
  },
  onAddFilter: function(e) {
    e.preventDefault();
    var $target = $(e.target);
    $target.hide();
    var filterType = $target.find('select.filterType').val();
    var field      = $target.find('select.fields').val();
    this.model.queryState.addFilter({type: filterType, field: field});
  },
  onRemoveFilter: function(e) {
    e.preventDefault();
    var $target = $(e.target);
    var filterId = $target.attr('data-filter-id');
    this.model.queryState.removeFilter(filterId);
  },
  onTermFiltersUpdate: function(e) {
   var self = this;
    e.preventDefault();
    var filters = self.model.queryState.get('filters');
    var $form = $(e.target);
    _.each($form.find('input'), function(input) {
      var $input = $(input);
      var filterType  = $input.attr('data-filter-type');
      var fieldId     = $input.attr('data-filter-field');
      var filterIndex = parseInt($input.attr('data-filter-id'), 10);
      var name        = $input.attr('name');
      var value       = $input.val();

      switch (filterType) {
        case 'term':
          filters[filterIndex].term = value;
          break;
        case 'range':
          filters[filterIndex][name] = value;
          break;
        case 'geo_distance':
          if(name === 'distance') {
            filters[filterIndex].distance = parseFloat(value);
          }
          else {
            filters[filterIndex].point[name] = parseFloat(value);
          }
          break;
      }
    });
    self.model.queryState.set({filters: filters, from: 0});
    self.model.queryState.trigger('change');
  }
});

})(jQuery, recline.View);

<commit_msg>Update widget.filtereditor.js<commit_after>/*jshint multistr:true */

this.recline = this.recline || {};
this.recline.View = this.recline.View || {};

(function($, my) {
  "use strict";

my.FilterEditor = Backbone.View.extend({
  className: 'recline-filter-editor well',
  template: ' \

 \

Filters
 \
      Add filter \
      <form class="form-stacked js-add" style="display: none;"> \
        <fieldset> \
          Field \
          <select class="fields"> \
            {{#fields}} \
            <option value="{{id}}">{{label}}</option> \
            {{/fields}} \
          </select> \
          Filter type \
          <select class="filterType"> \
            <option value="term">Value</option> \
            <option value="range">Range</option> \
            <option value="geo_distance">Geo distance</option> \
          </select> \
          <button type="submit" class="btn">Add</button> \
        </fieldset> \
      </form> \
      <form class="form-stacked js-edit"> \
        {{#filters}} \
          {{{filterRender}}} \
        {{/filters}} \
        {{#filters.length}} \
        <button type="submit" class="btn">Update</button> \
        {{/filters.length}} \
      </form> \

 \
  ',
  filterTemplates: {
    term: ' \

 \
        <fieldset> \
          <legend> \
            {{field}} {{type}} \
            × \
          </legend> \
          <input type="text" value="{{term}}" name="term" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
        </fieldset> \

 \
    ',
    range: ' \

 \
        <fieldset> \
          <legend> \
            {{field}} {{type}} \
            × \
          </legend> \
          From \
          <input type="text" value="{{from}}" name="from" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
          To \
          <input type="text" value="{{to}}" name="to" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
        </fieldset> \

 \
    ',
    geo_distance: ' \

 \
        <fieldset> \
          <legend> \
            {{field}} {{type}} \
            × \
          </legend> \
          Longitude \
          <input type="text" value="{{point.lon}}" name="lon" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
          Latitude \
          <input type="text" value="{{point.lat}}" name="lat" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
          Distance (km) \
          <input type="text" value="{{distance}}" name="distance" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
        </fieldset> \

 \
    '
  },
  events: {
    'click .js-remove-filter': 'onRemoveFilter',
    'click .js-add-filter': 'onAddFilterShow',
    'submit form.js-edit': 'onTermFiltersUpdate',
    'submit form.js-add': 'onAddFilter'
  },
  initialize: function() {
    _.bindAll(this, 'render');
    this.listenTo(this.model.fields, 'all', this.render);
    this.listenTo(this.model.queryState, 'change change:filters:new-blank', this.render);
    this.render();
  },
  render: function() {
    var self = this;
    var tmplData = $.extend(true, {}, this.model.queryState.toJSON());
    // we will use idx in list as there id ...
    tmplData.filters = _.map(tmplData.filters, function(filter, idx) {
      filter.id = idx;
      return filter;
    });
    tmplData.fields = this.model.fields.toJSON();
    tmplData.filterRender = function() {
      return Mustache.render(self.filterTemplates[this.type], this);
    };
    var out = Mustache.render(this.template, tmplData);
    this.$el.html(out);
  },
  onAddFilterShow: function(e) {
    e.preventDefault();
    var $target = $(e.target);
    $target.hide();
    this.$el.find('form.js-add').show();
  },
  onAddFilter: function(e) {
    e.preventDefault();
    var $target = $(e.target);
    $target.hide();
    var filterType = $target.find('select.filterType').val();
    var field      = $target.find('select.fields').val();
    this.model.queryState.addFilter({type: filterType, field: field});
  },
  onRemoveFilter: function(e) {
    e.preventDefault();
    var $target = $(e.target);
    var filterId = $target.attr('data-filter-id');
    this.model.queryState.removeFilter(filterId);
  },
  onTermFiltersUpdate: function(e) {
   var self = this;
    e.preventDefault();
    var filters = self.model.queryState.get('filters');
    var $form = $(e.target);
    _.each($form.find('input'), function(input) {
      var $input = $(input);
      var filterType  = $input.attr('data-filter-type');
      var fieldId     = $input.attr('data-filter-field');
      var filterIndex = parseInt($input.attr('data-filter-id'), 10);
      var name        = $input.attr('name');
      var value       = $input.val();

      switch (filterType) {
        case 'term':
          filters[filterIndex].term = value;
          break;
        case 'range':
          filters[filterIndex][name] = value;
          break;
        case 'geo_distance':
          if(name === 'distance') {
            filters[filterIndex].distance = parseFloat(value);
          }
          else {
            filters[filterIndex].point[name] = parseFloat(value);
          }
          break;
      }
    });
    self.model.queryState.set({filters: filters, from: 0});
    self.model.queryState.trigger('change');
  }
});

})(jQuery, this.recline.View);
<|endoftext|>


<commit_before>var _credentials,
    _production,
    Q   = require('q'),
    fs  = require('fs'),
    req = require('request');

function requestWithDeferred (method, path, qs, body) {
    var deferred = Q.defer();

    request(deferred, method, path, qs, body);

    return deferred.promise;
}

function request (deferred, method, path, qs, body) {
    var base = _production ? 'https://api.signaturit.com' : 'http://api.sandbox.signaturit.com';

    return req({
        method: method,
        uri: base + path,
        qs: qs,
        body: body,
        auth: {
            bearer: _credentials
        },
        headers: {
            'User-Agent': 'signaturit-node-sdk 0.0.6'
        },
        json: true
    }, function (error, response, body) {
        if (error) {
            deferred.reject(result);
        } else {
            deferred.resolve(body);
        }
    });
}

function SignaturitClient (credentials, production) {
    _credentials = credentials;
    _production  = production;
}

SignaturitClient.prototype.getAccount = function () {
    return requestWithDeferred('GET', '/v2/account.json');
};

SignaturitClient.prototype.setDocumentStorage = function (type, params) {
    params = params || {};

    params.type = type;

    return requestWithDeferred('POST', '/v2/account/storage.json', null, params);
};

SignaturitClient.prototype.revertToDefaultDocumentStorage = function () {
    return requestWithDeferred('DELETE', '/v2/account/storage.json');
};

SignaturitClient.prototype.getSignature = function (signatureId) {
    return requestWithDeferred('GET', '/v2/signs/' + signatureId + '.json');
};

SignaturitClient.prototype.getSignatures = function (limit, offset, status, since, data) {
    var params = {
        limit: limit || 100,
        offset: offset || 0
    };

    if (status) {
        params.status = status;
    }

    if (since) {
        params.since = since;
    }

    if (data) {
        for (var key in data) {
            var newKey = 'data.' + key;

            params[newKey] = data[key];
        }
    }

    return requestWithDeferred('GET', '/v2/signs.json', params);
};

SignaturitClient.prototype.countSignatures = function (status, since, data) {
    var params = {};

    if (status) {
        params.status = status;
    }

    if (since) {
        params.since = since;
    }

     if (data) {
        for (var key in data) {
            var newKey = 'data.' + key;

            params[newKey] = data[key];
        }
    }

    return requestWithDeferred('GET', '/v2/signs/count.json', params);
};

SignaturitClient.prototype.getSignatureDocument = function (signatureId, documentId) {
    return requestWithDeferred('GET', '/v2/signs/' + signatureId + '/documents/' + documentId + '.json');
};

SignaturitClient.prototype.getSignatureDocuments = function (signatureId) {
    return requestWithDeferred('GET', '/v2/signs/' + signatureId + '/documents.json');
};

SignaturitClient.prototype.getAuditTrail = function (signatureId, documentId, path) {
    var deferred = Q.defer();

    request(deferred, 'GET', '/v2/signs/' + signatureId + '/documents/' + documentId + '/download/doc_proof').pipe(
        fs.createWriteStream(path)
    );

    return deferred.promise;
};

SignaturitClient.prototype.getSignedDocument = function (signatureId, documentId, path) {
    var deferred = Q.defer();

    request(deferred, 'GET', '/v2/signs/' + signatureId + '/documents/' + documentId + '/download/signed').pipe(
        fs.createWriteStream(path)
    );

    return deferred.promise;
};

SignaturitClient.prototype.createSignatureRequest = function (filesPath, recipients, params) {
    var deferred = Q.defer(),
        req      = request(deferred, 'POST', '/v2/signs.json'),
        form     = req.form();

    filesPath = [].concat(filesPath);

    filesPath.forEach(function(filePath, i) {
        form.append(
            'files[' + i + ']',
            fs.createReadStream(filePath)
        );
    });

    recipients = [].concat(recipients);

    recipients.forEach(function(recipient, i) {
        if (recipient.email) {
            form.append('recipients[' + i + '][fullname]', recipient.fullname);
            form.append('recipients[' + i + '][email]', recipient.email);

            if (recipient.phone) {
                form.append('recipients[' + i + '][phone]', recipient.phone);
            }
        } else {
            form.append('recipients[' + i + ']', recipient);
        }
    });

    params = params || {};

    Object.keys(params).forEach(function(key) {
        if ('object' == typeof(params[key])) {
            Object.keys(params[key]).forEach(function(innerKey) {
                form.append(key +'[' + innerKey + ']', params[key][innerKey])
            });
        } else {
            form.append(key, params[key]);
        }
    });

    return deferred.promise;
};

SignaturitClient.prototype.cancelSignatureRequest = function (signatureId) {
    return requestWithDeferred('PATCH', '/v2/signs/' + signatureId + '/cancel.json');
};

SignaturitClient.prototype.sendReminder = function (signatureId, documentId) {
    return requestWithDeferred('POST', '/v2/signs/' + signatureId + '/documents/' + documentId + '/reminder.json');
};

SignaturitClient.prototype.getBranding = function (brandingId) {
    return requestWithDeferred('GET', '/v2/brandings/' + brandingId + '.json');
};

SignaturitClient.prototype.getBrandings = function () {
    return requestWithDeferred('GET', '/v2/brandings.json');
};

SignaturitClient.prototype.createBranding = function (params) {
    return requestWithDeferred('POST', '/v2/brandings.json', null, params);
};

SignaturitClient.prototype.updateBranding = function (brandingId, params) {
    return requestWithDeferred('PATCH', '/v2/brandings/' + brandingId + '.json', null, params);
};

SignaturitClient.prototype.updateBrandingLogo = function (brandingId, filePath) {
    var deferred = Q.defer();

    fs.createReadStream(filePath).pipe(
        request(deferred, 'PUT', '/v2/brandings/' + brandingId + '/logo.json')
    );

    return deferred.promise;
};

SignaturitClient.prototype.updateBrandingTemplate = function (brandingId, template, filePath) {
    var deferred = Q.defer();

    fs.createReadStream(filePath).pipe(
        request(deferred, 'PUT', '/v2/brandings/' + brandingId + '/emails/' + template + '.json')
    );

    return deferred.promise;
};

SignaturitClient.prototype.getTemplates = function () {
    return requestWithDeferred('GET', '/v2/templates.json');
};

module.exports = SignaturitClient;
<commit_msg>Update signaturit.js<commit_after>var _credentials,
    _production,
    Q   = require('q'),
    fs  = require('fs'),
    req = require('request');

function requestWithDeferred (method, path, qs, body) {
    var deferred = Q.defer();

    request(deferred, method, path, qs, body);

    return deferred.promise;
}

function request (deferred, method, path, qs, body) {
    var base = _production ? 'https://api.signaturit.com' : 'http://api.sandbox.signaturit.com';

    return req({
        method: method,
        uri: base + path,
        qs: qs,
        body: body,
        auth: {
            bearer: _credentials
        },
        headers: {
            'User-Agent': 'signaturit-node-sdk 0.0.6'
        },
        json: true
    }, function (error, response, body) {
        if (error) {
            deferred.reject(error);
        } else {
            deferred.resolve(body);
        }
    });
}

function SignaturitClient (credentials, production) {
    _credentials = credentials;
    _production  = production;
}

SignaturitClient.prototype.getAccount = function () {
    return requestWithDeferred('GET', '/v2/account.json');
};

SignaturitClient.prototype.setDocumentStorage = function (type, params) {
    params = params || {};

    params.type = type;

    return requestWithDeferred('POST', '/v2/account/storage.json', null, params);
};

SignaturitClient.prototype.revertToDefaultDocumentStorage = function () {
    return requestWithDeferred('DELETE', '/v2/account/storage.json');
};

SignaturitClient.prototype.getSignature = function (signatureId) {
    return requestWithDeferred('GET', '/v2/signs/' + signatureId + '.json');
};

SignaturitClient.prototype.getSignatures = function (limit, offset, status, since, data) {
    var params = {
        limit: limit || 100,
        offset: offset || 0
    };

    if (status) {
        params.status = status;
    }

    if (since) {
        params.since = since;
    }

    if (data) {
        for (var key in data) {
            var newKey = 'data.' + key;

            params[newKey] = data[key];
        }
    }

    return requestWithDeferred('GET', '/v2/signs.json', params);
};

SignaturitClient.prototype.countSignatures = function (status, since, data) {
    var params = {};

    if (status) {
        params.status = status;
    }

    if (since) {
        params.since = since;
    }

     if (data) {
        for (var key in data) {
            var newKey = 'data.' + key;

            params[newKey] = data[key];
        }
    }

    return requestWithDeferred('GET', '/v2/signs/count.json', params);
};

SignaturitClient.prototype.getSignatureDocument = function (signatureId, documentId) {
    return requestWithDeferred('GET', '/v2/signs/' + signatureId + '/documents/' + documentId + '.json');
};

SignaturitClient.prototype.getSignatureDocuments = function (signatureId) {
    return requestWithDeferred('GET', '/v2/signs/' + signatureId + '/documents.json');
};

SignaturitClient.prototype.getAuditTrail = function (signatureId, documentId, path) {
    var deferred = Q.defer();

    request(deferred, 'GET', '/v2/signs/' + signatureId + '/documents/' + documentId + '/download/doc_proof').pipe(
        fs.createWriteStream(path)
    );

    return deferred.promise;
};

SignaturitClient.prototype.getSignedDocument = function (signatureId, documentId, path) {
    var deferred = Q.defer();

    request(deferred, 'GET', '/v2/signs/' + signatureId + '/documents/' + documentId + '/download/signed').pipe(
        fs.createWriteStream(path)
    );

    return deferred.promise;
};

SignaturitClient.prototype.createSignatureRequest = function (filesPath, recipients, params) {
    var deferred = Q.defer(),
        req      = request(deferred, 'POST', '/v2/signs.json'),
        form     = req.form();

    filesPath = [].concat(filesPath);

    filesPath.forEach(function(filePath, i) {
        form.append(
            'files[' + i + ']',
            fs.createReadStream(filePath)
        );
    });

    recipients = [].concat(recipients);

    recipients.forEach(function(recipient, i) {
        if (recipient.email) {
            form.append('recipients[' + i + '][fullname]', recipient.fullname);
            form.append('recipients[' + i + '][email]', recipient.email);

            if (recipient.phone) {
                form.append('recipients[' + i + '][phone]', recipient.phone);
            }
        } else {
            form.append('recipients[' + i + ']', recipient);
        }
    });

    params = params || {};

    Object.keys(params).forEach(function(key) {
        if ('object' == typeof(params[key])) {
            Object.keys(params[key]).forEach(function(innerKey) {
                form.append(key +'[' + innerKey + ']', params[key][innerKey])
            });
        } else {
            form.append(key, params[key]);
        }
    });

    return deferred.promise;
};

SignaturitClient.prototype.cancelSignatureRequest = function (signatureId) {
    return requestWithDeferred('PATCH', '/v2/signs/' + signatureId + '/cancel.json');
};

SignaturitClient.prototype.sendReminder = function (signatureId, documentId) {
    return requestWithDeferred('POST', '/v2/signs/' + signatureId + '/documents/' + documentId + '/reminder.json');
};

SignaturitClient.prototype.getBranding = function (brandingId) {
    return requestWithDeferred('GET', '/v2/brandings/' + brandingId + '.json');
};

SignaturitClient.prototype.getBrandings = function () {
    return requestWithDeferred('GET', '/v2/brandings.json');
};

SignaturitClient.prototype.createBranding = function (params) {
    return requestWithDeferred('POST', '/v2/brandings.json', null, params);
};

SignaturitClient.prototype.updateBranding = function (brandingId, params) {
    return requestWithDeferred('PATCH', '/v2/brandings/' + brandingId + '.json', null, params);
};

SignaturitClient.prototype.updateBrandingLogo = function (brandingId, filePath) {
    var deferred = Q.defer();

    fs.createReadStream(filePath).pipe(
        request(deferred, 'PUT', '/v2/brandings/' + brandingId + '/logo.json')
    );

    return deferred.promise;
};

SignaturitClient.prototype.updateBrandingTemplate = function (brandingId, template, filePath) {
    var deferred = Q.defer();

    fs.createReadStream(filePath).pipe(
        request(deferred, 'PUT', '/v2/brandings/' + brandingId + '/emails/' + template + '.json')
    );

    return deferred.promise;
};

SignaturitClient.prototype.getTemplates = function () {
    return requestWithDeferred('GET', '/v2/templates.json');
};

module.exports = SignaturitClient;
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

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

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

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

var React = require('react');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var Tinter = require('../../../Tinter');
var sdk = require('../../../index');

var room_colors = [
    // magic room default values courtesy of Ribot
    ["#76cfa6", "#eaf5f0"],
    ["#81bddb", "#eaf1f4"],
    ["#bd79cb", "#f3eaf5"],
    ["#c65d94", "#f5eaef"],
    ["#e55e5e", "#f5eaea"],
    ["#eca46f", "#f5eeea"],
    ["#dad658", "#f5f4ea"],
    ["#80c553", "#eef5ea"],
    ["#bb814e", "#eee8e3"],
    ["#595959", "#ececec"],
];

module.exports = React.createClass({
    displayName: 'RoomSettings',

    propTypes: {
        room: React.PropTypes.object.isRequired,
    },

    getInitialState: function() {
        // work out the initial color index
        var room_color_index = undefined;
        var color_scheme_event = this.props.room.getAccountData("m.room.color_scheme");
        if (color_scheme_event) {
            var color_scheme = color_scheme_event.getContent();
            if (color_scheme.primary_color) color_scheme.primary_color = color_scheme.primary_color.toLowerCase();
            if (color_scheme.secondary_color) color_scheme.secondary_color = color_scheme.secondary_color.toLowerCase();
            // XXX: we should validate these values
            for (var i = 0; i < room_colors.length; i++) {
                var room_color = room_colors[i];
                if (room_color[0] === color_scheme.primary_color &&
                    room_color[1] === color_scheme.secondary_color)
                {
                    room_color_index = i;
                    break;
                }
            }
            if (room_color_index === undefined) {
                // append the unrecognised colours to our palette
                room_color_index = room_colors.length;
                room_colors[room_color_index] = [ color_scheme.primary_color, color_scheme.secondary_color ];
            }
        }
        else {
            room_color_index = 0;
        }

        return {
            power_levels_changed: false,
            color_scheme_changed: false,
            color_scheme_index: room_color_index,
        };
    },

    getTopic: function() {
        return this.refs.topic.value;
    },

    getJoinRules: function() {
        return this.refs.is_private.checked ? "invite" : "public";
    },

    getHistoryVisibility: function() {
        return this.refs.share_history.checked ? "shared" : "invited";
    },

    getPowerLevels: function() {
        if (!this.state.power_levels_changed) return undefined;

        var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
        power_levels = power_levels.getContent();

        var new_power_levels = {
            ban: parseInt(this.refs.ban.value),
            kick: parseInt(this.refs.kick.value),
            redact: parseInt(this.refs.redact.value),
            invite: parseInt(this.refs.invite.value),
            events_default: parseInt(this.refs.events_default.value),
            state_default: parseInt(this.refs.state_default.value),
            users_default: parseInt(this.refs.users_default.value),
            users: power_levels.users,
            events: power_levels.events,
        };

        return new_power_levels;
    },

    onPowerLevelsChanged: function() {
        this.setState({
            power_levels_changed: true
        });
    },

    getColorScheme: function() {
        if (!this.state.color_scheme_changed) return undefined;

        return {
            primary_color: room_colors[this.state.color_scheme_index][0],
            secondary_color: room_colors[this.state.color_scheme_index][1],
        };
    },

    onColorSchemeChanged: function(index) {
        // preview what the user just changed the scheme to.
        Tinter.tint(room_colors[index][0], room_colors[index][1]);

        this.setState({
            color_scheme_changed: true,
            color_scheme_index: index,
        });
    },

    render: function() {
        var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');

        var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
        if (topic) topic = topic.getContent().topic;

        var join_rule = this.props.room.currentState.getStateEvents('m.room.join_rules', '');
        if (join_rule) join_rule = join_rule.getContent().join_rule;

        var history_visibility = this.props.room.currentState.getStateEvents('m.room.history_visibility', '');
        if (history_visibility) history_visibility = history_visibility.getContent().history_visibility;

        var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');

        var events_levels = power_levels.events || {};

        if (power_levels) {
            power_levels = power_levels.getContent();

            var ban_level = parseInt(power_levels.ban);
            var kick_level = parseInt(power_levels.kick);
            var redact_level = parseInt(power_levels.redact);
            var invite_level = parseInt(power_levels.invite || 0);
            var send_level = parseInt(power_levels.events_default || 0);
            var state_level = parseInt(power_levels.state_default || 0);
            var default_user_level = parseInt(power_levels.users_default || 0);

            if (power_levels.ban == undefined) ban_level = 50;
            if (power_levels.kick == undefined) kick_level = 50;
            if (power_levels.redact == undefined) redact_level = 50;

            var user_levels = power_levels.users || {};

            var user_id = MatrixClientPeg.get().credentials.userId;

            var current_user_level = user_levels[user_id];
            if (current_user_level == undefined) current_user_level = default_user_level;

            var power_level_level = events_levels["m.room.power_levels"];
            if (power_level_level == undefined) {
                power_level_level = state_level;
            }

            var can_change_levels = current_user_level >= power_level_level;
        } else {
            var ban_level = 50;
            var kick_level = 50;
            var redact_level = 50;
            var invite_level = 0;
            var send_level = 0;
            var state_level = 0;
            var default_user_level = 0;

            var user_levels = [];
            var events_levels = [];

            var current_user_level = 0;

            var power_level_level = 0;

            var can_change_levels = false;
        }

        var room_avatar_level = parseInt(power_levels.state_default || 0);
        if (events_levels['m.room.avatar'] !== undefined) {
            room_avatar_level = events_levels['m.room.avatar'];
        }
        var can_set_room_avatar = current_user_level >= room_avatar_level;

        var self = this;

        var room_colors_section =

Room Colour

                    {room_colors.map(function(room_color, i) {
                        var selected;
                        if (i === self.state.color_scheme_index) {
                            selected =

                        }
                        var boundClick = self.onColorSchemeChanged.bind(this, i)
                        return (

                                { selected }

                        );
                    })}

;

        var change_avatar;
        if (can_set_room_avatar) {
            change_avatar =

Room Icon

                    <ChangeAvatar room={this.props.room} />

;
        }

        var banned = this.props.room.getMembersWithMembership("ban");

        var events_levels_section;
        if (events_levels.length) {
            events_levels_section =

Event levels

                        {Object.keys(events_levels).map(function(event_type, i) {
                            return (

                                    {event_type}
                                    <input type="text" defaultValue={events_levels[event_type]} size="3" id={"mx_RoomSettings_event_"+i} disabled/>

                            );
                        })}

;
        }

        var banned_users_section;
        if (banned.length) {
            banned_users_section =

Banned users

                        {banned.map(function(member, i) {
                            return (

                                    {member.userId}

                            );
                        })}

;
        }

        return (

                <textarea className="mx_RoomSettings_description" placeholder="Topic" defaultValue={topic} ref="topic"/>

                <input type="checkbox" ref="is_private" defaultChecked={join_rule != "public"}/> Make this room private

                <input type="checkbox" ref="share_history" defaultChecked={history_visibility == "shared"}/> Share message history with new users

                <input type="checkbox" /> Encrypt room

                { room_colors_section }

Power levels

                        Ban level
                        <input type="text" defaultValue={ban_level} size="3" ref="ban" id="mx_RoomSettings_ban_level"
                            disabled={!can_change_levels || current_user_level < ban_level} onChange={this.onPowerLevelsChanged}/>

                        Kick level
                        <input type="text" defaultValue={kick_level} size="3" ref="kick" id="mx_RoomSettings_kick_level"
                            disabled={!can_change_levels || current_user_level < kick_level} onChange={this.onPowerLevelsChanged}/>

                        Redact level
                        <input type="text" defaultValue={redact_level} size="3" ref="redact" id="mx_RoomSettings_redact_level"
                            disabled={!can_change_levels || current_user_level < redact_level} onChange={this.onPowerLevelsChanged}/>

                        Invite level
                        <input type="text" defaultValue={invite_level} size="3" ref="invite" id="mx_RoomSettings_invite_level"
                            disabled={!can_change_levels || current_user_level < invite_level} onChange={this.onPowerLevelsChanged}/>

                        Send event level
                        <input type="text" defaultValue={send_level} size="3" ref="events_default" id="mx_RoomSettings_event_level"
                            disabled={!can_change_levels || current_user_level < send_level} onChange={this.onPowerLevelsChanged}/>

                        Set state level
                        <input type="text" defaultValue={state_level} size="3" ref="state_default" id="mx_RoomSettings_state_level"
                            disabled={!can_change_levels || current_user_level < state_level} onChange={this.onPowerLevelsChanged}/>

                        Default user level
                        <input type="text" defaultValue={default_user_level} size="3" ref="users_default"
                            id="mx_RoomSettings_user_level" disabled={!can_change_levels || current_user_level < default_user_level}
                            onChange={this.onPowerLevelsChanged}/>

User levels

                    {Object.keys(user_levels).map(function(user, i) {
                        return (

                                {user}
                                <input type="text" defaultValue={user_levels[user]} size="3" id={"mx_RoomSettings_user_"+i} disabled/>

                        );
                    })}

                { events_levels_section }
                { banned_users_section }
                { change_avatar }

        );
    }
});
<commit_msg>idiot - fix bind warnings<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

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

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

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

var React = require('react');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var Tinter = require('../../../Tinter');
var sdk = require('../../../index');

var room_colors = [
    // magic room default values courtesy of Ribot
    ["#76cfa6", "#eaf5f0"],
    ["#81bddb", "#eaf1f4"],
    ["#bd79cb", "#f3eaf5"],
    ["#c65d94", "#f5eaef"],
    ["#e55e5e", "#f5eaea"],
    ["#eca46f", "#f5eeea"],
    ["#dad658", "#f5f4ea"],
    ["#80c553", "#eef5ea"],
    ["#bb814e", "#eee8e3"],
    ["#595959", "#ececec"],
];

module.exports = React.createClass({
    displayName: 'RoomSettings',

    propTypes: {
        room: React.PropTypes.object.isRequired,
    },

    getInitialState: function() {
        // work out the initial color index
        var room_color_index = undefined;
        var color_scheme_event = this.props.room.getAccountData("m.room.color_scheme");
        if (color_scheme_event) {
            var color_scheme = color_scheme_event.getContent();
            if (color_scheme.primary_color) color_scheme.primary_color = color_scheme.primary_color.toLowerCase();
            if (color_scheme.secondary_color) color_scheme.secondary_color = color_scheme.secondary_color.toLowerCase();
            // XXX: we should validate these values
            for (var i = 0; i < room_colors.length; i++) {
                var room_color = room_colors[i];
                if (room_color[0] === color_scheme.primary_color &&
                    room_color[1] === color_scheme.secondary_color)
                {
                    room_color_index = i;
                    break;
                }
            }
            if (room_color_index === undefined) {
                // append the unrecognised colours to our palette
                room_color_index = room_colors.length;
                room_colors[room_color_index] = [ color_scheme.primary_color, color_scheme.secondary_color ];
            }
        }
        else {
            room_color_index = 0;
        }

        return {
            power_levels_changed: false,
            color_scheme_changed: false,
            color_scheme_index: room_color_index,
        };
    },

    getTopic: function() {
        return this.refs.topic.value;
    },

    getJoinRules: function() {
        return this.refs.is_private.checked ? "invite" : "public";
    },

    getHistoryVisibility: function() {
        return this.refs.share_history.checked ? "shared" : "invited";
    },

    getPowerLevels: function() {
        if (!this.state.power_levels_changed) return undefined;

        var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
        power_levels = power_levels.getContent();

        var new_power_levels = {
            ban: parseInt(this.refs.ban.value),
            kick: parseInt(this.refs.kick.value),
            redact: parseInt(this.refs.redact.value),
            invite: parseInt(this.refs.invite.value),
            events_default: parseInt(this.refs.events_default.value),
            state_default: parseInt(this.refs.state_default.value),
            users_default: parseInt(this.refs.users_default.value),
            users: power_levels.users,
            events: power_levels.events,
        };

        return new_power_levels;
    },

    onPowerLevelsChanged: function() {
        this.setState({
            power_levels_changed: true
        });
    },

    getColorScheme: function() {
        if (!this.state.color_scheme_changed) return undefined;

        return {
            primary_color: room_colors[this.state.color_scheme_index][0],
            secondary_color: room_colors[this.state.color_scheme_index][1],
        };
    },

    onColorSchemeChanged: function(index) {
        // preview what the user just changed the scheme to.
        Tinter.tint(room_colors[index][0], room_colors[index][1]);

        this.setState({
            color_scheme_changed: true,
            color_scheme_index: index,
        });
    },

    render: function() {
        var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');

        var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
        if (topic) topic = topic.getContent().topic;

        var join_rule = this.props.room.currentState.getStateEvents('m.room.join_rules', '');
        if (join_rule) join_rule = join_rule.getContent().join_rule;

        var history_visibility = this.props.room.currentState.getStateEvents('m.room.history_visibility', '');
        if (history_visibility) history_visibility = history_visibility.getContent().history_visibility;

        var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');

        var events_levels = power_levels.events || {};

        if (power_levels) {
            power_levels = power_levels.getContent();

            var ban_level = parseInt(power_levels.ban);
            var kick_level = parseInt(power_levels.kick);
            var redact_level = parseInt(power_levels.redact);
            var invite_level = parseInt(power_levels.invite || 0);
            var send_level = parseInt(power_levels.events_default || 0);
            var state_level = parseInt(power_levels.state_default || 0);
            var default_user_level = parseInt(power_levels.users_default || 0);

            if (power_levels.ban == undefined) ban_level = 50;
            if (power_levels.kick == undefined) kick_level = 50;
            if (power_levels.redact == undefined) redact_level = 50;

            var user_levels = power_levels.users || {};

            var user_id = MatrixClientPeg.get().credentials.userId;

            var current_user_level = user_levels[user_id];
            if (current_user_level == undefined) current_user_level = default_user_level;

            var power_level_level = events_levels["m.room.power_levels"];
            if (power_level_level == undefined) {
                power_level_level = state_level;
            }

            var can_change_levels = current_user_level >= power_level_level;
        } else {
            var ban_level = 50;
            var kick_level = 50;
            var redact_level = 50;
            var invite_level = 0;
            var send_level = 0;
            var state_level = 0;
            var default_user_level = 0;

            var user_levels = [];
            var events_levels = [];

            var current_user_level = 0;

            var power_level_level = 0;

            var can_change_levels = false;
        }

        var room_avatar_level = parseInt(power_levels.state_default || 0);
        if (events_levels['m.room.avatar'] !== undefined) {
            room_avatar_level = events_levels['m.room.avatar'];
        }
        var can_set_room_avatar = current_user_level >= room_avatar_level;

        var self = this;

        var room_colors_section =

Room Colour

                    {room_colors.map(function(room_color, i) {
                        var selected;
                        if (i === self.state.color_scheme_index) {
                            selected =

                        }
                        var boundClick = self.onColorSchemeChanged.bind(self, i)
                        return (

                                { selected }

                        );
                    })}

;

        var change_avatar;
        if (can_set_room_avatar) {
            change_avatar =

Room Icon

                    <ChangeAvatar room={this.props.room} />

;
        }

        var banned = this.props.room.getMembersWithMembership("ban");

        var events_levels_section;
        if (events_levels.length) {
            events_levels_section =

Event levels

                        {Object.keys(events_levels).map(function(event_type, i) {
                            return (

                                    {event_type}
                                    <input type="text" defaultValue={events_levels[event_type]} size="3" id={"mx_RoomSettings_event_"+i} disabled/>

                            );
                        })}

;
        }

        var banned_users_section;
        if (banned.length) {
            banned_users_section =

Banned users

                        {banned.map(function(member, i) {
                            return (

                                    {member.userId}

                            );
                        })}

;
        }

        return (

                <textarea className="mx_RoomSettings_description" placeholder="Topic" defaultValue={topic} ref="topic"/>

                <input type="checkbox" ref="is_private" defaultChecked={join_rule != "public"}/> Make this room private

                <input type="checkbox" ref="share_history" defaultChecked={history_visibility == "shared"}/> Share message history with new users

                <input type="checkbox" /> Encrypt room

                { room_colors_section }

Power levels

                        Ban level
                        <input type="text" defaultValue={ban_level} size="3" ref="ban" id="mx_RoomSettings_ban_level"
                            disabled={!can_change_levels || current_user_level < ban_level} onChange={this.onPowerLevelsChanged}/>

                        Kick level
                        <input type="text" defaultValue={kick_level} size="3" ref="kick" id="mx_RoomSettings_kick_level"
                            disabled={!can_change_levels || current_user_level < kick_level} onChange={this.onPowerLevelsChanged}/>

                        Redact level
                        <input type="text" defaultValue={redact_level} size="3" ref="redact" id="mx_RoomSettings_redact_level"
                            disabled={!can_change_levels || current_user_level < redact_level} onChange={this.onPowerLevelsChanged}/>

                        Invite level
                        <input type="text" defaultValue={invite_level} size="3" ref="invite" id="mx_RoomSettings_invite_level"
                            disabled={!can_change_levels || current_user_level < invite_level} onChange={this.onPowerLevelsChanged}/>

                        Send event level
                        <input type="text" defaultValue={send_level} size="3" ref="events_default" id="mx_RoomSettings_event_level"
                            disabled={!can_change_levels || current_user_level < send_level} onChange={this.onPowerLevelsChanged}/>

                        Set state level
                        <input type="text" defaultValue={state_level} size="3" ref="state_default" id="mx_RoomSettings_state_level"
                            disabled={!can_change_levels || current_user_level < state_level} onChange={this.onPowerLevelsChanged}/>

                        Default user level
                        <input type="text" defaultValue={default_user_level} size="3" ref="users_default"
                            id="mx_RoomSettings_user_level" disabled={!can_change_levels || current_user_level < default_user_level}
                            onChange={this.onPowerLevelsChanged}/>

User levels

                    {Object.keys(user_levels).map(function(user, i) {
                        return (

                                {user}
                                <input type="text" defaultValue={user_levels[user]} size="3" id={"mx_RoomSettings_user_"+i} disabled/>

                        );
                    })}

                { events_levels_section }
                { banned_users_section }
                { change_avatar }

        );
    }
});
<|endoftext|>