<commit_before>/**
 * sidebar 相关功能
 */
define( function ( require ) {
    var
        $ = require( "jquery" ),
        layer = window.layer,
        App = require( "./app" ),
        namespace = "pkui.sidebar",
        ArtTemplate = require( "artTemplate" )
    ;

    AppSidebar.prototype.defaults = {
        menuUrl: "",
        oftenUsedUrl: "",
        recentUsedUrl: "",
        saveUsedMenuUrl: "",
        toggleSelector: "",
        sidebarSelector: "",
        template:
        '
'
        +   '
常用功能
'
        +   '    {{each oftenUsedMenuList }}'
        +   '        {{if $index < 6}}'
        +   '
'
        +   '             {{$value.menuName}}{{$value.count}}
'
        +   '        {{/if}}'
        +   '    {{/each}}'
        +   '
'
        +   '
'
        +   '
最近使用
'
        +   '    {{each recentUsedMenuList }}'
        +   '        {{if $index < 8}}'
        +   '
'
        +   '             {{$value.menuName}}
'
        +   '        {{/if}}'
        +   '    {{/each}}'
        +   '
'

    };

    function AppSidebar( opts ) {
        this.opts = $.extend( true, {}, this.defaults, opts );
        this.init();
    }

    AppSidebar.prototype.init = function () {
        var _this = this;
        this.render();
        this.bind();

        this.templateRender = ArtTemplate.compile( this.opts.template );

        this.getData( function () {
            _this.create();
        } );
    };

    AppSidebar.prototype.render = function () {
        this.$toggle = $( this.opts.toggleSelector );
        this.$sidebar = $( this.opts.sidebarSelector );
    };

    AppSidebar.prototype.bind = function () {
        var _this = this;

        this.$toggle.on( "click." + namespace, function () {
            var $sidebar = _this.$sidebar;

            if ( $sidebar.is( ".app-sidebar-showed" ) ) {
                _this.hide();
            }
            else {
                _this.show();
            }
        } );

        this.$sidebar.on( "click." + namespace, ".use-group-item", function () {

            var $item = $( this );

            // 开启 窗口
            new App(null, {
                "icon": $item.data( "icon" ),
                "title": $item.data( "menuName" ),
                "src": "./tpl/system/index.html",
                "menuId": $item.data( "menuId" ),
                "mode": "default"})
        } );

        $( document ).on( "inited.app", function ( event, menuId ) {
            if ( ! menuId ) {
                return;
            }
            _this.redraw( menuId );
        } );

    };

    /**
     * 1. 菜单数据
     * 2. 常用菜单数据
     * 3. 最近使用菜单数据
     */
    AppSidebar.prototype.getData = function ( callback ) {
        var
            _this = this
        ;
        this._getData( this.opts.menuUrl, function ( jsonResult ) {
            _this.sysMenuList = jsonResult.data;
            refresh();
        } );
        this._getData( this.opts.oftenUsedUrl, function ( jsonResult ) {
            _this.oftenUsedMenuList = jsonResult.data;
            refresh();
        } );
        this._getData( this.opts.recentUsedUrl, function ( jsonResult ) {
            _this.recentUsedMenuList = jsonResult.data;
            refresh();
        } );

        function refresh() {

            if ( _this.hasOwnProperty( "sysMenuList" ) &&
                 _this.hasOwnProperty( "oftenUsedMenuList" ) &&
                 _this.hasOwnProperty( "recentUsedMenuList" ) ) {

                callback && callback();
                _this.isDataReady = true;
            }
        }
    };

    AppSidebar.prototype._getData = function ( url, callback ) {
        $.ajax( {
            url: url
        } ).done( function ( jsonResult ) {
            if ( !jsonResult ||  jsonResult.success === false ) {
                layer.alert( ( jsonResult && jsonResult.message ) || "【应用侧边栏】请求数据失败：服务器内部错误！", { icon: 2 } );
                return;
            }
            callback && callback( jsonResult );
        } ).fail( function () {
            // 提示网络错误
            layer.alert( '【应用侧边栏】请求数据失败：网络错误/登陆失效。', { icon: 0 } );
        } ).always( function () {
            // do something
        } );
    };

    AppSidebar.prototype.show = function () {
        var $sidebar = this.$sidebar;
        $sidebar.stop().animate( { left: 0 }, function () {
            $sidebar.addClass( "app-sidebar-showed" )
        } );
    };
    AppSidebar.prototype.hide = function () {
        var $sidebar = this.$sidebar;
        $sidebar.stop().animate( { left: -$sidebar.width() }, function () {
            $sidebar.removeClass( "app-sidebar-showed" );
        } );
    };

    AppSidebar.prototype.create = function () {
        var
            html
        ;

        this._fmtData();

        html = this.templateRender( {
            oftenUsedMenuList: this.oftenUsedMenuList,
            recentUsedMenuList: this.recentUsedMenuList
        } );

        this.$sidebar.html( html );
    };

    AppSidebar.prototype._fmtData = function () {
        var temp,
            _this = this,
            lastPos,
            str,
            menuId, sysMenu
        ;

        if ( this.sysMenuList ) {
            temp = {};
            $.each( this.sysMenuList, function ( index, sysMenu ) {
                temp[ sysMenu[ "menuId" ] ] = {
                    menuId: sysMenu[ "menuId" ],
                    menuName: sysMenu[ "menuName" ],
                    icon: sysMenu[ "icon" ]
                }
            } );
            this.sysMenuList = temp;
        }
        if ( this.oftenUsedMenuList &&
             typeof this.oftenUsedMenuList === "string" &&
             this.oftenUsedMenuList.indexOf( "," ) !== -1 ) {
            try {
                temp = [];
                this.oftenUsedMenuList = this.oftenUsedMenuList.replace( /'/g, '"');
                this.oftenUsedMenuList = $.parseJSON( this.oftenUsedMenuList );
            } catch ( e ) {
                lastPos = _this.oftenUsedMenuList.lastIndexOf( "," );
                str = _this.oftenUsedMenuList.substring( 0, lastPos ) + "}";
                _this.oftenUsedMenuList = $.parseJSON( str );
            }

            for ( menuId in _this.oftenUsedMenuList ) {
                if ( _this.oftenUsedMenuList.hasOwnProperty( menuId ) ) {
                    sysMenu = _this.sysMenuList[ menuId ];
                    sysMenu.count = _this.oftenUsedMenuList[ menuId ];
                    temp.push( sysMenu );
                }
            }
            _this.oftenUsedMenuList = temp;

        }
        if ( this.recentUsedMenuList &&
             typeof this.recentUsedMenuList === "string" &&
             this.recentUsedMenuList.indexOf( "," ) !== -1 ) {

            temp = [];

            this.recentUsedMenuList = this.recentUsedMenuList.split( "," );

            $.each( this.recentUsedMenuList, function ( index, menuId ) {
                sysMenu = _this.sysMenuList[ menuId ];
                temp.push( sysMenu );
            } );

            this.recentUsedMenuList = temp;

        }
    };

    AppSidebar.prototype.redraw = function ( menuId ) {
        var
            _this = this,
            sysMenu,
            html
        ;
        /* 最近使用 */
        // 1. 若存在列表中，则删除
        if ( this.isInRecentUsedList( menuId ) ) {
            $.each( this.recentUsedMenuList, function ( index, item ) {
                if ( item && item.menuId == menuId ) {
                    _this.recentUsedMenuList.splice( index, 1 );
                    return true;
                }
            } );
        }
        // 2. 将其添加列表第一位
        this.recentUsedMenuList.unshift( this.sysMenuList[ menuId ] );

        /* 最常使用 */
        // 1. 若存在，则自增，并排序
        if ( this.isInOftenUsedList( menuId ) ) {
            $.each( this.oftenUsedMenuList, function ( index, item ) {
                if ( item && item.menuId == menuId ) {
                    item.count++;
                    if ( index !== 0 && item.count > _this.oftenUsedMenuList[ index - 1 ].count ) {
                        _this.oftenUsedMenuList.splice( index, 1 );
                        _this.oftenUsedMenuList.splice( index - 1, 0, item );
                    }
                    return true;
                }
            } );
        }
        // 2. 若不存在，则追加到末尾
        else {
            sysMenu = this.sysMenuList[ menuId ];
            sysMenu.count = 1;
            this.oftenUsedMenuList.push( sysMenu );
        }

        // 重新绘制
        html = this.templateRender( {
            oftenUsedMenuList: this.oftenUsedMenuList,
            recentUsedMenuList: this.recentUsedMenuList
        } );

        this.$sidebar.html( html );
    };

    AppSidebar.prototype.isInOftenUsedList =  function ( menuId, list ) {
        var isExist = false;
        list = list || this.oftenUsedMenuList;
        $.each( list, function ( index, item ) {
            if ( item.menuId == menuId ) {
                isExist = true;
                return true;
            }
        } );
        return isExist;
    };
    AppSidebar.prototype.isInRecentUsedList =  function ( menuId ) {
        return this.isInOftenUsedList( menuId, this.recentUsedMenuList );
    };
    return AppSidebar;
} );<commit_msg>【desktop】common/app-sidebar.js：添加注释<commit_after>/**
 * sidebar 相关功能
 */
define( function ( require ) {
    var
        $ = require( "jquery" ),
        layer = window.layer,
        App = require( "./app" ),
        namespace = "pkui.sidebar",
        ArtTemplate = require( "artTemplate" )
    ;

    AppSidebar.prototype.defaults = {
        menuUrl: "",
        oftenUsedUrl: "",
        recentUsedUrl: "",
        saveUsedMenuUrl: "",
        toggleSelector: "",
        sidebarSelector: "",
        template:
        '
'
        +   '
常用功能
'
        +   '    {{each oftenUsedMenuList }}'
        +   '        {{if $index < 6}}'
        +   '
'
        +   '             {{$value.menuName}}{{$value.count}}
'
        +   '        {{/if}}'
        +   '    {{/each}}'
        +   '
'
        +   '
'
        +   '
最近使用
'
        +   '    {{each recentUsedMenuList }}'
        +   '        {{if $index < 8}}'
        +   '
'
        +   '             {{$value.menuName}}
'
        +   '        {{/if}}'
        +   '    {{/each}}'
        +   '
'

    };

    /**
     * 构造函数
     * @param opts
     * @constructor
     */
    function AppSidebar( opts ) {
        this.opts = $.extend( true, {}, this.defaults, opts );
        this.init();
    }

    /**
     * 初始化方法
     */
    AppSidebar.prototype.init = function () {
        var _this = this;
        this.render();
        this.bind();

        this.templateRender = ArtTemplate.compile( this.opts.template );

        this.getData( function () {
            _this._fmtData();
            _this.draw();
        } );
    };

    /**
     * 准备数据
     */
    AppSidebar.prototype.render = function () {
        this.$toggle = $( this.opts.toggleSelector );
        this.$sidebar = $( this.opts.sidebarSelector );
    };

    /**
     * 绑定事件
     */
    AppSidebar.prototype.bind = function () {
        var _this = this;

        // 点击开关时，显示/隐藏 侧边栏
        this.$toggle.on( "click." + namespace, function () {
            var $sidebar = _this.$sidebar;

            if ( $sidebar.is( ".app-sidebar-showed" ) ) {
                _this.hide();
            }
            else {
                _this.show();
            }
        } );

        // 点击app条目，打开app
        this.$sidebar.on( "click." + namespace, ".use-group-item", function () {

            var $item = $( this );

            // 开启 窗口
            new App(null, {
                "icon": $item.data( "icon" ),
                "title": $item.data( "menuName" ),
                "src": "./tpl/system/index.html",
                "menuId": $item.data( "menuId" ),
                "mode": "default"})
        } );

        // 监听 在 $document 上触发的 inited.app 事件
        $( document ).on( "inited.app", function ( event, menuId ) {
            if ( ! menuId ) {
                return;
            }
            _this.redraw( menuId );
        } );

    };

    /**
     * 获取数据
     * 1. 菜单数据
     * 2. 常用菜单数据
     * 3. 最近使用菜单数据
     */
    AppSidebar.prototype.getData = function ( callback ) {
        var
            _this = this
        ;
        this._getData( this.opts.menuUrl, function ( jsonResult ) {
            _this.sysMenuList = jsonResult.data;
            refresh();
        } );
        this._getData( this.opts.oftenUsedUrl, function ( jsonResult ) {
            _this.oftenUsedMenuList = jsonResult.data;
            refresh();
        } );
        this._getData( this.opts.recentUsedUrl, function ( jsonResult ) {
            _this.recentUsedMenuList = jsonResult.data;
            refresh();
        } );

        function refresh() {

            if ( _this.hasOwnProperty( "sysMenuList" ) &&
                 _this.hasOwnProperty( "oftenUsedMenuList" ) &&
                 _this.hasOwnProperty( "recentUsedMenuList" ) ) {

                callback && callback();
                _this.isDataReady = true;
            }
        }
    };

    /**
     * 获取数据的公共方法
     * @param url
     * @param callback
     * @private
     */
    AppSidebar.prototype._getData = function ( url, callback ) {
        $.ajax( {
            url: url
        } ).done( function ( jsonResult ) {
            if ( !jsonResult ||  jsonResult.success === false ) {
                layer.alert( ( jsonResult && jsonResult.message ) || "【应用侧边栏】请求数据失败：服务器内部错误！", { icon: 2 } );
                return;
            }
            callback && callback( jsonResult );
        } ).fail( function () {
            // 提示网络错误
            layer.alert( '【应用侧边栏】请求数据失败：网络错误/登陆失效。', { icon: 0 } );
        } ).always( function () {
            // do something
        } );
    };

    /**
     * 显示侧边栏
     */
    AppSidebar.prototype.show = function () {
        var $sidebar = this.$sidebar;
        $sidebar.stop().animate( { left: 0 }, function () {
            $sidebar.addClass( "app-sidebar-showed" )
        } );
    };
    /**
     * 隐藏侧边栏
     */
    AppSidebar.prototype.hide = function () {
        var $sidebar = this.$sidebar;
        $sidebar.stop().animate( { left: -$sidebar.width() }, function () {
            $sidebar.removeClass( "app-sidebar-showed" );
        } );
    };

    /**
     * 绘制侧边栏的内容
     */
    AppSidebar.prototype.draw = function () {
        var
            html
        ;

        html = this.templateRender( {
            oftenUsedMenuList: this.oftenUsedMenuList,
            recentUsedMenuList: this.recentUsedMenuList
        } );

        this.$sidebar.html( html );
    };

    /**
     * 格式化数据
     * @private
     */
    AppSidebar.prototype._fmtData = function () {
        var temp,
            _this = this,
            lastPos,
            str,
            menuId, sysMenu
        ;

        if ( this.sysMenuList ) {
            temp = {};
            $.each( this.sysMenuList, function ( index, sysMenu ) {
                temp[ sysMenu[ "menuId" ] ] = {
                    menuId: sysMenu[ "menuId" ],
                    menuName: sysMenu[ "menuName" ],
                    icon: sysMenu[ "icon" ]
                }
            } );
            this.sysMenuList = temp;
        }
        if ( this.oftenUsedMenuList &&
             typeof this.oftenUsedMenuList === "string" &&
             this.oftenUsedMenuList.indexOf( "," ) !== -1 ) {
            try {
                temp = [];
                this.oftenUsedMenuList = this.oftenUsedMenuList.replace( /'/g, '"');
                this.oftenUsedMenuList = $.parseJSON( this.oftenUsedMenuList );
            } catch ( e ) {
                lastPos = _this.oftenUsedMenuList.lastIndexOf( "," );
                str = _this.oftenUsedMenuList.substring( 0, lastPos ) + "}";
                _this.oftenUsedMenuList = $.parseJSON( str );
            }

            for ( menuId in _this.oftenUsedMenuList ) {
                if ( _this.oftenUsedMenuList.hasOwnProperty( menuId ) ) {
                    sysMenu = _this.sysMenuList[ menuId ];
                    sysMenu.count = _this.oftenUsedMenuList[ menuId ];
                    temp.push( sysMenu );
                }
            }
            _this.oftenUsedMenuList = temp;

        }
        if ( this.recentUsedMenuList &&
             typeof this.recentUsedMenuList === "string" &&
             this.recentUsedMenuList.indexOf( "," ) !== -1 ) {

            temp = [];

            this.recentUsedMenuList = this.recentUsedMenuList.split( "," );

            $.each( this.recentUsedMenuList, function ( index, menuId ) {
                sysMenu = _this.sysMenuList[ menuId ];
                temp.push( sysMenu );
            } );

            this.recentUsedMenuList = temp;

        }
    };

    /**
     * 当打开一个新的app时，重新绘制侧边栏内容
     * @param menuId
     */
    AppSidebar.prototype.redraw = function ( menuId ) {
        var
            _this = this,
            sysMenu
        ;
        /* 最近使用 */
        // 1. 若存在列表中，则删除
        if ( this.isInRecentUsedList( menuId ) ) {
            $.each( this.recentUsedMenuList, function ( index, item ) {
                if ( item && item.menuId == menuId ) {
                    _this.recentUsedMenuList.splice( index, 1 );
                    return true;
                }
            } );
        }
        // 2. 将其添加列表第一位
        this.recentUsedMenuList.unshift( this.sysMenuList[ menuId ] );

        /* 最常使用 */
        // 1. 若存在，则自增，并排序
        if ( this.isInOftenUsedList( menuId ) ) {
            $.each( this.oftenUsedMenuList, function ( index, item ) {
                if ( item && item.menuId == menuId ) {
                    item.count++;
                    if ( index !== 0 && item.count > _this.oftenUsedMenuList[ index - 1 ].count ) {
                        _this.oftenUsedMenuList.splice( index, 1 );
                        _this.oftenUsedMenuList.splice( index - 1, 0, item );
                    }
                    return true;
                }
            } );
        }
        // 2. 若不存在，则追加到末尾
        else {
            sysMenu = this.sysMenuList[ menuId ];
            sysMenu.count = 1;
            this.oftenUsedMenuList.push( sysMenu );
        }

        // 重新绘制
        this.draw();
    };

    AppSidebar.prototype.isInOftenUsedList =  function ( menuId, list ) {
        var isExist = false;
        list = list || this.oftenUsedMenuList;
        $.each( list, function ( index, item ) {
            if ( item.menuId == menuId ) {
                isExist = true;
                return true;
            }
        } );
        return isExist;
    };
    AppSidebar.prototype.isInRecentUsedList =  function ( menuId ) {
        return this.isInOftenUsedList( menuId, this.recentUsedMenuList );
    };
    return AppSidebar;
} );<|endoftext|>


<commit_before>module.exports = {

//---------------------------------------------------------------------
// Action Name
//
// This is the name of the action displayed in the editor.
//---------------------------------------------------------------------

name: "Store Bot Client Info",

//---------------------------------------------------------------------
// Action Section
//
// This is the section the action will fall into.
//---------------------------------------------------------------------

section: "Bot Client Control",

//---------------------------------------------------------------------
// Action Subtitle
//
// This function generates the subtitle displayed next to the name.
//---------------------------------------------------------------------

subtitle: function(data) {
const info = ['Uptime in milliseconds', 'Ready at?', 'Ping', 'Guild amount', 'User amount', 'Ping rounded', 'Uptime in seconds', 'Uptime in minutes', 'Bots token', 'Voice connections amount', 'Total Amount of Channels', 'Total Amount of Emojis', 'Bot\'s Previous Pings', 'Uptime in Days', 'Uptime in Days (Rounded)', 'Memory (RAM) Usage', 'Bot Guilds Objects', 'Bot Guilds Names', 'Bot Guilds IDs', 'Bot Current Prefix', 'Bot Client ID'];
	return `Bot Client - ${info[parseInt(data.info)]}`;
},

//---------------------------------------------------------------------
	 // DBM Mods Manager Variables (Optional but nice to have!)
	 //
	 // These are variables that DBM Mods Manager uses to show information
	 // about the mods for people to see in the list.
	 //---------------------------------------------------------------------

	 // Who made the mod (If not set, defaults to "DBM Mods")
	 author: "Lasse, EliteArtz and EGGSY",

	 // The version of the mod (Defaults to 1.0.0)
	 version: "1.8.4",

	 // A short description to show on the mod line for this mod (Must be on a single line)
	 short_description: "Stores Bot Information like Ping, Total Members or Guilds...",

	 // If it depends on any other mods by name, ex: WrexMODS if the mod uses something from WrexMods

	 //---------------------------------------------------------------------

//---------------------------------------------------------------------
// Action Storage Function
//
// Stores the relevant variable info for the editor.
//---------------------------------------------------------------------

variableStorage: function(data, varType) {
	const type = parseInt(data.storage);
	if(type !== varType) return;
	const info = parseInt(data.info);
	let dataType = 'Unknown Type';
	switch(info) {
		case 0:
			dataType = "Number";
			break;
		case 1:
			dataType = "Date";
			break;
		case 2:
			dataType = "Number";
			break;
		case 3:
			dataType = "Number";
			break;
		case 4:
			dataType = "Number";
			break;
		case 5:
			dataType = "Number";
			break;
		case 6:
			dataType = "Number";
			break;
		case 7:
			dataType = "Number";
			break;
		case 8:
			dataType = "Token";
			break;
		case 9:
			dataType = "Number";
			break;
	}
	return ([data.varName2, dataType]);
},

//---------------------------------------------------------------------
// Action Fields
//
// These are the fields for the action. These fields are customized
// by creating elements with corresponding IDs in the HTML. These
// are also the names of the fields stored in the action's JSON data.
//---------------------------------------------------------------------

fields: ["info", "storage", "varName2"],

//---------------------------------------------------------------------
// Command HTML
//
// This function returns a string containing the HTML used for
// editting actions.
//
// The "isEvent" parameter will be true if this action is being used
// for an event. Due to their nature, events lack certain information,
// so edit the HTML to reflect this.
//
// The "data" parameter stores constants for select elements to use.
// Each is an array: index 0 for commands, index 1 for events.
// The names are: sendTargets, members, roles, channels,
//                messages, servers, variables
//---------------------------------------------------------------------

html: function(isEvent, data) {
	return `

			Mod Info:

			Created by EliteArtz, EGGSY and Lasse!

	Source Info:

	<select id="info" class="round">
		<option value="0">Uptime in milliseconds</option>
		<option value="1">Ready at</option>
		<option value="2">Ping</option>
		<option value="3">Total amount of guilds</option>
		<option value="4">Total amount of users</option>
		<option value="5">Ping rounded</option>
		<option value="6">Uptime in seconds</option>
		<option value="7">Uptime in minutes</option>
		<option value="8">Bots Token</option>
		<option value="9">Total Voice connections</option>
		<option value="10">Total Amount of Channels</option>
		<option value="11">Total Amount of Emojis</option>
		<option value="12">Bot's Previous Pings</option>
		<option value="13">Uptime in Days</option>
		<option value="14">Uptime in Days (Rounded)</option>
		<option value="15">Memory (RAM) Usage</option>
		<option value="16" selected>Bot Guilds Objects</option>
		<option value="17">Bot Guilds Names</option>
		<option value="18">Bot Guilds IDs</option>
		<option value="19">Bot Current Prefix</option>
		<option value="20">Bot Client ID</option>
	</select>

		Store In:

		<select id="storage" class="round">
			${data.variables[1]}
		</select>

		Variable Name:

		<input id="varName2" class="round" type="text">

`
},

//---------------------------------------------------------------------
// Action Editor Init Code
//
// When the HTML is first applied to the action editor, this code
// is also run. This helps add modifications or setup reactionary
// functions for the DOM elements.
//---------------------------------------------------------------------

init: function() {
},

//---------------------------------------------------------------------
// Action Bot Function
//
// This is the function for the action within the Bot's Action class.
// Keep in mind event calls won't have access to the "msg" parameter,
// so be sure to provide checks for variable existance.
//---------------------------------------------------------------------

action: function(cache) {
    const botClient = this.getDBM().Bot.bot;
    const dibiem = this.getDBM();
	const data = cache.actions[cache.index];
	const info = parseInt(data.info);
	if(!botClient) {
		this.callNextAction(cache);
		return;
	}
	switch(info) {
		case 0:
			result = botClient.uptime + 'ms';
			break;
		case 1:
			result = botClient.readyAt;
			break;
		case 2:
			result = botClient.ping;
			break;
		case 3:
			result = botClient.guilds.array().length;
			break;
		case 4:
			result = botClient.users.array().length;
			break;
		case 5:
			result = Math.round(botClient.ping);
			break;
		case 6:
			result = Math.floor(botClient.uptime/1000) + 's';
			break;
		case 7:
			result = Math.floor(botClient.uptime/1000/60) + 'm';
			break;
		case 8:
			result = botClient.token;
			break;
		case 9:
			result = botClient.voiceConnections.array().length;
			break;
		case 10:
			result = botClient.channels.size;
			break;
		case 11:
			result = botClient.emojis.size;
			break;
		case 12:
			result = botClient.pings;
			break;
		case 13:
			result = botClient.uptime/msToDay;
			break;
		case 14:
			result = Math.floor(botClient.uptime/msToDay);
			break;
		case 15:
			result = "%" + ((process.memoryUsage().heapUsed / 1024) / 1024).toFixed(2);
			break;
		case 16:
		    result = botClient.guilds;
		    break;
		case 17:
		    result = botClient.guilds.array();
		    break;
		case 18:
		    result = botClient.guilds.map(guilds => guilds.id);
            break;
        case 19:
            result = dibiem.Files.data.settings.tag;
            break;
        case 20:
            result = dibiem.Files.data.settings.client;
            break;
		default:
		break;
	}
	if(result !== undefined) {
		const storage = parseInt(data.storage);
		const varName2 = this.evalMessage(data.varName2, cache);
		this.storeValue(result, storage, varName2, cache);
	}
	this.callNextAction(cache);
},

//---------------------------------------------------------------------
// Action Bot Mod
//
// Upon initialization of the bot, this code is run. Using the bot's
// DBM namespace, one can add/modify existing functions if necessary.
// In order to reduce conflictions between mods, be sure to alias
// functions you wish to overwrite.
//---------------------------------------------------------------------

mod: function(DBM) {
}

}; // End of module
<commit_msg>Beautified Store Bot Client Infos code<commit_after>module.exports = {

//---------------------------------------------------------------------
// Action Name
//
// This is the name of the action displayed in the editor.
//---------------------------------------------------------------------

name: "Store Bot Client Info",

//---------------------------------------------------------------------
// Action Section
//
// This is the section the action will fall into.
//---------------------------------------------------------------------

section: "Bot Client Control",

//---------------------------------------------------------------------
// Action Subtitle
//
// This function generates the subtitle displayed next to the name.
//---------------------------------------------------------------------

subtitle: function(data) {
const info = ['Uptime in milliseconds', 'Ready at?', 'Ping', 'Guild amount', 'User amount', 'Ping rounded', 'Uptime in seconds', 'Uptime in minutes', 'Bots token', 'Voice connections amount', 'Total Amount of Channels', 'Total Amount of Emojis', 'Bot\'s Previous Pings', 'Uptime in Days', 'Uptime in Days (Rounded)', 'Memory (RAM) Usage', 'Bot Guilds Objects', 'Bot Guilds Names', 'Bot Guilds IDs', 'Bot Current Prefix', 'Bot Client ID'];
	return `Bot Client - ${info[parseInt(data.info)]}`;
},

//---------------------------------------------------------------------
	 // DBM Mods Manager Variables (Optional but nice to have!)
	 //
	 // These are variables that DBM Mods Manager uses to show information
	 // about the mods for people to see in the list.
	 //---------------------------------------------------------------------

	 // Who made the mod (If not set, defaults to "DBM Mods")
	 author: "Lasse, EliteArtz and EGGSY",

	 // The version of the mod (Defaults to 1.0.0)
	 version: "1.8.4",

	 // A short description to show on the mod line for this mod (Must be on a single line)
	 short_description: "Stores Bot Information like Ping, Total Members or Guilds...",

	 // If it depends on any other mods by name, ex: WrexMODS if the mod uses something from WrexMods

	 //---------------------------------------------------------------------

//---------------------------------------------------------------------
// Action Storage Function
//
// Stores the relevant variable info for the editor.
//---------------------------------------------------------------------

variableStorage: function(data, varType) {
	const type = parseInt(data.storage);
	if(type !== varType) return;
	const info = parseInt(data.info);
	let dataType = 'Unknown Type';
	switch(info) {
		case 0:
			dataType = "Number";
			break;
		case 1:
			dataType = "Date";
			break;
		case 2:
			dataType = "Number";
			break;
		case 3:
			dataType = "Number";
			break;
		case 4:
			dataType = "Number";
			break;
		case 5:
			dataType = "Number";
			break;
		case 6:
			dataType = "Number";
			break;
		case 7:
			dataType = "Number";
			break;
		case 8:
			dataType = "Token";
			break;
		case 9:
			dataType = "Number";
			break;
	}
	return ([data.varName2, dataType]);
},

//---------------------------------------------------------------------
// Action Fields
//
// These are the fields for the action. These fields are customized
// by creating elements with corresponding IDs in the HTML. These
// are also the names of the fields stored in the action's JSON data.
//---------------------------------------------------------------------

fields: ["info", "storage", "varName2"],

//---------------------------------------------------------------------
// Command HTML
//
// This function returns a string containing the HTML used for
// editting actions.
//
// The "isEvent" parameter will be true if this action is being used
// for an event. Due to their nature, events lack certain information,
// so edit the HTML to reflect this.
//
// The "data" parameter stores constants for select elements to use.
// Each is an array: index 0 for commands, index 1 for events.
// The names are: sendTargets, members, roles, channels,
//                messages, servers, variables
//---------------------------------------------------------------------

html: function(isEvent, data) {
	return `

			Mod Info:

			Created by EliteArtz, EGGSY and Lasse!

	Source Info:

	<select id="info" class="round">
		<option value="0">Uptime in milliseconds</option>
		<option value="1">Ready at</option>
		<option value="2">Ping</option>
		<option value="3">Total amount of guilds</option>
		<option value="4">Total amount of users</option>
		<option value="5">Ping rounded</option>
		<option value="6">Uptime in seconds</option>
		<option value="7">Uptime in minutes</option>
		<option value="8">Bots Token</option>
		<option value="9">Total Voice connections</option>
		<option value="10">Total Amount of Channels</option>
		<option value="11">Total Amount of Emojis</option>
		<option value="12">Bot's Previous Pings</option>
		<option value="13">Uptime in Days</option>
		<option value="14">Uptime in Days (Rounded)</option>
		<option value="15">Memory (RAM) Usage</option>
		<option value="16">Bot Guilds Objects</option>
		<option value="17">Bot Guilds Names</option>
		<option value="18">Bot Guilds IDs</option>
		<option value="19">Bot Current Prefix</option>
		<option value="20">Bot Client ID</option>
	</select>

		Store In:

		<select id="storage" class="round">
			${data.variables[1]}
		</select>

		Variable Name:

		<input id="varName2" class="round" type="text">

`
},

//---------------------------------------------------------------------
// Action Editor Init Code
//
// When the HTML is first applied to the action editor, this code
// is also run. This helps add modifications or setup reactionary
// functions for the DOM elements.
//---------------------------------------------------------------------

init: function() {
},

//---------------------------------------------------------------------
// Action Bot Function
//
// This is the function for the action within the Bot's Action class.
// Keep in mind event calls won't have access to the "msg" parameter,
// so be sure to provide checks for variable existance.
//---------------------------------------------------------------------

action: function(cache) {
  const botClient = this.getDBM().Bot.bot;
  const dibiem = this.getDBM(); //EliteArtz... really????
	const data = cache.actions[cache.index];
	const info = parseInt(data.info);
	if(!botClient) {
		this.callNextAction(cache);
		return;
	}
	switch(info) {
		case 0:
			result = botClient.uptime + 'ms';
			break;
		case 1:
			result = botClient.readyAt;
			break;
		case 2:
			result = botClient.ping;
			break;
		case 3:
			result = botClient.guilds.array().length;
			break;
		case 4:
			result = botClient.users.array().length;
			break;
		case 5:
			result = Math.round(botClient.ping);
			break;
		case 6:
			result = Math.floor(botClient.uptime/1000) + 's';
			break;
		case 7:
			result = Math.floor(botClient.uptime/1000/60) + 'm';
			break;
		case 8:
			result = botClient.token;
			break;
		case 9:
			result = botClient.voiceConnections.array().length;
			break;
		case 10:
			result = botClient.channels.size;
			break;
		case 11:
			result = botClient.emojis.size;
			break;
		case 12:
			result = botClient.pings;
			break;
		case 13:
			result = botClient.uptime/msToDay;
			break;
		case 14:
			result = Math.floor(botClient.uptime/msToDay);
			break;
		case 15:
			result = "%" + ((process.memoryUsage().heapUsed / 1024) / 1024).toFixed(2);
			break;
		case 16:
		    result = botClient.guilds;
		    break;
		case 17:
		    result = botClient.guilds.array();
		    break;
		case 18:
		    result = botClient.guilds.map(guilds => guilds.id);
            break;
        case 19:
            result = dibiem.Files.data.settings.tag;
            break;
        case 20:
            result = dibiem.Files.data.settings.client;
            break;
		default:
		break;
	}
	if(result !== undefined) {
		const storage = parseInt(data.storage);
		const varName2 = this.evalMessage(data.varName2, cache);
		this.storeValue(result, storage, varName2, cache);
	}
	this.callNextAction(cache);
},

//---------------------------------------------------------------------
// Action Bot Mod
//
// Upon initialization of the bot, this code is run. Using the bot's
// DBM namespace, one can add/modify existing functions if necessary.
// In order to reduce conflictions between mods, be sure to alias
// functions you wish to overwrite.
//---------------------------------------------------------------------

mod: function(DBM) {
}

}; // End of module
<|endoftext|>


<commit_before>config = require('../config.js');
exports.config = config;

// ==========================================================================
// Handle generic request parameters
// --------------------------------------------------------------------------

exports.addLimitAndOffset = function (query, params) {
  var limit = params.limit ? parseInt(params.limit) : config.settings.result_row_limit.default
  if (limit > config.settings.result_row_limit.max) {
    limit = config.settings.result_row_limit.default;
  }
  query.limit(limit);
  query.offset(params.offset ? parseInt(params.offset) : 0);
}

// add member related options to a db query according to parameters given by client
// TODO limit to access privileges
exports.addMemberOptions = function (req, query, params, relation) {
  var table_name = 'member';
  if (relation) {
    table_name = relation + '_member';
  };
  var member_id = params[relation ? relation + '_member_id' : 'member_id'];
  var member_disabled = params[relation ? relation + '_member_disabled' : 'member_disabled'] ? true : false;
  var member_search = params[relation ? relation + '_member_search' : 'member_search'];
  var member_order_by_name = parseInt(params[relation ? relation + '_member_order_by_name' : 'member_order_by_name']);
  var member_order_by_created = parseInt(params[relation ? relation + '_member_order_by_created' : 'member_order_by_created']);

  if (member_id) {
    query.addWhere(['"' + table_name + '"."id" IN (??)', member_id.split(',')]);
  };
  /*if (member_disabled == true) {
    query.addWhere('"' + table_name + '"."active" = FALSE');
  } else {
    query.addWhere('"' + table_name + '"."active" = TRUE OR "' + table_name + '"."active" ISNULL');
  };*/
  if (member_search) {
    query.addWhere(['"' + table_name + '"."text_search_data" @@ text_search_query(?)', member_search]);
  };
  if (member_order_by_name) {
    query.addOrderBy('"' + table_name + '"."name"')
  }
  if (member_order_by_created) {
    query.addOrderBy('"' + table_name + '"."created" DESC')
  }
};

// add policy related options to a db query according to parameters given by client
exports.addPolicyOptions = function (req, query, params) {
  if (params.policy_id) {
    query.addWhere(['policy.id IN (??)', params.policy_id.split(',')]);
  }
  if (params.policy_order_by_name) {
    query.addOrderBy('"policy"."name"')
  }
};

// add unit related options to a db query according to parameters given by client
exports.addUnitOptions = function (req, query, params) {
  if (params.unit_id) {
    query.addWhere(['"unit"."id" IN (??)', params.unit_id.split(',')]);
  }
  if (params.unit_parent_id) {
    query.addWhere(['"unit"."parent_id" = ?', params.unit_parent_id]);
  }
  if (params.unit_without_parent) {
    query.addWhere('"unit"."parent_id" ISNULL');
  }
  if (params.unit_disabled) {
    query.addWhere('"unit"."active" = FALSE');
  } else {
    query.addWhere('"unit"."active" = TRUE');
  }
  if (params.unit_order_by_name) {
    query.addOrderBy('"unit"."name"')
  }
}

// add area related options to a db query according to parameters given by client
exports.addAreaOptions = function (req, query, params) {
  exports.addUnitOptions(req, query, params);
  if (params.area_id) {
    query.addWhere(['"area"."id" IN (??)', params.area_id.split(',')]);
  }
  if (params.area_disabled) {
    query.addWhere('"area"."active" = FALSE');
  } else {
    query.addWhere('"area"."active" = TRUE');
  }
  if (req.current_access_level == 'member' && params.area_my) {
    query.addWhere(['"area"."id" IN (SELECT "area_id" FROM "membership" WHERE "member_id" = ?)', req.current_member_id]);
  }
  if (params.area_order_by_name) {
    query.addOrderBy('"area"."name"')
  }
}

// add issue related options to a db query according to parameters given by client
exports.addIssueOptions = function (req, query, params) {
  exports.addAreaOptions(req, query, params);
  exports.addPolicyOptions(req, query, params);

  if (params.issue_id) query.addWhere(['issue.id IN (??)', params.issue_id.split(',')]);

  if (params.issue_state) {
    var issue_state_string;
    if (params.issue_state == 'open') {
      issue_states = ['admission', 'discussion', 'verification', 'voting'];
    } else if (params.issue_state == 'closed') {
      issue_state_string = ['canceled_revoked_before_accepted', 'canceled_issue_not_accepted', 'canceled_after_revocation_during_discussion', 'canceled_after_revocation_during_verification', 'calculation', 'canceled_no_initiative_admitted', 'finished_without_winner', 'finished_with_winner'];
    } else {
      issue_states = params.issue_state.split(',');
    }
    query.addWhere(['"issue"."state" IN (??)', issue_states]);
  };

  if (params.issue_accepted) query.addWhere('"issue"."accepted" NOTNULL');
  if (params.issue_half_frozen) query.addWhere('"issue"."half_frozen" NOTNULL');
  if (params.issue_fully_frozen) query.addWhere('"issue"."fully_frozen" NOTNULL');
  if (params.issue_closed) query.addWhere('"issue"."closed" NOTNULL');
  if (params.issue_cleaned) query.addWhere('"issue"."cleaned" NOTNULL');

  if (params.issue_created_after) query.addWhere(['"issue"."created" >= ?', params.issue_created_after]);
  if (params.issue_created_before) query.addWhere(['"issue"."created" < ?', params.issue_created_before]);
  if (params.issue_accepted_after) query.addWhere(['"issue"."accepted" >= ?', params.issue_accepted_after]);
  if (params.issue_accepted_before) query.addWhere(['"issue"."accepted" < ?', params.issue_accepted_before]);
  if (params.issue_half_frozen_after) query.addWhere(['"issue"."half_frozen" >= ?', params.issue_half_frozen_after]);
  if (params.issue_half_frozen_before) query.addWhere(['"issue"."half_frozen" < ?', params.issue_half_frozen_before]);
  if (params.issue_fully_frozen_after) query.addWhere(['"issue"."fully_frozen" >= ?', params.issue_fully_frozen_after]);
  if (params.issue_fully_frozen_before) query.addWhere(['"issue"."fully_frozen" < ?', params.issue_fully_frozen_before]);
  if (params.issue_closed_after) query.addWhere(['"issue"."closed" >= ?', params.issue_closed_after]);
  if (params.issue_closed_before) query.addWhere(['"issue"."closed" < ?', params.issue_closed_before]);
  if (params.issue_cleaned_after) query.addWhere(['"issue."cleaned" >= ?', params.issue_cleaned_after]);
  if (params.issue_cleaned_before) query.addWhere(['"issue"."cleaned" < ?', params.issue_cleaned_before]);

  if (params.issue_state_time_left_below) {
    query.addWhere(['(case issue.state when \'admission\' then issue.created + issue.admission_time when \'discussion\' then issue.accepted + issue.discussion_time when \'verification\' then issue.half_frozen + issue.verification_time when \'voting\' then issue.fully_frozen + issue.voting_time end) - now() < ?', params.issue_state_time_left_below]);
  };

  if (params.issue_order_by_state_time_left) {
    query.addOrderBy('(case issue.state when \'admission\' then issue.created + issue.admission_time when \'discussion\' then issue.accepted + issue.discussion_time when \'verification\' then issue.half_frozen + issue.verification_time when \'voting\' then issue.fully_frozen + issue.voting_time end)');
  };

  if (params.issue_order_by_id) query.addOrderBy('"issue"."id"');
}

// add initiative related options to a db query according to parameters given by client
exports.addInitiativeOptions = function (req, query, params) {
  exports.addIssueOptions(req, query, params);
  if (params.initiative_id) {
    query.addWhere(['initiative.id IN (??)', params.initiative_id.split(',')]);
  }
  // TODO
  //query.from.push('JOIN initiator AS initiative_initiator ON initiative_initiator.initiative_id = initiative.id AND initiative_initiator.accepted JOIN member AS initiator_member ON initiator_member.id = initiative_initiator.member_id');
  //query.from.push('JOIN supporter AS initiative_supporter ON initiative_supporter.initiative_id = initiative.id JOIN member AS supporter_member ON supporter_member.id = initiative_supporter.member_id');
  //exports.addMemberOptions(query, params, 'initiator');
  //exports.addMemberOptions(query, params, 'supporter');

  if (params.initiative_revoked) query.addWhere('initiative.revoked NOTNULL');
  if (params.initiative_revoked_after) query.addWhere(['initiative.revoked >= ?', params.initiative_revoked_after]);
  if (params.initiative_revoked_before) query.addWhere(['initiative.revoked < ?', params.initiative_revoked_before]);
  if (params.initiative_revoked_by_member_id) query.addWhere(['initiative.revoked_by_member_id = ?', params.initiative_revoked_by_member_id]);
  if (params.initiative_suggested_initiative_id) query.addWhere(['initiative.suggested_initiative_id = ?', params.initiative_suggested_initiative_id]);

  if (params.initiative_admitted) query.addWhere('initiative.admitted NOTNULL');
  if (params.initiative_created_after) query.addWhere(['initiative.created >= ?', params.initiative_created_after]);
  if (params.initiative_created_before) query.addWhere(['initiative.created < ?',params.initiative_created_before]);
  if (params.initiative_admitted_after) query.addWhere(['initiative.admitted >= ?', params.initiative_admitted_after]);
  if (params.initiative_admitted_before) query.addWhere(['initiative.admitted < ?', params.initiative_admitted_before]);

  if (params.initiative_supporter_count_below) query.addWhere(['initiative.supporter_count < ?', params.initiative_supporter_count_below]);
  if (params.initiative_supporter_count_above) query.addWhere(['initiative.supporter_count >= ?', params.initiative_supporter_count_above]);

  if (params.initiative_attainable) query.addWhere('initiative.attainable');
  if (params.initiative_favored) query.addWhere('initiative.favored');
  if (params.initiative_unfavored) query.addWhere('initiative.unfavored');

  if (params.initiative_max_preliminary_rank) query.addWhere(['initiative.preliminary_rank <= ?', params.initiative_max_preliminary_rank]);
  if (params.initiative_max_final_rank) query.addWhere(['initiative.preliminary_rank <= ?', params.initiative_max_final_rank]);

  if (params.initiative_disqualified) query.addWhere('initiative.disqualified');
  if (params.initiative_winner) query.addWhere('initiative.winner');

  if (params.initiative_search) {
    query.addWhere(['initiative.text_search_data @@ text_search_query(?)', params.initiative_search]);
  };
  if (params.initiative_order_by_id) {
    query.addOrderBy('initiative.id');
  }
}

// add suggestion related options to a db query according to parameters given by client
exports.addSuggestionOptions = function (req, query, params) {
  exports.addInitiativeOptions(req, query, params);
  if (params.suggestion_id) {
    query.addWhere(['suggestion.id IN (??)', params.suggestion_id]);
  }
  if (params.suggestion_search) {
    query.addWhere(['suggestion.text_search_data @@ text_search_query(?)', params.suggestion_search]);
  };
  if (params.include_suggestion) {
    addObjectFields(query, 'suggestion');
  };
}
<commit_msg>Updated general params to API revision 141<commit_after>config = require('../config.js');
exports.config = config;

// ==========================================================================
// Handle generic request parameters
// --------------------------------------------------------------------------

exports.addLimitAndOffset = function (query, params) {
  var limit = params.limit ? parseInt(params.limit) : config.settings.result_row_limit.default
  if (limit > config.settings.result_row_limit.max) {
    limit = config.settings.result_row_limit.default;
  }
  query.limit(limit);
  query.offset(params.offset ? parseInt(params.offset) : 0);
}

// add member related options to a db query according to parameters given by client
// TODO limit to access privileges
exports.addMemberOptions = function (req, query, params, relation) {
  var table_name = 'member';
  if (relation) table_name = relation + '_member';

  var member_id = params[relation ? relation + '_member_id' : 'member_id'];
  var member_disabled = params[relation ? relation + '_member_disabled' : 'member_disabled'] ? true : false;
  var member_search = params[relation ? relation + '_member_search' : 'member_search'];
  var member_order_by_name = parseInt(params[relation ? relation + '_member_order_by_name' : 'member_order_by_name']);
  var member_order_by_created = parseInt(params[relation ? relation + '_member_order_by_created' : 'member_order_by_created']);

  if (member_id) {
    query.addWhere(['"' + table_name + '"."id" IN (??)', member_id.split(',')]);
  };
  if (member_disabled == 'only') {
    query.addWhere('"' + table_name + '"."active" = FALSE');
  } else if (member_disabled != 'include') {
    query.addWhere('"' + table_name + '"."active" = TRUE OR "' + table_name + '"."active" ISNULL');
  };
  if (member_search) {
    query.addWhere(['"' + table_name + '"."text_search_data" @@ text_search_query(?)', member_search]);
  };
  if (member_order_by_name) {
    query.addOrderBy('"' + table_name + '"."name"')
  }
  if (member_order_by_created) {
    query.addOrderBy('"' + table_name + '"."created" DESC')
  }
};

// add policy related options to a db query according to parameters given by client
exports.addPolicyOptions = function (req, query, params) {
  if (params.policy_id) {
    query.addWhere(['policy.id IN (??)', params.policy_id.split(',')]);
  }
  if (params.policy_order_by_name) {
    query.addOrderBy('"policy"."name"')
  }
};

// add unit related options to a db query according to parameters given by client
exports.addUnitOptions = function (req, query, params) {
  if (params.unit_id) {
    query.addWhere(['"unit"."id" IN (??)', params.unit_id.split(',')]);
  }
  if (params.unit_parent_id) {
    query.addWhere(['"unit"."parent_id" = ?', params.unit_parent_id]);
  }
  if (params.unit_without_parent == '1') {
    query.addWhere('"unit"."parent_id" ISNULL');
  }
  if (params.unit_disabled == 'only') {
    query.addWhere('"unit"."active" = FALSE');
  } else if (params.unit_disabled != 'include') {
    query.addWhere('"unit"."active" = TRUE');
  }
  if (params.unit_order_by_name) {
    query.addOrderBy('"unit"."name"')
  }
}

// add area related options to a db query according to parameters given by client
exports.addAreaOptions = function (req, query, params) {
  exports.addUnitOptions(req, query, params);
  if (params.area_id) {
    query.addWhere(['"area"."id" IN (??)', params.area_id.split(',')]);
  }
  if (params.area_disabled == 'only') {
    query.addWhere('"area"."active" = FALSE');
  } else if (params.area_disabled == 'include') {
    query.addWhere('"area"."active" = TRUE');
  }
  if (req.current_access_level == 'member' && params.area_my) {
    query.addWhere(['"area"."id" IN (SELECT "area_id" FROM "membership" WHERE "member_id" = ?)', req.current_member_id]);
  }
  if (params.area_order_by_name) {
    query.addOrderBy('"area"."name"')
  }
}

// add issue related options to a db query according to parameters given by client
exports.addIssueOptions = function (req, query, params) {
  exports.addAreaOptions(req, query, params);
  exports.addPolicyOptions(req, query, params);

  if (params.issue_id) query.addWhere(['issue.id IN (??)', params.issue_id.split(',')]);

  if (params.issue_state) {
    var issue_state_string;
    if (params.issue_state == 'open') {
      issue_states = ['admission', 'discussion', 'verification', 'voting'];
    } else if (params.issue_state == 'closed') {
      issue_state_string = ['canceled_revoked_before_accepted', 'canceled_issue_not_accepted', 'canceled_after_revocation_during_discussion', 'canceled_after_revocation_during_verification', 'calculation', 'canceled_no_initiative_admitted', 'finished_without_winner', 'finished_with_winner'];
    } else {
      issue_states = params.issue_state.split(',');
    }
    query.addWhere(['"issue"."state" IN (??)', issue_states]);
  };

  if (params.issue_accepted == '1') query.addWhere('"issue"."accepted" NOTNULL');
  if (params.issue_accepted == '0') query.addWhere('"issue"."accepted" ISNULL');
  if (params.issue_half_frozen == '1') query.addWhere('"issue"."half_frozen" NOTNULL');
  if (params.issue_half_frozen == '0') query.addWhere('"issue"."half_frozen" ISNULL');
  if (params.issue_fully_frozen == '1') query.addWhere('"issue"."fully_frozen" NOTNULL');
  if (params.issue_fully_frozen == '0') query.addWhere('"issue"."fully_frozen" ISNULL');
  if (params.issue_closed == '1') query.addWhere('"issue"."closed" NOTNULL');
  if (params.issue_closed == '0') query.addWhere('"issue"."closed" ISNULL');
  if (params.issue_cleaned == '1') query.addWhere('"issue"."cleaned" NOTNULL');
  if (params.issue_cleaned == '0') query.addWhere('"issue"."cleaned" ISNULL');

  if (params.issue_ranks_available == '1') query.addWhere('"issue"."ranks_available"');
  if (params.issue_ranks_available == '0') query.addWhere('NOT "issue"."ranks_available"');

  if (params.issue_created_after) query.addWhere(['"issue"."created" >= ?', params.issue_created_after]);
  if (params.issue_created_before) query.addWhere(['"issue"."created" < ?', params.issue_created_before]);
  if (params.issue_accepted_after) query.addWhere(['"issue"."accepted" >= ?', params.issue_accepted_after]);
  if (params.issue_accepted_before) query.addWhere(['"issue"."accepted" < ?', params.issue_accepted_before]);
  if (params.issue_half_frozen_after) query.addWhere(['"issue"."half_frozen" >= ?', params.issue_half_frozen_after]);
  if (params.issue_half_frozen_before) query.addWhere(['"issue"."half_frozen" < ?', params.issue_half_frozen_before]);
  if (params.issue_fully_frozen_after) query.addWhere(['"issue"."fully_frozen" >= ?', params.issue_fully_frozen_after]);
  if (params.issue_fully_frozen_before) query.addWhere(['"issue"."fully_frozen" < ?', params.issue_fully_frozen_before]);
  if (params.issue_closed_after) query.addWhere(['"issue"."closed" >= ?', params.issue_closed_after]);
  if (params.issue_closed_before) query.addWhere(['"issue"."closed" < ?', params.issue_closed_before]);
  if (params.issue_cleaned_after) query.addWhere(['"issue."cleaned" >= ?', params.issue_cleaned_after]);
  if (params.issue_cleaned_before) query.addWhere(['"issue"."cleaned" < ?', params.issue_cleaned_before]);

  if (params.issue_state_time_left_below) {
    query.addWhere(['(case issue.state when \'admission\' then issue.created + issue.admission_time when \'discussion\' then issue.accepted + issue.discussion_time when \'verification\' then issue.half_frozen + issue.verification_time when \'voting\' then issue.fully_frozen + issue.voting_time end) - now() < ?', params.issue_state_time_left_below]);
  };

  if (params.issue_order_by_state_time_left) {
    query.addOrderBy('(case issue.state when \'admission\' then issue.created + issue.admission_time when \'discussion\' then issue.accepted + issue.discussion_time when \'verification\' then issue.half_frozen + issue.verification_time when \'voting\' then issue.fully_frozen + issue.voting_time end)');
  };

  if (params.issue_order_by_id) query.addOrderBy('"issue"."id"');
}

// add initiative related options to a db query according to parameters given by client
exports.addInitiativeOptions = function (req, query, params) {
  exports.addIssueOptions(req, query, params);
  if (params.initiative_id) {
    query.addWhere(['initiative.id IN (??)', params.initiative_id.split(',')]);
  }
  // TODO
  //query.from.push('JOIN initiator AS initiative_initiator ON initiative_initiator.initiative_id = initiative.id AND initiative_initiator.accepted JOIN member AS initiator_member ON initiator_member.id = initiative_initiator.member_id');
  //query.from.push('JOIN supporter AS initiative_supporter ON initiative_supporter.initiative_id = initiative.id JOIN member AS supporter_member ON supporter_member.id = initiative_supporter.member_id');
  //exports.addMemberOptions(query, params, 'initiator');
  //exports.addMemberOptions(query, params, 'supporter');

  if (params.initiative_revoked == '1') query.addWhere('initiative.revoked NOTNULL');
  if (params.initiative_revoked == '0') query.addWhere('initiative.revoked ISNULL');
  if (params.initiative_revoked_after) query.addWhere(['initiative.revoked >= ?', params.initiative_revoked_after]);
  if (params.initiative_revoked_before) query.addWhere(['initiative.revoked < ?', params.initiative_revoked_before]);
  // TODO check accesslevel
  if (params.initiative_revoked_by_member_id) query.addWhere(['initiative.revoked_by_member_id = ?', params.initiative_revoked_by_member_id]);
  if (params.initiative_suggested_initiative_id) query.addWhere(['initiative.suggested_initiative_id = ?', params.initiative_suggested_initiative_id]);

  if (params.initiative_admitted == '1') query.addWhere('initiative.admitted NOTNULL');
  if (params.initiative_admitted == '0') query.addWhere('initiative.admitted ISNULL');
  if (params.initiative_created_after) query.addWhere(['initiative.created >= ?', params.initiative_created_after]);
  if (params.initiative_created_before) query.addWhere(['initiative.created < ?',params.initiative_created_before]);
  if (params.initiative_admitted_after) query.addWhere(['initiative.admitted >= ?', params.initiative_admitted_after]);
  if (params.initiative_admitted_before) query.addWhere(['initiative.admitted < ?', params.initiative_admitted_before]);

  if (params.initiative_supporter_count_below) query.addWhere(['initiative.supporter_count < ?', params.initiative_supporter_count_below]);
  if (params.initiative_supporter_count_above) query.addWhere(['initiative.supporter_count >= ?', params.initiative_supporter_count_above]);

  if (params.initiative_attainable == '1') query.addWhere('initiative.attainable');
  if (params.initiative_attainable == '0') query.addWhere('NOT initiative.attainable');
  if (params.initiative_favored == '1') query.addWhere('initiative.favored');
  if (params.initiative_favored == '0') query.addWhere('NOT initiative.favored');
  if (params.initiative_unfavored == '1') query.addWhere('initiative.unfavored');
  if (params.initiative_unfavored == '0') query.addWhere('NOT initiative.unfavored');

  if (params.initiative_max_preliminary_rank) query.addWhere(['initiative.preliminary_rank <= ?', params.initiative_max_preliminary_rank]);
  if (params.initiative_max_final_rank) query.addWhere(['initiative.preliminary_rank <= ?', params.initiative_max_final_rank]);

  if (params.initiative_disqualified == '1') query.addWhere('initiative.disqualified');
  if (params.initiative_disqualified == '0') query.addWhere('NOT initiative.disqualified');
  if (params.initiative_winner == '1') query.addWhere('initiative.winner');
  if (params.initiative_winner == '0') query.addWhere('NOT initiative.winner');

  if (params.initiative_search) {
    query.addWhere(['initiative.text_search_data @@ text_search_query(?)', params.initiative_search]);
  };
  if (params.initiative_order_by_id) {
    query.addOrderBy('initiative.id');
  }
}

// add suggestion related options to a db query according to parameters given by client
exports.addSuggestionOptions = function (req, query, params) {
  exports.addInitiativeOptions(req, query, params);
  if (params.suggestion_id) {
    query.addWhere(['suggestion.id IN (??)', params.suggestion_id]);
  }
  if (params.suggestion_search) {
    query.addWhere(['suggestion.text_search_data @@ text_search_query(?)', params.suggestion_search]);
  };
  if (params.include_suggestion) {
    addObjectFields(query, 'suggestion');
  };
}
<|endoftext|>


<commit_before>// @flow
// An infinite scrolling chat list. Using react-virtualized which doens't really handle this case out of the box
// We control which set of messages we render in our state object
// We load that in in our constructor, after you stop scrolling or if we get an update and we're not currently scrolling

import LoadingMore from './messages/loading-more'
import React, {Component} from 'react'
import ReactDOM from 'react-dom'
import SidePanel from './side-panel/index.desktop'
import Popup from './messages/popup'
import _ from 'lodash'
import messageFactory from './messages'
import {AutoSizer, CellMeasurer, List, defaultCellMeasurerCellSizeCache} from 'react-virtualized'
import {Box} from '../../common-adapters'
import {globalColors, globalStyles} from '../../styles'

import type {Message, MessageID} from '../../constants/chat'
import type {Props} from './'

type State = {
  isLockedToBottom: boolean,
  isScrolling: boolean,
  messages: List<Message>,
  scrollTop: number,
  selectedMessageID?: MessageID,
}

class ConversationList extends Component<void, Props, State> {
  _cellCache: any;
  _cellMeasurer: any;
  _list: any;
  state: State;
  _toRemeasure: List;

  constructor (props: Props) {
    super(props)

    this.state = {
      isLockedToBottom: true,
      isScrolling: false,
      messages: props.messages,
      scrollTop: 0,
    }

    this._cellCache = new CellSizeCache(this._indexToID)
    this._toRemeasure = []
  }

  _indexToID = index => {
    // loader message
    if (index === 0) {
      return 0
    } else {
      // minus one because loader message is there
      const messageIndex = index - 1
      const message = this.state.messages.get(messageIndex)
      // We want a stable key -- messages have an outboxID but no messageID,
      // then later gain a messageID.  So if we prefer outboxIDs to messageIDs
      // for the key, every row keeps its key.
      const id = message && (message.outboxID || message.messageID || message.timestamp)
      if (id == null) {
        console.warn('id is null for index:', messageIndex)
      }
      return id
    }
  }

  componentWillUpdate (nextProps: Props, nextState: State) {
    // If a message has moved from pending to sent, tell the List to discard
    // heights for it (which will re-render it and everything after it)
    if (this._toRemeasure.length) {
      this._toRemeasure.forEach(item => {
        this._list.recomputeRowHeights(item)
      })
      this._toRemeasure = []
    }
  }

  componentDidUpdate (prevProps: Props, prevState: State) {
    if (!this.state.isLockedToBottom && this.state.messages !== prevState.messages && prevState.messages.count() > 1) {
      // Figure out how many new items we have
      const prependedCount = this.state.messages.indexOf(prevState.messages.first())
      if (prependedCount !== -1) {
        // Measure the new items so we can adjust our scrollTop so your position doesn't jump
        const scrollTop = this.state.scrollTop + _.range(0, prependedCount)
          .map(index => this._cellMeasurer.getRowHeight({index: index + 1})) // +1 since 0 is the loading message
          .reduce((total, height) => total + height, 0)

        // Disabling eslint as we normally don't want to call setState in a componentDidUpdate in case you infinitely re-render
        this.setState({scrollTop}) // eslint-disable-line react/no-did-update-set-state
      }
    }
  }

  componentWillReceiveProps (nextProps: Props) {
    if (this.props.selectedConversation !== nextProps.selectedConversation) {
      this.setState({isLockedToBottom: true})
    }

    // If we're not scrolling let's update our internal messages
    if (!this.state.isScrolling) {
      this._invalidateChangedMessages(nextProps)
      this.setState({
        messages: nextProps.messages,
      })
    }
  }

  _invalidateChangedMessages (props: Props) {
    this.state.messages.forEach((item, index) => {
      if (item.messageID !== props.messages.get(index, {}).messageID) {
        this._toRemeasure.push(index + 1)
      }
    })
  }

  _onScrollSettled = _.debounce(() => {
    // If we've stopped scrolling let's update our internal messages
    this._invalidateChangedMessages(this.props)
    this.setState({
      isScrolling: false,
      ...(this.state.messages !== this.props.messages ? {messages: this.props.messages} : null),
    })
  }, 1000)

  _onScroll = _.throttle(({clientHeight, scrollHeight, scrollTop}) => {
    // Do nothing if we haven't really loaded anything
    if (!clientHeight) {
      return
    }

    // At the top, load more messages. Action handles loading state and if there's actually any more
    if (scrollTop === 0) {
      this.props.onLoadMoreMessages()
    }

    const isLockedToBottom = scrollTop + clientHeight === scrollHeight
    this.setState({
      isLockedToBottom,
      isScrolling: true,
      scrollTop,
    })

    // This is debounced so it resets the call
    this._onScrollSettled()
  }, 100)

  showPopup (message: Message, event: any) {
    if (message.type !== 'Text') return
    this.setState({
      selectedMessageID: message.messageID,
    })

    const clientRect = event.target.getBoundingClientRect()
    // Position next to button (client rect)
    // TODO: Measure instead of pixel math
    const x = clientRect.left - 205
    let y = clientRect.top - (message.followState === 'You' ? 200 : 116)
    if (y < 10) y = 10
    const popupComponent = <Popup
      message={message}
      onEditMessage={this.props.onEditMessage}
      onDeleteMessage={this.props.onDeleteMessage}
      onHidden={() => {
        ReactDOM.unmountComponentAtNode(document.getElementById('popupContainer'))
        this.setState({
          selectedMessageID: undefined,
        })
      }}
      style={{position: 'absolute', top: y, left: x}}
      />
    const container = document.getElementById('popupContainer')
    // ReactDOM.render(popupComponent, event.target)
    ReactDOM.render(popupComponent, container)
  }

  _rowRenderer = ({index, key, style, isScrolling}: {index: number, key: string, style: Object, isScrolling: boolean}) => {
    if (index === 0) {
      return <LoadingMore style={style} key={key || index} hasMoreItems={this.props.moreToLoad} />
    }

    const message = this.state.messages.get(index - 1)
    const prevMessage = this.state.messages.get(index - 2)
    const isFirstMessage = index - 1 === 0
    const skipMsgHeader = (prevMessage && prevMessage.type === 'Text' && prevMessage.author === message.author)
    const onAction = (event) => { this.showPopup(message, event) }
    const isSelected = this.state.selectedMessageID === message.messageID
    const isFirstNewMessage = this.props.firstNewMessageID ? this.props.firstNewMessageID === message.messageID : false
    // TODO: We need to update the message component selected status
    // when showing popup, which isn't currently working.

    return messageFactory(message, isFirstMessage || !skipMsgHeader, index, key, isFirstNewMessage, style, isScrolling, onAction, isSelected)
  }

  render () {
    const messageCount = this.state.messages.count()
    const countWithLoading = messageCount + 1 // Loading row on top always
    let scrollToIndex = this.state.isLockedToBottom ? countWithLoading - 1 : undefined
    let scrollTop = scrollToIndex ? undefined : this.state.scrollTop

    const realCSS = `
    .message {
      background-color: transparent;
    }
    .message .action-button {
      visibility: hidden;
    }
    .message:hover {
      background-color: ${globalColors.black_05};
    }
    .message:hover .action-button {
      visibility: visible;
    }
    `

    return (
      <Box style={{...globalStyles.flexBoxColumn, flex: 1, position: 'relative'}}>
        <style>{realCSS}</style>
        <AutoSizer>
          {({height, width}) => (
            <CellMeasurer
              cellRenderer={({rowIndex, ...rest}) => this._rowRenderer({index: rowIndex, ...rest})}
              columnCount={1}
              ref={r => { this._cellMeasurer = r }}
              cellSizeCache={this._cellCache}
              rowCount={countWithLoading} >
              {({getRowHeight}) => (
                <List
                  height={height}
                  ref={r => { this._list = r }}
                  width={width}
                  onScroll={this._onScroll}
                  scrollTop={scrollTop}
                  scrollToIndex={scrollToIndex}
                  rowCount={countWithLoading}
                  rowHeight={getRowHeight}
                  rowRenderer={this._rowRenderer} />
              )}
            </CellMeasurer>
          )}
        </AutoSizer>
        {this.props.sidePanelOpen && <Box style={{...globalStyles.flexBoxColumn, position: 'absolute', right: 0, top: 0, bottom: 0, width: 320}}>
          <SidePanel {...this.props} />
        </Box>}
      </Box>
    )
  }
}

class CellSizeCache extends defaultCellMeasurerCellSizeCache {
  _indexToID: (index: number) => number;

  constructor (indexToID) {
    super({uniformColumnWidth: true})
    this._indexToID = indexToID
  }

  getRowHeight (index) {
    return super.getRowHeight(this._indexToID(index))
  }

  setRowHeight (index, height) {
    super.setRowHeight(this._indexToID(index), height)
  }

  hasRowHeight (index) {
    return super.hasRowHeight(this._indexToID(index))
  }

  clearRowHeight (index) {
    return super.clearRowHeight(this._indexToID(index))
  }
}

export default ConversationList
<commit_msg>Make id selection more explicit<commit_after>// @flow
// An infinite scrolling chat list. Using react-virtualized which doens't really handle this case out of the box
// We control which set of messages we render in our state object
// We load that in in our constructor, after you stop scrolling or if we get an update and we're not currently scrolling

import LoadingMore from './messages/loading-more'
import React, {Component} from 'react'
import ReactDOM from 'react-dom'
import SidePanel from './side-panel/index.desktop'
import Popup from './messages/popup'
import _ from 'lodash'
import messageFactory from './messages'
import {AutoSizer, CellMeasurer, List, defaultCellMeasurerCellSizeCache} from 'react-virtualized'
import {Box} from '../../common-adapters'
import {globalColors, globalStyles} from '../../styles'

import type {Message, MessageID} from '../../constants/chat'
import type {Props} from './'

type State = {
  isLockedToBottom: boolean,
  isScrolling: boolean,
  messages: List<Message>,
  scrollTop: number,
  selectedMessageID?: MessageID,
}

class ConversationList extends Component<void, Props, State> {
  _cellCache: any;
  _cellMeasurer: any;
  _list: any;
  state: State;
  _toRemeasure: List;

  constructor (props: Props) {
    super(props)

    this.state = {
      isLockedToBottom: true,
      isScrolling: false,
      messages: props.messages,
      scrollTop: 0,
    }

    this._cellCache = new CellSizeCache(this._indexToID)
    this._toRemeasure = []
  }

  _indexToID = index => {
    // loader message
    if (index === 0) {
      return 0
    } else {
      // minus one because loader message is there
      const messageIndex = index - 1
      const message = this.state.messages.get(messageIndex)
      let id
      if (message) {
        if (message.type === 'Timestamp') {
          id = message.timestamp
        } else {
          // We want a stable key -- messages have an outboxID but no
          // messageID, then later gain a messageID.  So if we prefer
          // outboxIDs to messageIDs for the key, every row keeps its key.
          id = message && (message.outboxID || message.messageID)
        }
      }
      if (id == null) {
        console.warn('id is null for index:', messageIndex)
      }
      return id
    }
  }

  componentWillUpdate (nextProps: Props, nextState: State) {
    // If a message has moved from pending to sent, tell the List to discard
    // heights for it (which will re-render it and everything after it)
    if (this._toRemeasure.length) {
      this._toRemeasure.forEach(item => {
        this._list.recomputeRowHeights(item)
      })
      this._toRemeasure = []
    }
  }

  componentDidUpdate (prevProps: Props, prevState: State) {
    if (!this.state.isLockedToBottom && this.state.messages !== prevState.messages && prevState.messages.count() > 1) {
      // Figure out how many new items we have
      const prependedCount = this.state.messages.indexOf(prevState.messages.first())
      if (prependedCount !== -1) {
        // Measure the new items so we can adjust our scrollTop so your position doesn't jump
        const scrollTop = this.state.scrollTop + _.range(0, prependedCount)
          .map(index => this._cellMeasurer.getRowHeight({index: index + 1})) // +1 since 0 is the loading message
          .reduce((total, height) => total + height, 0)

        // Disabling eslint as we normally don't want to call setState in a componentDidUpdate in case you infinitely re-render
        this.setState({scrollTop}) // eslint-disable-line react/no-did-update-set-state
      }
    }
  }

  componentWillReceiveProps (nextProps: Props) {
    if (this.props.selectedConversation !== nextProps.selectedConversation) {
      this.setState({isLockedToBottom: true})
    }

    // If we're not scrolling let's update our internal messages
    if (!this.state.isScrolling) {
      this._invalidateChangedMessages(nextProps)
      this.setState({
        messages: nextProps.messages,
      })
    }
  }

  _invalidateChangedMessages (props: Props) {
    this.state.messages.forEach((item, index) => {
      if (item.messageID !== props.messages.get(index, {}).messageID) {
        this._toRemeasure.push(index + 1)
      }
    })
  }

  _onScrollSettled = _.debounce(() => {
    // If we've stopped scrolling let's update our internal messages
    this._invalidateChangedMessages(this.props)
    this.setState({
      isScrolling: false,
      ...(this.state.messages !== this.props.messages ? {messages: this.props.messages} : null),
    })
  }, 1000)

  _onScroll = _.throttle(({clientHeight, scrollHeight, scrollTop}) => {
    // Do nothing if we haven't really loaded anything
    if (!clientHeight) {
      return
    }

    // At the top, load more messages. Action handles loading state and if there's actually any more
    if (scrollTop === 0) {
      this.props.onLoadMoreMessages()
    }

    const isLockedToBottom = scrollTop + clientHeight === scrollHeight
    this.setState({
      isLockedToBottom,
      isScrolling: true,
      scrollTop,
    })

    // This is debounced so it resets the call
    this._onScrollSettled()
  }, 100)

  showPopup (message: Message, event: any) {
    if (message.type !== 'Text') return
    this.setState({
      selectedMessageID: message.messageID,
    })

    const clientRect = event.target.getBoundingClientRect()
    // Position next to button (client rect)
    // TODO: Measure instead of pixel math
    const x = clientRect.left - 205
    let y = clientRect.top - (message.followState === 'You' ? 200 : 116)
    if (y < 10) y = 10
    const popupComponent = <Popup
      message={message}
      onEditMessage={this.props.onEditMessage}
      onDeleteMessage={this.props.onDeleteMessage}
      onHidden={() => {
        ReactDOM.unmountComponentAtNode(document.getElementById('popupContainer'))
        this.setState({
          selectedMessageID: undefined,
        })
      }}
      style={{position: 'absolute', top: y, left: x}}
      />
    const container = document.getElementById('popupContainer')
    // ReactDOM.render(popupComponent, event.target)
    ReactDOM.render(popupComponent, container)
  }

  _rowRenderer = ({index, key, style, isScrolling}: {index: number, key: string, style: Object, isScrolling: boolean}) => {
    if (index === 0) {
      return <LoadingMore style={style} key={key || index} hasMoreItems={this.props.moreToLoad} />
    }

    const message = this.state.messages.get(index - 1)
    const prevMessage = this.state.messages.get(index - 2)
    const isFirstMessage = index - 1 === 0
    const skipMsgHeader = (prevMessage && prevMessage.type === 'Text' && prevMessage.author === message.author)
    const onAction = (event) => { this.showPopup(message, event) }
    const isSelected = this.state.selectedMessageID === message.messageID
    const isFirstNewMessage = this.props.firstNewMessageID ? this.props.firstNewMessageID === message.messageID : false
    // TODO: We need to update the message component selected status
    // when showing popup, which isn't currently working.

    return messageFactory(message, isFirstMessage || !skipMsgHeader, index, key, isFirstNewMessage, style, isScrolling, onAction, isSelected)
  }

  render () {
    const messageCount = this.state.messages.count()
    const countWithLoading = messageCount + 1 // Loading row on top always
    let scrollToIndex = this.state.isLockedToBottom ? countWithLoading - 1 : undefined
    let scrollTop = scrollToIndex ? undefined : this.state.scrollTop

    const realCSS = `
    .message {
      background-color: transparent;
    }
    .message .action-button {
      visibility: hidden;
    }
    .message:hover {
      background-color: ${globalColors.black_05};
    }
    .message:hover .action-button {
      visibility: visible;
    }
    `

    return (
      <Box style={{...globalStyles.flexBoxColumn, flex: 1, position: 'relative'}}>
        <style>{realCSS}</style>
        <AutoSizer>
          {({height, width}) => (
            <CellMeasurer
              cellRenderer={({rowIndex, ...rest}) => this._rowRenderer({index: rowIndex, ...rest})}
              columnCount={1}
              ref={r => { this._cellMeasurer = r }}
              cellSizeCache={this._cellCache}
              rowCount={countWithLoading} >
              {({getRowHeight}) => (
                <List
                  height={height}
                  ref={r => { this._list = r }}
                  width={width}
                  onScroll={this._onScroll}
                  scrollTop={scrollTop}
                  scrollToIndex={scrollToIndex}
                  rowCount={countWithLoading}
                  rowHeight={getRowHeight}
                  rowRenderer={this._rowRenderer} />
              )}
            </CellMeasurer>
          )}
        </AutoSizer>
        {this.props.sidePanelOpen && <Box style={{...globalStyles.flexBoxColumn, position: 'absolute', right: 0, top: 0, bottom: 0, width: 320}}>
          <SidePanel {...this.props} />
        </Box>}
      </Box>
    )
  }
}

class CellSizeCache extends defaultCellMeasurerCellSizeCache {
  _indexToID: (index: number) => number;

  constructor (indexToID) {
    super({uniformColumnWidth: true})
    this._indexToID = indexToID
  }

  getRowHeight (index) {
    return super.getRowHeight(this._indexToID(index))
  }

  setRowHeight (index, height) {
    super.setRowHeight(this._indexToID(index), height)
  }

  hasRowHeight (index) {
    return super.hasRowHeight(this._indexToID(index))
  }

  clearRowHeight (index) {
    return super.clearRowHeight(this._indexToID(index))
  }
}

export default ConversationList
<|endoftext|>


<commit_before>var SystemBuilder = require('systemjs-builder');
var argv = require('yargs').argv;
var builder = new SystemBuilder();

builder.loadConfig('./src/systemjs.config.js')
  .then(function(){
	  var outputFile = argv.prod ? 'dist/bundle.min.js' : 'dist/bundle.js';
	  return builder.buildStatic('app', outputFile, {
		  minify: argv.prod,
		  mangle: argv.prod,
		  rollup: argv.prod
	  });
  })
  .then(function(){
	  console.log('bundle built successfully!');
  });
<commit_msg>Fix errors with npm run bundle -- closes #31<commit_after>var SystemBuilder = require('systemjs-builder');
var argv = require('yargs').argv;

var sourceDirName = 'src';
var sourcePath = './' + sourceDirName;

var builder = new SystemBuilder(sourcePath, sourcePath +  "/systemjs.config.js");

var outputFile = argv.prod ? './dist/bundle.min.js' : './dist/bundle.js';

// The systemJS config is in `./src` and uses `node_modules` as if it's relative to it
//		which works in lite-server because it shows `./` and `./src` as the same folder
//		(see bs-config.json)
//
// In bundle though, the builder thinks that `node_modules` is relative to `./src`,
//		not to `./`, which we need to correct by removing `src/` from path.
builder.bundle('node_modules/*', {
	fetch: function (load, fetch) {
		load.address =
			load.address.replace(sourceDirName + "/node_modules", "node_modules");

		return fetch(load);
	}
});

builder.buildStatic('app', outputFile, {
	minify: argv.prod,
	mangle: argv.prod,
	rollup: argv.prod,
	// Keeps URLs in components' `templateUrl` relative to component path
	// See more at http://stackoverflow.com/a/37537725/146656
	encodeNames: false
});<|endoftext|>


<commit_before>import globalSf from '../../../global-sf'
import React from 'react'
import Icon from 'material-ui/Icon'
import AddIcon from 'material-ui-icons/Add'
import RemoveIcon from 'material-ui-icons/Remove'
import IconButton from 'material-ui/IconButton'
import { withStyles } from 'material-ui/styles'

const styles = theme => ({
  expandButton: {
    margin: 0,
    fontSize: 15,
    height: '1.5em',
    top: '0.2em'
  }
})

const idRegex = /\b[a-zA-Z0-9]{18}\b|\b[a-zA-Z0-9]{15}\b/g,
  logEntryToDivTagClass = {
    USER_DEBUG: 'debug',
    SYSTEM_: 'system',
    SOQL_EXECUTE_: 'soql',
    SOQL_EXECUTE_END: 'soql',
    METHOD_: 'method',
    CONSTRUCTOR_: 'method',
    EXCEPTION_: 'err',
    FATAL_ERROR: 'err',
    CODE_UNIT: 'method',
    CALLOUT: 'callout',
    VALIDATION_: 'method',
    EXECUTION_: 'rest',
    DML_BEGIN: 'rest',
    DML_END: 'rest',
    ENTERING_MANAGED_PKG: 'system'
  }

export function ParsedLog({ body }) {
  if (!body || body === '') return

  const intro = body.match(
    /^\d{1,2}\.\d\s[^]*?(?=\d\d:\d\d:\d\d\.\d{0,3}\s\(\d+\))/m
  )
  const theRest = body.match(
    /^\d\d:\d\d:\d\d\.\d{0,3}\s\(\d+\)\|[A-Z_]*[^]*?(?=\d\d:\d\d:\d\d\.\d{0,3}\s\(\d+\))/gm
  )
  return (

{intro}

      {theRest.map((body, index) => {
        return <LogElement body={body} key={index} />
      })}
      {null}

  )
}

@withStyles(styles)
class LogElement extends React.Component {
  constructor(props) {
    super(props)
    this.toggleIndentation = this.toggleIndentation.bind(this)
    this.state = { indented: props.indented }
  }

  toggleIndentation() {
    this.setState(prevState => ({
      ...prevState,
      indented: !prevState.indented
    }))
  }

  render() {
    const { body, classes } = this.props
    const { indented } = this.state
    const eventTypeMatch = /^\d\d:\d\d:\d\d\.\d{0,3}\s\(\d+\)\|([A-Z_]*)/.exec(
      body
    )
    if (!eventTypeMatch) {
      console.error(`Didn't find event type for ${body} `)
      return null
    }
    const eventType = eventTypeMatch[1]
    const finalBody = () => {
      if (eventType === 'USER_DEBUG' && indented) {
        const parsedUserDebug = body.match(
          /^(\d\d:\d\d:\d\d\.\d{0,3}\s\(\d+\)\|USER_DEBUG\|[\d+\]\|[A-Z_]+\|)([^]*)/m
        )
        if (parsedUserDebug && parsedUserDebug.length > 2) {
          console.log(parsedUserDebug[1])
          return `${parsedUserDebug[1]}${beautifyUserDebug(parsedUserDebug[2])}`
        }
      }
      return body
    }

    const filtered = Object.entries(logEntryToDivTagClass).find(me =>
      eventType.startsWith(me[0])
    )
    const className = filtered ? filtered[1] : 'rest'
    return (

        {eventType === 'USER_DEBUG' ? (
          <IconButton
            color="contrast"
            className={classes.expandButton}
            aria-label="Add"
            onClick={this.toggleIndentation}
          >
            {indented ? <RemoveIcon /> : <AddIcon />}
          </IconButton>
        ) : null}
        {finalBody()}

    )
  }
}

function beautifyUserDebug(userDebug) {
  if (looksLikeHtml(userDebug)) {
    return html_beautify(userDebug)
  }
  if (isJsonString(userDebug)) {
    return JSON.stringify(JSON.parse(userDebug), null, 2)
  }
  if (looksLikeSfdcObject(userDebug)) {
    return js_beautify(sfdcObjectBeautify(userDebug))
  }
  return userDebug
}

function isJsonString(str) {
  try {
    JSON.parse(str)
  } catch (e) {
    return false
  }
  return true
}

function sfdcObjectBeautify(string) {
  string = string.replace(/={/g, ':{')
  return string.replace(/([{| |\[]\w+)=(.+?)(?=, |},|}\)|:{|])/g, function(
    match,
    p1,
    p2
  ) {
    return p1 + ":'" + p2 + "'"
  })
}

function looksLikeSfdcObject(string) {
  return string.match(/\w+:{\w+=.+,?\s*}/)
}

function looksLikeHtml(source) {
  var trimmed = source.replace(/^[ \t\n\r]+/, '')
  return trimmed && trimmed.substring(0, 1) === '<'
}
<commit_msg>remove console<commit_after>import globalSf from '../../../global-sf'
import React from 'react'
import Icon from 'material-ui/Icon'
import AddIcon from 'material-ui-icons/Add'
import RemoveIcon from 'material-ui-icons/Remove'
import IconButton from 'material-ui/IconButton'
import { withStyles } from 'material-ui/styles'

const styles = theme => ({
  expandButton: {
    margin: 0,
    fontSize: 15,
    height: '1.5em',
    top: '0.2em'
  }
})

const idRegex = /\b[a-zA-Z0-9]{18}\b|\b[a-zA-Z0-9]{15}\b/g,
  logEntryToDivTagClass = {
    USER_DEBUG: 'debug',
    SYSTEM_: 'system',
    SOQL_EXECUTE_: 'soql',
    SOQL_EXECUTE_END: 'soql',
    METHOD_: 'method',
    CONSTRUCTOR_: 'method',
    EXCEPTION_: 'err',
    FATAL_ERROR: 'err',
    CODE_UNIT: 'method',
    CALLOUT: 'callout',
    VALIDATION_: 'method',
    EXECUTION_: 'rest',
    DML_BEGIN: 'rest',
    DML_END: 'rest',
    ENTERING_MANAGED_PKG: 'system'
  }

export function ParsedLog({ body }) {
  if (!body || body === '') return

  const intro = body.match(
    /^\d{1,2}\.\d\s[^]*?(?=\d\d:\d\d:\d\d\.\d{0,3}\s\(\d+\))/m
  )
  const theRest = body.match(
    /^\d\d:\d\d:\d\d\.\d{0,3}\s\(\d+\)\|[A-Z_]*[^]*?(?=\d\d:\d\d:\d\d\.\d{0,3}\s\(\d+\))/gm
  )
  return (

{intro}

      {theRest.map((body, index) => {
        return <LogElement body={body} key={index} />
      })}
      {null}

  )
}

@withStyles(styles)
class LogElement extends React.Component {
  constructor(props) {
    super(props)
    this.toggleIndentation = this.toggleIndentation.bind(this)
    this.state = { indented: props.indented }
  }

  toggleIndentation() {
    this.setState(prevState => ({
      ...prevState,
      indented: !prevState.indented
    }))
  }

  render() {
    const { body, classes } = this.props
    const { indented } = this.state
    const eventTypeMatch = /^\d\d:\d\d:\d\d\.\d{0,3}\s\(\d+\)\|([A-Z_]*)/.exec(
      body
    )
    if (!eventTypeMatch) {
      console.error(`Didn't find event type for ${body} `)
      return null
    }
    const eventType = eventTypeMatch[1]
    const finalBody = () => {
      if (eventType === 'USER_DEBUG' && indented) {
        const parsedUserDebug = body.match(
          /^(\d\d:\d\d:\d\d\.\d{0,3}\s\(\d+\)\|USER_DEBUG\|[\d+\]\|[A-Z_]+\|)([^]*)/m
        )
        if (parsedUserDebug && parsedUserDebug.length > 2) {
          return `${parsedUserDebug[1]}${beautifyUserDebug(parsedUserDebug[2])}`
        }
      }
      return body
    }

    const filtered = Object.entries(logEntryToDivTagClass).find(me =>
      eventType.startsWith(me[0])
    )
    const className = filtered ? filtered[1] : 'rest'
    return (

        {eventType === 'USER_DEBUG' ? (
          <IconButton
            color="contrast"
            className={classes.expandButton}
            aria-label="Add"
            onClick={this.toggleIndentation}
          >
            {indented ? <RemoveIcon /> : <AddIcon />}
          </IconButton>
        ) : null}
        {finalBody()}

    )
  }
}

function beautifyUserDebug(userDebug) {
  if (looksLikeHtml(userDebug)) {
    return html_beautify(userDebug)
  }
  if (isJsonString(userDebug)) {
    return JSON.stringify(JSON.parse(userDebug), null, 2)
  }
  if (looksLikeSfdcObject(userDebug)) {
    return js_beautify(sfdcObjectBeautify(userDebug))
  }
  return userDebug
}

function isJsonString(str) {
  try {
    JSON.parse(str)
  } catch (e) {
    return false
  }
  return true
}

function sfdcObjectBeautify(string) {
  string = string.replace(/={/g, ':{')
  return string.replace(/([{| |\[]\w+)=(.+?)(?=, |},|}\)|:{|])/g, function(
    match,
    p1,
    p2
  ) {
    return p1 + ":'" + p2 + "'"
  })
}

function looksLikeSfdcObject(string) {
  return string.match(/\w+:{\w+=.+,?\s*}/)
}

function looksLikeHtml(source) {
  var trimmed = source.replace(/^[ \t\n\r]+/, '')
  return trimmed && trimmed.substring(0, 1) === '<'
}
<|endoftext|>


<commit_before>var Singleton       = require("./Singleton");
var ChainInstance   = require("./ChainInstance");

module.exports = ChainFind;

function ChainFind(Model, opts) {
	var chain = {
		find: function (conditions) {
			for (var k in conditions) {
				opts.conditions[k] = conditions[k];
			}
			return this;
		},
		only: function () {
			if (arguments.length && Array.isArray(arguments[0])) {
				opts.only = arguments[0];
			} else {
				opts.only = Array.prototype.slice.apply(arguments);
			}
			return this;
		},
		limit: function (limit) {
			opts.limit = limit;
			return this;
		},
		skip: function (offset) {
			return this.offset(offset);
		},
		offset: function (offset) {
			opts.offset = offset;
			return this;
		},
		order: function (property, order) {
			if (!Array.isArray(opts.order)) {
				opts.order = [];
			}
			opts.order.push([ property, (order && order.toUpperCase() == "Z" ? "Z" : "A") ]);
			return this;
		},
		count: function (cb) {
			opts.driver.count(opts.table, opts.conditions, opts, function (err, data) {
				if (err || data.length === 0) {
					return cb(err);
				}
				return cb(null, data[0].c);
			});
			return this;
		},
		remove: function (cb) {
			opts.driver.find([ opts.id ], opts.table, opts.conditions, {
				limit  : opts.limit,
				order  : opts.order,
				merge  : opts.merge,
				offset : opts.offset,
				exists : opts.exists
			}, function (err, data) {
				if (err) {
					return cb(err);
				}
				if (data.length === 0) {
					return cb(null);
				}

				var ids = [], conditions = {};

				for (var i = 0; i < data.length; i++) {
					ids.push(data[i][opts.id]);
				}

				conditions[opts.id] = ids;

				return opts.driver.remove(opts.table, conditions, cb);
			});
			return this;
		},
		first: function (cb) {
			return this.run(function (err, items) {
				return cb(err, items.length > 0 ? items[0] : null);
			});
		},
		last: function (cb) {
			return this.run(function (err, items) {
				return cb(err, items.length > 0 ? items[items.length - 1] : null);
			});
		},
		each: function (cb) {
			return new ChainInstance(this, cb);
		},
		run: function (cb) {
			return this.all(cb);
		},
		all: function (cb) {
			opts.driver.find(opts.only, opts.table, opts.conditions, {
				limit  : opts.limit,
				order  : opts.order,
				merge  : opts.merge,
				offset : opts.offset,
				exists : opts.exists
			}, function (err, data) {
				if (err) {
					return cb(err);
				}
				if (data.length === 0) {
					return cb(null, []);
				}
				var pending = data.length;

				for (var i = 0; i < data.length; i++) {
					(function (idx) {
						opts.newInstance(data[idx], function (err, instance) {
							data[idx] = instance;

							if (--pending === 0) {
								return cb(null, data);
							}
						});
					})(i);
				}
			});
			return this;
		}
	};
	if (opts.associations) {
		for (var i = 0; i < opts.associations.length; i++) {
			addChainMethod(chain, opts.associations[i], opts);
		}
	}
	for (var k in Model) {
		if ([ "hasOne", "hasMany",
		      "drop", "sync", "get", "find", "count", "clear", "create",
		      "exists", "settings", "aggregate" ].indexOf(k) >= 0) continue;
		if (typeof Model[k] != "function") continue;

		chain[k] = Model[k];
	}
	return chain;
}

function addChainMethod(chain, association, opts) {
	chain[association.hasAccessor] = function (value) {
		if (!opts.exists) {
			opts.exists = [];
		}
		var conditions = {};

		conditions[association.mergeAssocId] = value;

		opts.exists.push({
			table      : association.mergeTable,
			link       : [ association.mergeId, association.model.id ],
			conditions : conditions
		});

		return chain;
	};
}
<commit_msg>Adds Model.all() to list of not linked ChainFind methods<commit_after>var Singleton       = require("./Singleton");
var ChainInstance   = require("./ChainInstance");

module.exports = ChainFind;

function ChainFind(Model, opts) {
	var chain = {
		find: function (conditions) {
			for (var k in conditions) {
				opts.conditions[k] = conditions[k];
			}
			return this;
		},
		only: function () {
			if (arguments.length && Array.isArray(arguments[0])) {
				opts.only = arguments[0];
			} else {
				opts.only = Array.prototype.slice.apply(arguments);
			}
			return this;
		},
		limit: function (limit) {
			opts.limit = limit;
			return this;
		},
		skip: function (offset) {
			return this.offset(offset);
		},
		offset: function (offset) {
			opts.offset = offset;
			return this;
		},
		order: function (property, order) {
			if (!Array.isArray(opts.order)) {
				opts.order = [];
			}
			opts.order.push([ property, (order && order.toUpperCase() == "Z" ? "Z" : "A") ]);
			return this;
		},
		count: function (cb) {
			opts.driver.count(opts.table, opts.conditions, opts, function (err, data) {
				if (err || data.length === 0) {
					return cb(err);
				}
				return cb(null, data[0].c);
			});
			return this;
		},
		remove: function (cb) {
			opts.driver.find([ opts.id ], opts.table, opts.conditions, {
				limit  : opts.limit,
				order  : opts.order,
				merge  : opts.merge,
				offset : opts.offset,
				exists : opts.exists
			}, function (err, data) {
				if (err) {
					return cb(err);
				}
				if (data.length === 0) {
					return cb(null);
				}

				var ids = [], conditions = {};

				for (var i = 0; i < data.length; i++) {
					ids.push(data[i][opts.id]);
				}

				conditions[opts.id] = ids;

				return opts.driver.remove(opts.table, conditions, cb);
			});
			return this;
		},
		first: function (cb) {
			return this.run(function (err, items) {
				return cb(err, items.length > 0 ? items[0] : null);
			});
		},
		last: function (cb) {
			return this.run(function (err, items) {
				return cb(err, items.length > 0 ? items[items.length - 1] : null);
			});
		},
		each: function (cb) {
			return new ChainInstance(this, cb);
		},
		run: function (cb) {
			return this.all(cb);
		},
		all: function (cb) {
			opts.driver.find(opts.only, opts.table, opts.conditions, {
				limit  : opts.limit,
				order  : opts.order,
				merge  : opts.merge,
				offset : opts.offset,
				exists : opts.exists
			}, function (err, data) {
				if (err) {
					return cb(err);
				}
				if (data.length === 0) {
					return cb(null, []);
				}
				var pending = data.length;

				for (var i = 0; i < data.length; i++) {
					(function (idx) {
						opts.newInstance(data[idx], function (err, instance) {
							data[idx] = instance;

							if (--pending === 0) {
								return cb(null, data);
							}
						});
					})(i);
				}
			});
			return this;
		}
	};
	if (opts.associations) {
		for (var i = 0; i < opts.associations.length; i++) {
			addChainMethod(chain, opts.associations[i], opts);
		}
	}
	for (var k in Model) {
		if ([ "hasOne", "hasMany",
		      "drop", "sync", "get", "find", "all", "count", "clear", "create",
		      "exists", "settings", "aggregate" ].indexOf(k) >= 0) continue;
		if (typeof Model[k] != "function") continue;

		chain[k] = Model[k];
	}
	return chain;
}

function addChainMethod(chain, association, opts) {
	chain[association.hasAccessor] = function (value) {
		if (!opts.exists) {
			opts.exists = [];
		}
		var conditions = {};

		conditions[association.mergeAssocId] = value;

		opts.exists.push({
			table      : association.mergeTable,
			link       : [ association.mergeId, association.model.id ],
			conditions : conditions
		});

		return chain;
	};
}
<|endoftext|>