<commit_before>/**
 * MaziScript
 *
 * @file MaziScript.js
 * @version 0.0.1
 * @description MaziScript的核心部分，负责Markdown的核心匹配和转义
 *
 * @license MIT License
 * @author Yesterday17
 * {@link http://www.yesterday17.cn}
 *
 * @updateTime 2016-12-09
 */

var MaziScript = function(){
    "use strict";

    var chapterData = {
        //信息
        origin : "",                   //原始文本
        handle : "",                   //处理的文本，随时会改变

        //辅助数据
        titleData : {
            start : -1,                //标题在origin中的开始位置（未执行rep操作）
            end : -1,                  //标题在handle中的结束位置（未执行rep操作）

            ctStart : -1,              //标题内容在handle中的开始位置
            ctEnd : -1                 //标题内容在handle中的结束位置
        },

        //MaziScript分部分
        title : "",                    //章节的标题，通过title(rep)识别
        horizontalPart : [],           //未经过分析的分隔线分开的部分
        content : {                    //正文部分
            text : [],                 //真正的文章内容 计算字数
            append : []                //不计入字数的作者赠送字数/感言
        },
        appendix : [],                 //附录部分，即原补记部分
        command : [],                  //从正文/附录等中获得的语句信息

        //MaziScript辅助信息存放
        flag : [],                     //被{}标记的内容

        //最终的内容
        final : ""
    };

    //创建chapterData的空拷贝 以便清空时使用
    var emptyChapterData = $.extend(true, {}, chapterData);

    var defaultOptions = {
        titlePrefix : true,
        replaceBracket : true,

        //段首空格规范
        paragraphProfix : true,
        paragraphProfixNum : 2
    };

    /**
     * titleStart参数 遗留产物 统一数据调用
     */
    Object.defineProperty(chapterData, "titleStart", {
        /**
         * titleStart get()
         *
         * @param {void}
         * @returns {Number} 和titleData.start相同
         */
        get : function(){
            return this.titleData.start;
        },

        /**
         * titleStart set()
         *
         * @param {Number} value 需要改成的数值
         * @returns {null}
         */
        set : function(value){
            if(typeof value == "number" && value != -1){
                this.titleData.start = value;
            }
        }
    });

    /**
     * titleEnd参数 遗留产物 统一数据调用
     */
    Object.defineProperty(chapterData, "titleEnd", {
        /**
         * titleEnd get()
         *
         * @param {void}
         * @returns {Number} 和titleData.end相同
         */
        get : function(){
            return this.titleData.end;
        },

         /**
         * titleEnd set()
         *
         * @param {Number} value 需要改成的数值
         * @returns {null}
         */
        set : function(value){
            if(typeof value == "number" && value != -1){
                this.titleData.end = value;
            }
        }
    });

    //正则表达式列表
    var regex = {
        title : /[\[【][^\]】]+[\]】]/g,                          //以[]括起的内容为标题
        flag : /\{[^\{\}]+\}/g,                                  //以{}括起的内容未标记
        language : /\n=[》>][^\n]+\n/g,                          //以=>开头的一行视为语句

        horizontal : /\n[#]+###\n/g,                            //大于等于四个#(####)的独立一行视作分隔线
        //Bug: 在遇到非下一个开始时便已经舍去一些值

        contentAppend : /\n[-]+---[^-]\n*/,
        //Bug: 同样的实现方法，同样的Bug

        escape : /[\\\'\*\_\{\}\[\]\(\)\#\+-\.!]/g               //Markdown中需要转义的字符
    };

    //为增强结构（可视化）进行的结构修正
    var structure = {
        titlePrefix : "##",
        paragraphPrefix : "　",

        contentFlag : "正文",
        appendixFlag : "附录"
    };

    //工具箱
    var utils = {
        /**
         * 去除字符串开头的指定字符
         *
         * @param {String} str 需要处理的字符串
         * @param {String} chars 需要移除的字符组成的字符串
         * @returns {String} 处理后的字符串
         */
        removeBeginningChars : function(str, chars){
            (function(){
                //注：str.length属性是会随时改变的 不是第一次记录的值
                var len = str.length;
                for(var i = 0 ; i <= len ; i++){
                    //console.log(str.length);
                    var flag = false;

                    for(var j = 0 ; j <= chars.length ; j++){
                        if(str[0] == chars[j]){
                            str = str.substring(1);
                            flag = true;
                            break;
                        }
                    }

                    if(!flag) break;
                }
            })();
            return str;
        },

        /**
         * 去除字符串末尾的指定字符
         *
         * @param {String} str 需要处理的字符串
         * @param {String} chars 需要移除的字符组成的字符串
         * @returns {String} 处理后的字符串
         */
        removeEndingChars : function(str, chars){
            (function(){
                var len = str.length;
                for(var i = 0 ; i <= len ; i++){
                    var flag = false;

                    for(var j = 0 ; j <= chars.length ; j++){
                        if(str[str.length - 1] == chars[j]){
                            str = str.substring(0, str.length - 1);
                            flag = true;
                            break;
                        }
                    }

                    if(!flag) break;
                }
            })();
            return str;
        },

        /**
         * 获得字符串移除了的字符数量
         *
         *
         */
        removedLength : function(origin, final){
            if(typeof origin == typeof final == "string"){
                return origin.length - final.length;
            }
            else{
                return -1;
            }
        },

        removeCrlf : function(str){
            return this.removeEndingChars(this.removeBeginningChars(str, "\n"), "\n");
        }
    }

    //工具箱的数据处理部分
    utils.data = {
        /**
         * 字数统计
         *
         * @param text 是否统计正文的字数
         * @param append 是否统计作者留言的字数
         * @param title 是否统计标题的字数
         */
        wordsCount : function(text, append, title){
            //设置默认值
            text = typeof text == "undefined" ? true : text;
            append = typeof append == "undefined" ? false : text;
            title = typeof title == "undefined" ? false : text;

            return (text === true ? chapterData.content.text.length : 0)
                 + (append === true ? chapterData.content.append.length : 0)
                 + (title === true ? chapterData.title.length : 0);
        }
    }

    /**
     * 获得章节的标题 保存并进行转义处理
     *
     * @param {Boolean} replaceBracket 是否在显示时将章节的方括号删除
     * @returns {void}
     */
    var title = function(replaceBracket){
        var rep = typeof replaceBracket === "boolean" ? replaceBracket : true;

        chapterData.handle = chapterData.handle.replace(regex.title, function(match, pos, originalText){
            if(chapterData.title.length != 0) return match;

            chapterData.title = match.substring(1, match.length - 1);

            //完成位置的对应
            chapterData.titleData.start = pos
            chapterData.titleData.end = pos + match.length;

            //测试，可以直接通过substring函数获得其内容
            //console.log(chapterData.origin.substring(chapterData.titleStart, chapterData.titleEnd));
            //注：titleStart和titleEnd是旧版本内容 现内容全部更正至chapterData.titleData.start/end

            if(rep) {
                chapterData.titleData.ctStart = chapterData.titleData.start + 1;
                chapterData.titleData.ctEnd = chapterData.titleData.end - 1;

                chapterData.final = (defaultOptions.titlePrefix ? structure.titlePrefix : "") + chapterData.title;
                return "";
            }
            else{
                chapterData.titleData.ctStart = chapterData.titleData.start;
                chapterData.titleData.ctEnd = chapterData.titleData.end;

                chapterData.final = (defaultOptions.titlePrefix ? structure.titlePrefix : "") + match;
                return "";
            }
        });
        utils.removeCrlf(chapterData.handle);
    }

    /**
     * 根据由#构成的分隔线区分MaziScript中的部分
     *
     * @param {void}
     * @returns {void}
     */
    var horizontal = function(){
        var pre = 0;
        chapterData.handle = chapterData.handle.replace(regex.horizontal, function(match, pos, originalText){
            chapterData.horizontalPart.push(
                utils.removeEndingChars(utils.removeBeginningChars((function(){
                    /*
                    if(pre == 0) {
                        pre = pos + match.length;
                        return match;
                    }
                    */

                    var ans = originalText.substring(pre, pos);
                    console.log(ans);
                    pre = pos + match.length;

                    return ans;
                }()), "#\n"), "\n")
            );

            return "";
            //return utils.removeBeginningChars(match, "#");
        });
        chapterData.horizontalPart.push(
            utils.removeEndingChars(utils.removeBeginningChars(chapterData.handle.substring(pre), "#\n"), "\n")
        );

        utils.removeCrlf(chapterData.handle);
    }

    /**
     * 根据每个部分的开头确定部分的处理办法
     *
     * @param {void}
     * @returns {void}
     */
    var definePart = function(){
        (function(){
            for(var i in chapterData.horizontalPart){
                var text = utils.removeCrlf(chapterData.horizontalPart[i]);
                var flagType = -1;

                text = text.replace(regex.flag, function(match, pos, originalText){
                    var flagContent = match.substring(1, match.length - 1);
                    var type = -1;

                    switch(flagContent){
                        case structure.contentFlag:
                            flagType = type = 1;
                            break;
                        case structure.appendixFlag:
                            flagType = type = 2;
                            break;
                        default:
                            chapterData.flag.push(flagContent);
                            type = -1;
                            break;
                    }

                    return type == 1 || type == 2 ? "" : flagContent;
                });

                text = text.replace(regex.language, function(match, pos, originalText){
                    chapterData.command.push(match.substring(3));
                    return "";
                });

                if(flagType == 1){
                    (function(){
                        //
                        //区分是否存在作者留言
                        var append = typeof text.match(regex.contentAppend) == "undefined";

                        if(append){
                            chapterData.content.push(text);
                        }
                        else{
                            text = text.replace(regex.contentAppend, function(match, pos, originalText){
                                chapterData.content.append.push(utils.removeBeginningChars(match, "-\n"));
                                return "\n";
                            });
                            chapterData.content.text.push(text);
                        }
                    })();
                }
                else if(flagType == 2){
                    chapterData.appendix.push(text);
                }
            }
        })();
    }

    /**
     * Markdown转义 整个过程的最后一步
     *
     * @param {void}
     * @returns {void}
     */
    var escape = function(){
        //TODO: 增加字符的转义，避免转义字符的使用
        (function(){
            for(var i in chapterData.content.text){
                chapterData.final += "\n" + chapterData.content.text[i].replace(regex.escape, "\\$&");
            }

            for(i in chapterData.content.append){
                chapterData.final += "\n" + chapterData.content.append[i].replace(regex.escape, "\\$&");
            }
        })();
        //chapterData.final += chapterData.handle.replace(regex.escape, "\\$&");
        //chapterData.handle = chapterData.handle.replace(regex.escape, "\\$&");
    }

    return {
        /**
         * 进行MaziScript文本解释
         *
         * @param {String} text 输入的文本
         * @returns {String} 处理后的Markdown文本
         */
        parse : function(text){
            this.clear();

            if(typeof text == "undefined"){
                chapterData.origin = chapterData.handle = "";
            }
            else{
                chapterData.origin = chapterData.handle = text;
            }

            //根据options确定是否移除括号
            title(defaultOptions.replaceBracket);
            horizontal();

            definePart();

            console.log(chapterData);

            //进行转义处理
            escape();

            if(chapterData.final != undefined){
                return chapterData.final;
            }
            else{
                return "";
            }
        },

        /**
         * 清空已经使用了的chapterData
         *
         * @param {void}
         * @returns {void}
         */
        clear : function(){
            chapterData = $.extend(true, {}, emptyChapterData);
        },

        /**
         * 输出调试信息
         *
         * @param {void}
         * @returns {void}
         */
        debug : function(){
            console.log(chapterData);
            console.log(emptyChapterData);
        }
    };

}();<commit_msg>修复horizontal的Bug<commit_after>/**
 * MaziScript
 *
 * @file MaziScript.js
 * @version 0.0.1
 * @description MaziScript的核心部分，负责Markdown的核心匹配和转义
 *
 * @license MIT License
 * @author Yesterday17
 * {@link http://www.yesterday17.cn}
 *
 * @updateTime 2016-12-09
 */

var MaziScript = function(){
    "use strict";

    var chapterData = {
        //信息
        origin : "",                   //原始文本
        handle : "",                   //处理的文本，随时会改变

        //辅助数据
        titleData : {
            start : -1,                //标题在origin中的开始位置（未执行rep操作）
            end : -1,                  //标题在handle中的结束位置（未执行rep操作）

            ctStart : -1,              //标题内容在handle中的开始位置
            ctEnd : -1                 //标题内容在handle中的结束位置
        },

        //MaziScript分部分
        title : "",                    //章节的标题，通过title(rep)识别
        horizontalPart : [],           //未经过分析的分隔线分开的部分
        content : {                    //正文部分
            text : [],                 //真正的文章内容 计算字数
            append : []                //不计入字数的作者赠送字数/感言
        },
        appendix : [],                 //附录部分，即原补记部分
        command : [],                  //从正文/附录等中获得的语句信息

        //MaziScript辅助信息存放
        flag : [],                     //被{}标记的内容

        //最终的内容
        final : ""
    };

    //创建chapterData的空拷贝 以便清空时使用
    var emptyChapterData = $.extend(true, {}, chapterData);

    var defaultOptions = {
        titlePrefix : true,
        replaceBracket : true,

        //段首空格规范
        paragraphProfix : true,
        paragraphProfixNum : 2
    };

    /**
     * titleStart参数 遗留产物 统一数据调用
     */
    Object.defineProperty(chapterData, "titleStart", {
        /**
         * titleStart get()
         *
         * @param {void}
         * @returns {Number} 和titleData.start相同
         */
        get : function(){
            return this.titleData.start;
        },

        /**
         * titleStart set()
         *
         * @param {Number} value 需要改成的数值
         * @returns {null}
         */
        set : function(value){
            if(typeof value == "number" && value != -1){
                this.titleData.start = value;
            }
        }
    });

    /**
     * titleEnd参数 遗留产物 统一数据调用
     */
    Object.defineProperty(chapterData, "titleEnd", {
        /**
         * titleEnd get()
         *
         * @param {void}
         * @returns {Number} 和titleData.end相同
         */
        get : function(){
            return this.titleData.end;
        },

         /**
         * titleEnd set()
         *
         * @param {Number} value 需要改成的数值
         * @returns {null}
         */
        set : function(value){
            if(typeof value == "number" && value != -1){
                this.titleData.end = value;
            }
        }
    });

    //正则表达式列表
    var regex = {
        title : /[\[【][^\]】]+[\]】]/g,                          //以[]括起的内容为标题
        flag : /\{[^\{\}]+\}/g,                                  //以{}括起的内容未标记
        language : /\n=[》>][^\n]+\n/g,                          //以=>开头的一行视为语句

        horizontal : /\n[#]+###\n/g,                            //大于等于四个#(####)的独立一行视作分隔线
        //Bug: 在遇到非下一个开始时便已经舍去一些值

        contentAppend : /\n[-]+---[^-]\n*/,
        //Bug: 同样的实现方法，同样的Bug

        escape : /[\\\'\*\_\{\}\[\]\(\)\#\+-\.!]/g               //Markdown中需要转义的字符
    };

    //为增强结构（可视化）进行的结构修正
    var structure = {
        titlePrefix : "##",
        paragraphPrefix : "　",

        contentFlag : "正文",
        appendixFlag : "附录"
    };

    //工具箱
    var utils = {
        /**
         * 去除字符串开头的指定字符
         *
         * @param {String} str 需要处理的字符串
         * @param {String} chars 需要移除的字符组成的字符串
         * @returns {String} 处理后的字符串
         */
        removeBeginningChars : function(str, chars){
            (function(){
                //注：str.length属性是会随时改变的 不是第一次记录的值
                var len = str.length;
                for(var i = 0 ; i <= len ; i++){
                    var flag = false;

                    for(var j = 0 ; j <= chars.length ; j++){
                        if(str[0] == chars[j]){
                            str = str.substring(1);
                            flag = true;
                            break;
                        }
                    }

                    if(!flag) break;
                }
            })();
            return str;
        },

        /**
         * 去除字符串末尾的指定字符
         *
         * @param {String} str 需要处理的字符串
         * @param {String} chars 需要移除的字符组成的字符串
         * @returns {String} 处理后的字符串
         */
        removeEndingChars : function(str, chars){
            (function(){
                var len = str.length;
                for(var i = 0 ; i <= len ; i++){
                    var flag = false;

                    for(var j = 0 ; j <= chars.length ; j++){
                        if(str[str.length - 1] == chars[j]){
                            str = str.substring(0, str.length - 1);
                            flag = true;
                            break;
                        }
                    }

                    if(!flag) break;
                }
            })();
            return str;
        },

        /**
         * 获得字符串移除了的字符数量
         *
         *
         */
        removedLength : function(origin, final){
            if(typeof origin == typeof final == "string"){
                return origin.length - final.length;
            }
            else{
                return -1;
            }
        },

        removeCrlf : function(str){
            return this.removeEndingChars(this.removeBeginningChars(str, "\n"), "\n");
        }
    }

    //工具箱的数据处理部分
    utils.data = {
        /**
         * 字数统计
         *
         * @param text 是否统计正文的字数
         * @param append 是否统计作者留言的字数
         * @param title 是否统计标题的字数
         */
        wordsCount : function(text, append, title){
            //设置默认值
            text = typeof text == "undefined" ? true : text;
            append = typeof append == "undefined" ? false : text;
            title = typeof title == "undefined" ? false : text;

            return (text === true ? chapterData.content.text.length : 0)
                 + (append === true ? chapterData.content.append.length : 0)
                 + (title === true ? chapterData.title.length : 0);
        }
    }

    /**
     * 获得章节的标题 保存并进行转义处理
     *
     * @param {Boolean} replaceBracket 是否在显示时将章节的方括号删除
     * @returns {void}
     */
    var title = function(replaceBracket){
        var rep = typeof replaceBracket === "boolean" ? replaceBracket : true;

        chapterData.handle = chapterData.handle.replace(regex.title, function(match, pos, originalText){
            if(chapterData.title.length != 0) return match;

            chapterData.title = match.substring(1, match.length - 1);

            //完成位置的对应
            chapterData.titleData.start = pos
            chapterData.titleData.end = pos + match.length;

            //测试，可以直接通过substring函数获得其内容
            //console.log(chapterData.origin.substring(chapterData.titleStart, chapterData.titleEnd));
            //注：titleStart和titleEnd是旧版本内容 现内容全部更正至chapterData.titleData.start/end

            if(rep) {
                chapterData.titleData.ctStart = chapterData.titleData.start + 1;
                chapterData.titleData.ctEnd = chapterData.titleData.end - 1;

                chapterData.final = (defaultOptions.titlePrefix ? structure.titlePrefix : "") + chapterData.title;
                return "";
            }
            else{
                chapterData.titleData.ctStart = chapterData.titleData.start;
                chapterData.titleData.ctEnd = chapterData.titleData.end;

                chapterData.final = (defaultOptions.titlePrefix ? structure.titlePrefix : "") + match;
                return "";
            }
        });
        utils.removeCrlf(chapterData.handle);
    }

    /**
     * 根据由#构成的分隔线区分MaziScript中的部分
     * 这一步结束以后chapterData.handle废弃
     *
     * @param {void}
     * @returns {void}
     */
    var horizontal = function(){
        var pre = 0, tpos = 0, sharp = 0;
        chapterData.handle = chapterData.handle.replace(regex.horizontal, function(match, pos, originalText){
            chapterData.horizontalPart.push(
                utils.removeEndingChars(utils.removeBeginningChars((function(){
                    var ans = originalText.substring(pre, pos);
                    pre = pos + match.length;
                    tpos = pos;sharp += match.length - 2;

                    return ans;
                }()), "#\n"), "\n")
            );

            return "";
        });
        chapterData.horizontalPart.push(
            utils.removeEndingChars(utils.removeBeginningChars(chapterData.handle.substring(tpos-sharp), "#\n"), "\n")
        );

        //去除因算法问题多余生成的空项
        chapterData.horizontalPart.shift();
    }

    /**
     * 根据每个部分的开头确定部分的处理办法
     *
     * @param {void}
     * @returns {void}
     */
    var definePart = function(){
        (function(){
            for(var i in chapterData.horizontalPart){
                var text = utils.removeCrlf(chapterData.horizontalPart[i]);
                var flagType = -1;

                text = text.replace(regex.flag, function(match, pos, originalText){
                    var flagContent = match.substring(1, match.length - 1);
                    var type = -1;

                    switch(flagContent){
                        case structure.contentFlag:
                            flagType = type = 1;
                            break;
                        case structure.appendixFlag:
                            flagType = type = 2;
                            break;
                        default:
                            chapterData.flag.push(flagContent);
                            type = -1;
                            break;
                    }

                    return type == 1 || type == 2 ? "" : flagContent;
                });

                text = text.replace(regex.language, function(match, pos, originalText){
                    chapterData.command.push(match.substring(3));
                    return "";
                });

                if(flagType == 1){
                    (function(){
                        //
                        //区分是否存在作者留言
                        var append = typeof text.match(regex.contentAppend) == "undefined";

                        if(append){
                            chapterData.content.push(text);
                        }
                        else{
                            text = text.replace(regex.contentAppend, function(match, pos, originalText){
                                chapterData.content.append.push(utils.removeBeginningChars(match, "-\n"));
                                return "\n";
                            });
                            chapterData.content.text.push(text);
                        }
                    })();
                }
                else if(flagType == 2){
                    chapterData.appendix.push(text);
                }
            }
        })();
    }

    /**
     * Markdown转义 整个过程的最后一步
     *
     * @param {void}
     * @returns {void}
     */
    var escape = function(){
        //TODO: 增加字符的转义，避免转义字符的使用
        (function(){
            for(var i in chapterData.content.text){
                chapterData.final += "\n" + chapterData.content.text[i].replace(regex.escape, "\\$&");
            }

            for(i in chapterData.content.append){
                chapterData.final += "\n" + chapterData.content.append[i].replace(regex.escape, "\\$&");
            }
        })();
        //chapterData.final += chapterData.handle.replace(regex.escape, "\\$&");
        //chapterData.handle = chapterData.handle.replace(regex.escape, "\\$&");
    }

    return {
        /**
         * 进行MaziScript文本解释
         *
         * @param {String} text 输入的文本
         * @returns {String} 处理后的Markdown文本
         */
        parse : function(text){
            this.clear();

            if(typeof text == "undefined"){
                chapterData.origin = chapterData.handle = "";
            }
            else{
                chapterData.origin = chapterData.handle = text;
            }

            //根据options确定是否移除括号
            title(defaultOptions.replaceBracket);
            horizontal();

            definePart();

            //进行转义处理
            escape();

            if(chapterData.final != undefined){
                return chapterData.final;
            }
            else{
                return "";
            }
        },

        /**
         * 清空已经使用了的chapterData
         *
         * @param {void}
         * @returns {void}
         */
        clear : function(){
            chapterData = $.extend(true, {}, emptyChapterData);
        },

        /**
         * 输出调试信息
         *
         * @param {void}
         * @returns {void}
         */
        debug : function(){
            console.log(chapterData);
            console.log(emptyChapterData);
        }
    };

}();<|endoftext|>


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

var $ = self.Bliss = function(expr, con) {
	return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
};

// Copy properties from one object to another. Overwrites allowed.
$.extend = function (to, from) {
	for (var property in from) {
		// To copy gettters/setters, preserve flags etc
		var descriptor = Object.getOwnPropertyDescriptor(from, property);

		if (descriptor && (!descriptor.writable || !descriptor.configurable || !descriptor.enumerable || descriptor.get || descriptor.set)) {
			Object.defineProperty(to, property, descriptor);
		}
		else {
			to[property] = from[property];
		}
	}

	return to;
}

$.extend($, {
	$: function(expr, con) {
		return expr instanceof Node || expr instanceof Window? [expr] :
		       [].slice.call(typeof expr == "string"? (con || document).querySelectorAll(expr) : expr || []);
	},

	/**
	 * Returns the [[Class]] of an object in lowercase (eg. array, date, regexp, string etc)
	 */
	type: function(obj) {
		if (obj === null) { return 'null'; }

		if (obj === undefined) { return 'undefined'; }

		var ret = (Object.prototype.toString.call(obj).match(/^\[object\s+(.*?)\]$/)[1] || "").toLowerCase();

		if(ret == 'number' && isNaN(obj)) {
			return 'NaN';
		}

		return ret;
	},

	/*
	 * Return first non-undefined value. Mainly used internally.
	 */
	firstDefined: function () {
		for (var i=0; i<arguments.length; i++) {
			if (arguments[i] !== undefined) {
				return arguments[i];
			}
		}
	},

	create: function (o) {
		var doc = o.document || document;

		if ($.type(o) === "string") {
			return doc.createTextNode(o);
		}

		var element = doc.createElement(o.tag);

		return element._.set(o);
	},

	// Clones any object, including DOM elements
	clone: function (o) {
		if (o === null || o === undefined) {
			return o;
		}

		var type = $.type(o);
		var clone;

		// Clone elements, with events
		// TODO instanceof Node doesn’t account for nodes from other documents
		if (o.cloneNode && o instanceof Node) {
			clone = o.cloneNode(true);

			var copyEvents = function(from, to) {

				var listeners = from._.bliss.listeners;

				for (var type in listeners) {
					listeners[type].forEach(function(l){
						to.addEventListener(type, l.callback, l.capture);
					});
				}
			};

			var cloneDescendants = $.$("*", clone);

			$.$("*", o).forEach(function(element, i, arr) {
				copyEvents(element, cloneDescendants[i]);
			});

			copyEvents(o, clone);
		}

		// TODO clone other types of objects

		return clone;
	},

	/*
	 * Fetch API inspired XHR helper. Returns promise.
	 */
	fetch: function(url, o) {
		// Set defaults & fixup arguments
		url = new URL(url);
		o = o || {};
		o.data = o.data || '';
		o.method = o.method || 'GET';
		o.headers = o.headers || {};
		o.onerror = o.onerror || $.fetch.onerror || null;

		var xhr = new XMLHttpRequest();

		if (o.method === "GET" && o.data) {
			url.search += o.data;
		}

		document.body.setAttribute('data-loading', url);

		xhr.open(o.method, url, !o.sync);

		for (var property in o) {

			if (property in xhr) {
				try {
					xhr[property] = o[property];
				}
				catch (e) {
					self.console && console.error(e);
				}
			}
		}

		if (o.method !== 'GET' && !o.headers['Content-type'] && !o.headers['Content-Type']) {
			xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		}

		for (var header in o.headers) {
			xhr.setRequestHeader(header, o.headers[header]);
		}

		return new Promise(function(resolve, reject){
			xhr.onload = function(){
				document.body.removeAttribute('data-loading');

				if (xhr.status === 0 || xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
					// Success
					resolve(xhr);
				}
				else if (o.onerror) {
					reject(Error(xhr.statusText));
				}

			};

			xhr.onerror = function() {
				reject(Error("Network Error"));
			};

			xhr.send(o.method === 'GET'? null : o.data);
		});
	}
});

$.Element = function (element) {
	this.element = element;

	// Author-defined element-related data
	this.data = {};

	// Internal Bliss element-related data
	this.bliss = {};
};

$.Element.prototype = {
	set: function (properties) {
		if (arguments.length == 2) {
			properties = {};
			properties[arguments[0]] = arguments[1];
		}

		for (var property in properties) {
			if (property in $.setSpecial) {
				$.setSpecial[property].call(this, properties[property]);
			}
			else if (property in this) {
				this[property] = properties[property];
			}
			else {
				this.setAttribute(property, properties[property]);
			}
		}

		return this;
	},

	// Remove element from the DOM, optionally with a fade
	remove: function(animation, duration) {
		if (animation && "transition" in this.style) {
			$.style(this, {
				transition: (duration || 400) + "ms",
				transitionProperty: Object.keys(animation).join(", ")
			});

			var me = this;
			this.addEventListener("transitionend", function(){
				$.remove(me);
			});

			$.style(this, animation);
		}
		else {
			this.parentNode && this.parentNode.removeChild(this);
		}
	},

	// Fire a synthesized event on the element
	fireEvent: function (type, properties) {
		var evt = document.createEvent("HTMLEvents");

		evt.initEvent(type, true, true );

		this.dispatchEvent($.extend(evt, properties));
	}
};

/*
 * Properties with custom handling in element._.set()
 * Also available as functions directly on element._ and on $
 */
$.setSpecial = {
	// Set a bunch of inline CSS styles
	style: function (val) {
		$.extend(this.style, val);
	},

	// Set a bunch of attributes
	attributes: function (val) {
		for (var attribute in val) {
			this.setAttribute(attribute, o[attribute]);
		}
	},

	// Set a bunch of properties on the element
	properties: function (val) {
		$.extend(this, val);
	},

	// Bind one or more events to the element
	events: function (val) {
		for (var events in val) {
			events.split(/\s+/).forEach(function (event) {
				this.addEventListener(event, val[events]);
			}, this);
		}
	},

	// Event delegation
	delegate: function(val) {
		if (arguments.length === 3) {
			// Called with ("type", "selector", callback)
			val = {};
			val[arguments[0]] = {};
			val[arguments[0]][arguments[1]] = arguments[2];
		}
		else if (arguments.length === 2) {
			// Called with ("type", selectors & callbacks)
			val = {};
			val[arguments[0]] = arguments[1];
		}

		var element = this;

		for (var type in val) {
			(function (type, callbacks) {
				element.addEventListener(type, function(evt) {
					for (var selector in callbacks) {
						if (evt.target.matches(selector)) { // Do ancestors count?
							callbacks[selector].call(this, evt);
						}
					}
				});
			})(type, val[type]);
		}
	},

	// Set the contents as a string, an element, an object to create an element or an array of these
	contents: function (val) {
		if(!val && val !== 0) {
			return;
		}

		(Array.isArray(val)? val : [val]).forEach(function (child) {
			if (["string", "number", "object"].indexOf($.type(child)) > -1) {
				child = $.create(child);
			}

			if (child instanceof Node) {
				(/^template$/i.test(this.nodeName)? this.content || this : this).appendChild(child);
			}
		}, this);
	},

	// Append the element inside another element
	inside: function (element) {
		element.appendChild(this);
	},

	// Insert the element before another element
	before: function (element) {
		element.parentNode.insertBefore(this, element);
	},

	// Insert the element after another element
	after: function (element) {
		element.parentNode.insertBefore(this, element.nextSibling);
	},

	// Insert the element before another element in the DOM
	start: function (element) {
		element.insertBefore(this, element.firstChild);
	},

	// Wrap the element around another element
	around: function (element) {
		if (element.parentNode) {
			$.setSpecial.before.call(this, element);
		}

		(/^template$/i.test(this.nodeName)? this.content || this : this).appendChild(element);
	}
};

$.Array = function (array) {
	this.array = array;
};

// Extends Bliss with more methods

$.add = function (methods, on) {
	on = $.extend({$: true, element: true, array: true}, on);

	if ($.type(arguments[0]) === "string") {
		methods = {};
		methods[arguments[0]] = arguments[1];
	}

	for (var method in methods) {
		try {
			var callback = methods[method];
		}
		catch (e) {
			continue;
		}

		(function(method, callback){

		if ($.type(callback) == "function") {
			if (on.$) {
				$[method] = function () {
					var args = [].slice.apply(arguments);
					var element = args.shift();

					return element && $.firstDefined(callback.apply(element, args), element);
				}
			}

			if (on.element) {
				$.Element.prototype[method] = function () {
					return $.firstDefined(callback.apply(this.element, arguments), this.element);
				}
			}

			if (on.array) {
				$.Array.prototype[method] = function() {
					var args = arguments;

					return this.array.map(function(element) {
						return $.firstDefined(callback.apply(element, args), element);
					});
				}
			}
		}

		})(method, callback);
	}
};

$.add($.Element.prototype);
$.add($.setSpecial);
$.add(HTMLElement.prototype, {$: false, element: false});

// Define the _ property on arrays and elements

Object.defineProperty(Element.prototype, "_", {
	get: function () {
		Object.defineProperty(this, "_", {
			value: new $.Element(this)
		});

		return this._;
	},
	configurable: true
});

Object.defineProperty(Array.prototype, "_", {
	get: function () {
		Object.defineProperty(this, "_", {
			value: new $.Array(this)
		});

		return this._;
	},
	configurable: true
});

// Hijack addEventListener and removeEventListener to store callbacks

if (self.EventTarget && "addEventListener" in EventTarget.prototype) {
	var addEventListener = EventTarget.prototype.addEventListener,
	    removeEventListener = EventTarget.prototype.removeEventListener,
	    filter = function(callback, capture, l){
	    	return !(l.callback === callback && l.capture == capture);
	    };

	EventTarget.prototype.addEventListener = function(type, callback, capture) {
		if (this._) {
			var listeners = this._.bliss.listeners = this._.bliss.listeners || {};

			listeners[type] = listeners[type] || [];

			if (listeners[type].filter(filter.bind(null, callback, capture)).length === 0) {
				listeners[type].push({callback: callback, capture: capture});
			}
		}

		return addEventListener.apply(this, arguments);
	};

	EventTarget.prototype.removeEventListener = function(type, callback, capture) {
		if (this._) {
			var listeners = this._.bliss.listeners = this._.bliss.listeners || {};

			listeners[type] = listeners[type] || [];
			listeners[type] = listeners[type].filter(filter.bind(null, callback, capture));
		}

		return removeEventListener.apply(this, arguments);
	};
}

// Export to global scope
self.$ = $;
self.$$ = $.$;

})();<commit_msg>Added helpers for: lazily evaluated properties, OOP-like classes, dynamically including scripts<commit_after>"use strict";
(function() {

var $ = self.Bliss = function(expr, con) {
	return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
};

// Copy properties from one object to another. Overwrites allowed.
$.extend = function (to, from) {
	for (var property in from) {
		// To copy gettters/setters, preserve flags etc
		var descriptor = Object.getOwnPropertyDescriptor(from, property);

		if (descriptor && (!descriptor.writable || !descriptor.configurable || !descriptor.enumerable || descriptor.get || descriptor.set)) {
			Object.defineProperty(to, property, descriptor);
		}
		else {
			to[property] = from[property];
		}
	}

	return to;
}

$.extend($, {
	$: function(expr, con) {
		return expr instanceof Node || expr instanceof Window? [expr] :
		       [].slice.call(typeof expr == "string"? (con || document).querySelectorAll(expr) : expr || []);
	},

	/**
	 * Returns the [[Class]] of an object in lowercase (eg. array, date, regexp, string etc)
	 */
	type: function(obj) {
		if (obj === null) { return 'null'; }

		if (obj === undefined) { return 'undefined'; }

		var ret = (Object.prototype.toString.call(obj).match(/^\[object\s+(.*?)\]$/)[1] || "").toLowerCase();

		if(ret == 'number' && isNaN(obj)) {
			return 'NaN';
		}

		return ret;
	},

	/*
	 * Return first non-undefined value. Mainly used internally.
	 */
	firstDefined: function () {
		for (var i=0; i<arguments.length; i++) {
			if (arguments[i] !== undefined) {
				return arguments[i];
			}
		}
	},

	create: function (o) {
		var doc = o.document || document;

		if ($.type(o) === "string") {
			return doc.createTextNode(o);
		}

		var element = doc.createElement(o.tag);

		return element._.set(o);
	},

	// Clones any object, including DOM elements
	clone: function (o) {
		if (o === null || o === undefined) {
			return o;
		}

		var type = $.type(o);
		var clone;

		// Clone elements, with events
		// TODO instanceof Node doesn’t account for nodes from other documents
		if (o.cloneNode && o instanceof Node) {
			clone = o.cloneNode(true);

			var copyEvents = function(from, to) {

				var listeners = from._.bliss.listeners;

				for (var type in listeners) {
					listeners[type].forEach(function(l){
						to.addEventListener(type, l.callback, l.capture);
					});
				}
			};

			var cloneDescendants = $.$("*", clone);

			$.$("*", o).forEach(function(element, i, arr) {
				copyEvents(element, cloneDescendants[i]);
			});

			copyEvents(o, clone);
		}

		// TODO clone other types of objects

		return clone;
	},

	// Lazily evaluated properties
	lazy: function(obj, property, getter) {
		if (arguments.length === 3) {
			Object.defineProperty(obj, property, {
				get: function() {
					delete this[property];

					return this[property] = getter.call(this);
				},
				configurable: true,
				enumerable: true
			});
		}
		else {
			for (var prop in property) {
				$.lazy(obj, prop, property[prop]);
			}
		}
	},

	// Helper for defining OOP-like “classes”
	Class: function(o) {
		//Super.apply(this, arguments);
		var init = o.constructor;
		delete o.constructor;

		var ret = function() {
			this.constructor.super.apply(this, arguments);
			return init.apply(this, arguments);
		};

		ret.super = o.extends || Object;
		delete o.extends;

		ret.prototype = $.extend(Object.create(ret.super.prototype), {
			constructor: ret
		});

		$.extend(ret, o.static);
		delete o.static;

		// Anything that remains is an instance method or ret.prototype.constructor
		$.extend(ret.prototype, o);

		return ret;
	},

	// Includes a script, returns a promise
	include: function(url) {
		var script = document.createElement("script");

		return new Promise(function(resolve, reject){
			$.set(script, {
				async: true,
				onload: function() {
					resolve();
				},
				onerror: function() {
					reject();
				},
				src: url,
				inside: document.head
			});
		});

	},

	/*
	 * Fetch API inspired XHR helper. Returns promise.
	 */
	fetch: function(url, o) {
		// Set defaults & fixup arguments
		url = new URL(url);
		o = o || {};
		o.data = o.data || '';
		o.method = o.method || 'GET';
		o.headers = o.headers || {};
		o.onerror = o.onerror || $.fetch.onerror || null;

		var xhr = new XMLHttpRequest();

		if (o.method === "GET" && o.data) {
			url.search += o.data;
		}

		document.body.setAttribute('data-loading', url);

		xhr.open(o.method, url, !o.sync);

		for (var property in o) {

			if (property in xhr) {
				try {
					xhr[property] = o[property];
				}
				catch (e) {
					self.console && console.error(e);
				}
			}
		}

		if (o.method !== 'GET' && !o.headers['Content-type'] && !o.headers['Content-Type']) {
			xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		}

		for (var header in o.headers) {
			xhr.setRequestHeader(header, o.headers[header]);
		}

		return new Promise(function(resolve, reject){
			xhr.onload = function(){
				document.body.removeAttribute('data-loading');

				if (xhr.status === 0 || xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
					// Success
					resolve(xhr);
				}
				else if (o.onerror) {
					reject(Error(xhr.statusText));
				}

			};

			xhr.onerror = function() {
				reject(Error("Network Error"));
			};

			xhr.send(o.method === 'GET'? null : o.data);
		});
	}
});

$.Element = function (element) {
	this.element = element;

	// Author-defined element-related data
	this.data = {};

	// Internal Bliss element-related data
	this.bliss = {};
};

$.Element.prototype = {
	set: function (properties) {
		if (arguments.length == 2) {
			properties = {};
			properties[arguments[0]] = arguments[1];
		}

		for (var property in properties) {
			if (property in $.setSpecial) {
				$.setSpecial[property].call(this, properties[property]);
			}
			else if (property in this) {
				this[property] = properties[property];
			}
			else {
				this.setAttribute(property, properties[property]);
			}
		}

		return this;
	},

	// Remove element from the DOM, optionally with a fade
	remove: function(animation, duration) {
		if (animation && "transition" in this.style) {
			$.style(this, {
				transition: (duration || 400) + "ms",
				transitionProperty: Object.keys(animation).join(", ")
			});

			var me = this;
			this.addEventListener("transitionend", function(){
				$.remove(me);
			});

			$.style(this, animation);
		}
		else {
			this.parentNode && this.parentNode.removeChild(this);
		}
	},

	// Fire a synthesized event on the element
	fireEvent: function (type, properties) {
		var evt = document.createEvent("HTMLEvents");

		evt.initEvent(type, true, true );

		this.dispatchEvent($.extend(evt, properties));
	}
};

/*
 * Properties with custom handling in element._.set()
 * Also available as functions directly on element._ and on $
 */
$.setSpecial = {
	// Set a bunch of inline CSS styles
	style: function (val) {
		$.extend(this.style, val);
	},

	// Set a bunch of attributes
	attributes: function (val) {
		for (var attribute in val) {
			this.setAttribute(attribute, o[attribute]);
		}
	},

	// Set a bunch of properties on the element
	properties: function (val) {
		$.extend(this, val);
	},

	// Bind one or more events to the element
	events: function (val) {
		for (var events in val) {
			events.split(/\s+/).forEach(function (event) {
				this.addEventListener(event, val[events]);
			}, this);
		}
	},

	// Event delegation
	delegate: function(val) {
		if (arguments.length === 3) {
			// Called with ("type", "selector", callback)
			val = {};
			val[arguments[0]] = {};
			val[arguments[0]][arguments[1]] = arguments[2];
		}
		else if (arguments.length === 2) {
			// Called with ("type", selectors & callbacks)
			val = {};
			val[arguments[0]] = arguments[1];
		}

		var element = this;

		for (var type in val) {
			(function (type, callbacks) {
				element.addEventListener(type, function(evt) {
					for (var selector in callbacks) {
						if (evt.target.matches(selector)) { // Do ancestors count?
							callbacks[selector].call(this, evt);
						}
					}
				});
			})(type, val[type]);
		}
	},

	// Set the contents as a string, an element, an object to create an element or an array of these
	contents: function (val) {
		if(!val && val !== 0) {
			return;
		}

		(Array.isArray(val)? val : [val]).forEach(function (child) {
			if (["string", "number", "object"].indexOf($.type(child)) > -1) {
				child = $.create(child);
			}

			if (child instanceof Node) {
				(/^template$/i.test(this.nodeName)? this.content || this : this).appendChild(child);
			}
		}, this);
	},

	// Append the element inside another element
	inside: function (element) {
		element.appendChild(this);
	},

	// Insert the element before another element
	before: function (element) {
		element.parentNode.insertBefore(this, element);
	},

	// Insert the element after another element
	after: function (element) {
		element.parentNode.insertBefore(this, element.nextSibling);
	},

	// Insert the element before another element in the DOM
	start: function (element) {
		element.insertBefore(this, element.firstChild);
	},

	// Wrap the element around another element
	around: function (element) {
		if (element.parentNode) {
			$.setSpecial.before.call(this, element);
		}

		(/^template$/i.test(this.nodeName)? this.content || this : this).appendChild(element);
	}
};

$.Array = function (array) {
	this.array = array;
};

// Extends Bliss with more methods

$.add = function (methods, on) {
	on = $.extend({$: true, element: true, array: true}, on);

	if ($.type(arguments[0]) === "string") {
		methods = {};
		methods[arguments[0]] = arguments[1];
	}

	for (var method in methods) {
		try {
			var callback = methods[method];
		}
		catch (e) {
			continue;
		}

		(function(method, callback){

		if ($.type(callback) == "function") {
			if (on.$) {
				$[method] = function () {
					var args = [].slice.apply(arguments);
					var element = args.shift();

					return element && $.firstDefined(callback.apply(element, args), element);
				}
			}

			if (on.element) {
				$.Element.prototype[method] = function () {
					return $.firstDefined(callback.apply(this.element, arguments), this.element);
				}
			}

			if (on.array) {
				$.Array.prototype[method] = function() {
					var args = arguments;

					return this.array.map(function(element) {
						return $.firstDefined(callback.apply(element, args), element);
					});
				}
			}
		}

		})(method, callback);
	}
};

$.add($.Element.prototype);
$.add($.setSpecial);
$.add(HTMLElement.prototype, {$: false, element: false});

// Define the _ property on arrays and elements

Object.defineProperty(Element.prototype, "_", {
	get: function () {
		Object.defineProperty(this, "_", {
			value: new $.Element(this)
		});

		return this._;
	},
	configurable: true
});

Object.defineProperty(Array.prototype, "_", {
	get: function () {
		Object.defineProperty(this, "_", {
			value: new $.Array(this)
		});

		return this._;
	},
	configurable: true
});

// Hijack addEventListener and removeEventListener to store callbacks

if (self.EventTarget && "addEventListener" in EventTarget.prototype) {
	var addEventListener = EventTarget.prototype.addEventListener,
	    removeEventListener = EventTarget.prototype.removeEventListener,
	    filter = function(callback, capture, l){
	    	return !(l.callback === callback && l.capture == capture);
	    };

	EventTarget.prototype.addEventListener = function(type, callback, capture) {
		if (this._) {
			var listeners = this._.bliss.listeners = this._.bliss.listeners || {};

			listeners[type] = listeners[type] || [];

			if (listeners[type].filter(filter.bind(null, callback, capture)).length === 0) {
				listeners[type].push({callback: callback, capture: capture});
			}
		}

		return addEventListener.apply(this, arguments);
	};

	EventTarget.prototype.removeEventListener = function(type, callback, capture) {
		if (this._) {
			var listeners = this._.bliss.listeners = this._.bliss.listeners || {};

			listeners[type] = listeners[type] || [];
			listeners[type] = listeners[type].filter(filter.bind(null, callback, capture));
		}

		return removeEventListener.apply(this, arguments);
	};
}

// Export to global scope
self.$ = $;
self.$$ = $.$;

})();<|endoftext|>


<commit_before>'use strict';

UniCollection._publications = {};
/**
 * Publish with mappings, this is the replacement of Meteor.publish.
 * It works for non-universe collections in the same way like Meteor.publish
 *
 * @param name Name of the record set.
 * If null, the set has no name, and the record set is automatically sent to all connected clients
 * (if you use mixin "PublishAccessMixin" then with access control)
 * @param handler {Function} Function called on the server each time a client subscribes.
 * Inside the function, this is the publish handler object, described below.
 * If the client passed arguments to subscribe, the function is called with the same arguments.
 * @param options {Object}
 * @param options.override {boolean} resets handler for publication name. (only named publication can be overridden)
 * @param options.userOnly {boolean} publication will be available only for users
 * @param options.adminOnly {boolean} publication will be available only for admins
 * @returns {*}
 */
UniCollection.publish = function (name, handler, options = {}) {
    if (!_.isFunction(handler)) {
        throw new Meteor.Error(404, 'UniCollection.publish: handler must be an function');
    }
    if (name) {
        let isAlreadyDefined = !!UniCollection._publications[name];
        if (isAlreadyDefined && (!options || !options.override)) {
            throw new Meteor.Error(403, 'Publication is already declared for name: "' + name + '", ' +
                'if you want override it, set override: true in options');
        }
        UniCollection._publications[name] = {handler, options};
        if (isAlreadyDefined) {
            return;
        }
    }
    var newHandler = function () {
        let {handler, options} = UniCollection._publications[name];
        if (options.userOnly && !this.userId) {
            return this.ready();
        }
        if (options.adminOnly && !UniUsers.isAdminLoggedIn()) {
            return this.ready();
        }
        this._directAdded = this.added;
        this._directChanged = this.changed;
        this._directRemoved = this.removed;
        this._uniMappingsObs = {};
        this._uniDocCounts = {};
        this._uniMappings = {};

        this.added = addedHandler;
        this.changed = changedHandler;
        this.removed = removedHandler;

        this._doMapping = _doMapping.bind(this);
        this._stopObserveHandlesAndCleanUp = _stopObserveHandlesAndCleanUp.bind(this);

        this.setMappings = function (collectionName, mappings) {
            if (_.isObject(collectionName) && collectionName._name) {
                collectionName = collectionName._name;
            }
            if (!_.isArray(mappings) && _.isObject(mappings) && mappings.collection) {
                mappings = [mappings];
            }
            if (!_.isArray(mappings)) {
                throw Meteor.Error(500, 'Parameter mappings must be an array of object');
            }
            if (!_.isString(collectionName)) {
                throw Meteor.Error(500, 'CollectionName must be a string or collection object');
            }
            this._uniMappings[collectionName] = mappings;
        };
        var curs = handler.apply(this, arguments);
        if (curs) {
            _eachCursorsCheck.call(this, curs);
        }
    };

    Meteor.publish(name, newHandler);
};

var _prepareUniDocCount = function (collectionName, id) {
    this._uniDocCounts[collectionName] = this._uniDocCounts[collectionName] || {};
    this._uniDocCounts[collectionName][id] =
        this._uniDocCounts[collectionName][id] || 0;
};

var addedHandler = function (collectionName, id, doc) {
    _prepareUniDocCount.call(this, collectionName, id, doc);
    var col = UniCollection._uniCollections[collectionName];
    //checks if no universe collection
    if (!col || !col._uniPublishAddedHandler) {
        this._uniDocCounts[collectionName][id]++;
        this._directAdded(collectionName, id, doc);
        this._doMapping(id, doc, collectionName);
        return true;
    }
    return col._uniPublishAddedHandler(this, collectionName, id, doc);

};

var changedHandler = function (collectionName, id, changedFields, allowedFields) {
    var col = UniCollection._uniCollections[collectionName];
    //checks if no universe collection
    if (!col || !col._uniPublishChangedHandler) {
        this._directChanged(collectionName, id, changedFields);
        this._doMapping(id, changedFields, collectionName);
        return;
    }
    return col._uniPublishChangedHandler(this, collectionName, id, changedFields, allowedFields);
};

var removedHandler = function (collectionName, id) {
    var col = UniCollection._uniCollections[collectionName];
    if (!col || !col._uniPublishRemovedHandler) {
        if (!this._uniDocCounts[collectionName] || this._uniDocCounts[collectionName][id] <= 0) {
            return;
        }
        --this._uniDocCounts[collectionName][id];
        if (!this._uniDocCounts[collectionName][id]) {
            delete this._uniDocCounts[collectionName][id];
            _stopObserveHandlesAndCleanUp.call(this, collectionName, id);
            return this._directRemoved(collectionName, id);
        }
    }
    return col._uniPublishRemovedHandler(this, collectionName, id);

};

var _eachCursorsCheck = function (curs, _parentDocId) {
    if (!_.isArray(curs)) {
        curs = [curs];
    }
    var sub = this;
    if (curs.length) {
        let handles = _.map(curs, function (cursor) {
            if (!_.isObject(cursor) || !cursor.observeChanges) {
                throw Meteor.Error(500, 'Publish function can only return a Cursor or an array of Cursors');
            }
            var collName = cursor._getCollectionName();
            var obs = {docs: {}, name: collName};
            var allowedFields = UniUtils.get(cursor._cursorDescription, 'options.fields');
            obs.handle = cursor.observeChanges({
                added  : function (id, fields) {
                    obs.docs[id] = true;
                    sub.added(collName, id, fields);
                },
                changed: function (id, fields) {
                    sub.changed(collName, id, fields, allowedFields);
                },
                removed: function (id) {
                    delete obs.docs[id];
                    sub.removed(collName, id);
                }
            });
            return obs;
        });
        if (!_parentDocId) {
            sub.ready();
        }
        sub.onStop(function () {
            _.each(handles, function (h) {
                h && h.handle && h.handle.stop();
            });
        });
        return handles;
    }
};

var _doMapping = function (id, doc, collectionName) {
    var mappings = this._uniMappings[collectionName];
    if (!mappings) {
        return;
    }
    var sub = this;
    var mapFilter;
    _.each(mappings, function (mapping) {
        mapFilter = {};
        if (mapping.reverse) {
            mapFilter[mapping.key] = id;
        } else {
            mapFilter._id = UniUtils.get(doc, mapping.key);
            if (!mapFilter._id) {
                return;
            }
            if (_.isArray(mapFilter._id)) {
                if (!mapFilter._id.length) {
                    return;
                }
                mapFilter._id = {
                    $in: mapFilter._id
                };
            }
        }
        _.extend(mapFilter, mapping.filter);
        var key = mapping.reverse && '_reverse';
        //stopping and clearing up of observers
        _stopObserveHandlesAndCleanUp.call(sub, collectionName, id, key);
        var handles = _eachCursorsCheck.call(sub, mapping.collection.find(mapFilter, mapping.options), id);
        //adding new observers
        handles && _saveObserveHandles.call(sub, collectionName, id, key, handles[0]);
    });

};

var _saveObserveHandles = function (collectionName, id, key, handles) {
    UniUtils.set(this._uniMappingsObs, collectionName + '.' + id + '.' + key, handles);
};

/**
 * Stops subscriptions and removes old docs
 * @context sub
 * @param collectionName
 * @param id
 * @param key
 * @private
 */

var _stopObserveHandlesAndCleanUp = function (collectionName, id, key) {
    var toStopping;
    var sub = this;
    if (key) {
        toStopping = {'key': UniUtils.get(sub._uniMappingsObs, collectionName + '.' + id + '.' + key)};
    } else {
        toStopping = UniUtils.get(sub._uniMappingsObs, collectionName + '.' + id) || {};
    }
    _.each(toStopping, function (obs) {
        if (obs) {
            obs.handle && obs.handle.stop();
            obs.docs && _.each(obs.docs, function (v, i) {
                v && sub.removed(obs.name, i);
            });
            obs.docs = {};
        }
    });
};
<commit_msg>fix #11<commit_after>'use strict';

UniCollection._publications = {};
/**
 * Publish with mappings, this is the replacement of Meteor.publish.
 * It works for non-universe collections in the same way like Meteor.publish
 *
 * @param name Name of the record set.
 * If null, the set has no name, and the record set is automatically sent to all connected clients
 * (if you use mixin "PublishAccessMixin" then with access control)
 * @param handler {Function} Function called on the server each time a client subscribes.
 * Inside the function, this is the publish handler object, described below.
 * If the client passed arguments to subscribe, the function is called with the same arguments.
 * @param options {Object}
 * @param options.override {boolean} resets handler for publication name. (only named publication can be overridden)
 * @param options.userOnly {boolean} publication will be available only for users
 * @param options.adminOnly {boolean} publication will be available only for admins
 * @returns {*}
 */
UniCollection.publish = function (name, handler, options = {}) {
    if (!_.isFunction(handler)) {
        throw new Meteor.Error(404, 'UniCollection.publish: handler must be an function');
    }
    if (name) {
        let isAlreadyDefined = !!UniCollection._publications[name];
        if (isAlreadyDefined && (!options || !options.override)) {
            throw new Meteor.Error(403, 'Publication is already declared for name: "' + name + '", ' +
                'if you want override it, set override: true in options');
        }
        UniCollection._publications[name] = {handler, options};
        if (isAlreadyDefined) {
            return;
        }
    }
    var newHandler = function () {
        let {handler, options} = UniCollection._publications[name];
        if (options.userOnly && !this.userId) {
            return this.ready();
        }
        if (options.adminOnly && !UniUsers.isAdminLoggedIn()) {
            return this.ready();
        }
        this._directAdded = this.added;
        this._directChanged = this.changed;
        this._directRemoved = this.removed;
        this._uniMappingsObs = {};
        this._uniDocCounts = {};
        this._uniMappings = {};

        this.added = addedHandler;
        this.changed = changedHandler;
        this.removed = removedHandler;

        this._doMapping = _doMapping.bind(this);
        this._stopObserveHandlesAndCleanUp = _stopObserveHandlesAndCleanUp.bind(this);

        this.setMappings = function (collectionName, mappings) {
            if (_.isObject(collectionName) && collectionName._name) {
                collectionName = collectionName._name;
            }
            if (!_.isArray(mappings) && _.isObject(mappings) && mappings.collection) {
                mappings = [mappings];
            }
            if (!_.isArray(mappings)) {
                throw Meteor.Error(500, 'Parameter mappings must be an array of object');
            }
            if (!_.isString(collectionName)) {
                throw Meteor.Error(500, 'CollectionName must be a string or collection object');
            }
            this._uniMappings[collectionName] = mappings;
        };
        var curs = handler.apply(this, arguments);
        if (curs) {
            _eachCursorsCheck.call(this, curs);
        }
    };

    Meteor.publish(name, newHandler);
};

var _prepareUniDocCount = function (collectionName, id) {
    this._uniDocCounts[collectionName] = this._uniDocCounts[collectionName] || {};
    this._uniDocCounts[collectionName][id] =
        this._uniDocCounts[collectionName][id] || 0;
};

var addedHandler = function (collectionName, id, doc) {
    _prepareUniDocCount.call(this, collectionName, id, doc);
    var col = UniCollection._uniCollections[collectionName];
    //checks if no universe collection
    if (!col || !col._uniPublishAddedHandler) {
        this._uniDocCounts[collectionName][id]++;
        this._directAdded(collectionName, id, doc);
        this._doMapping(id, doc, collectionName);
        return true;
    }
    return col._uniPublishAddedHandler(this, collectionName, id, doc);

};

var changedHandler = function (collectionName, id, changedFields, allowedFields) {
    var col = UniCollection._uniCollections[collectionName];
    //checks if no universe collection
    if (!col || !col._uniPublishChangedHandler) {
        this._directChanged(collectionName, id, changedFields);
        this._doMapping(id, changedFields, collectionName);
        return;
    }
    return col._uniPublishChangedHandler(this, collectionName, id, changedFields, allowedFields);
};

var removedHandler = function (collectionName, id) {
    var col = UniCollection._uniCollections[collectionName];
    if (!col || !col._uniPublishRemovedHandler) {
        if (!this._uniDocCounts[collectionName] || this._uniDocCounts[collectionName][id] <= 0) {
            return;
        }
        --this._uniDocCounts[collectionName][id];
        if (!this._uniDocCounts[collectionName][id]) {
            delete this._uniDocCounts[collectionName][id];
            _stopObserveHandlesAndCleanUp.call(this, collectionName, id);
            return this._directRemoved(collectionName, id);
        }
    }
    return col._uniPublishRemovedHandler(this, collectionName, id);

};

var _eachCursorsCheck = function (curs, _parentDocId) {
    if (!_.isArray(curs)) {
        curs = [curs];
    }
    var sub = this;
    if (curs.length) {
        let handles = _.map(curs, function (cursor) {
            if (!_.isObject(cursor) || !cursor.observeChanges) {
                throw Meteor.Error(500, 'Publish function can only return a Cursor or an array of Cursors');
            }
            var collName = cursor._getCollectionName();
            var obs = {docs: {}, name: collName};
            var allowedFields = UniUtils.get(cursor._cursorDescription, 'options.fields');
            obs.handle = cursor.observeChanges({
                added  : function (id, fields) {
                    obs.docs[id] = true;
                    sub.added(collName, id, fields);
                },
                changed: function (id, fields) {
                    sub.changed(collName, id, fields, allowedFields);
                },
                removed: function (id) {
                    delete obs.docs[id];
                    sub.removed(collName, id);
                }
            });
            return obs;
        });
        if (!_parentDocId) {
            sub.ready();
        }
        sub.onStop(function () {
            _.each(handles, function (h) {
                h && h.handle && h.handle.stop();
            });
        });
        return handles;
    }
};

var _doMapping = function (id, doc, collectionName) {
    var mappings = this._uniMappings[collectionName];
    if (!mappings) {
        return;
    }
    var sub = this;
    var mapFilter;
    _.each(mappings, function (mapping) {
        mapFilter = {};
        if (mapping.reverse) {
            mapFilter[mapping.key] = id;
        } else {
            mapFilter._id = UniUtils.get(doc, mapping.key);
            if (!mapFilter._id) {
                return;
            }
            if (_.isArray(mapFilter._id)) {
                if (!mapFilter._id.length) {
                    return;
                }
                mapFilter._id = {
                    $in: mapFilter._id
                };
            }
        }
        _.extend(mapFilter, mapping.filter);
        var key = mapping.reverse && '_reverse';
        var withCollectionName = (typeof mapping.collection !== 'string'
            ? (mapping.collection && mapping.collection._name)
            : mapping.collection);
        //stopping and clearing up of observers
        _stopObserveHandlesAndCleanUp.call(sub, collectionName, id, key, withCollectionName);
        var handles = _eachCursorsCheck.call(sub, mapping.collection.find(mapFilter, mapping.options), id);
        //adding new observers
        handles && _saveObserveHandles.call(sub, collectionName, id, key, withCollectionName, handles[0]);
    });

};

var _saveObserveHandles = function (collectionName, id, key, withCollectionName, handles) {
    if (withCollectionName) {
        withCollectionName = '_' + withCollectionName;
    } else {
        withCollectionName = withCollectionName || '';
    }
    key = key + withCollectionName;
    UniUtils.set(this._uniMappingsObs, collectionName + '.' + id + '.' + key, handles);
};

/**
 * Stops subscriptions and removes old docs
 * @context sub
 * @param collectionName
 * @param id
 * @param key
 * @param withCollectionName
 * @private
 */

var _stopObserveHandlesAndCleanUp = function (collectionName, id, key, withCollectionName) {
    var toStopping;
    var sub = this;
    if (withCollectionName) {
        withCollectionName = '_' + withCollectionName;
    } else {
        withCollectionName = withCollectionName || '';
    }
    key = key + withCollectionName;
    if (key) {
        toStopping = {'key': UniUtils.get(sub._uniMappingsObs, collectionName + '.' + id + '.' + key)};
    } else {
        toStopping = UniUtils.get(sub._uniMappingsObs, collectionName + '.' + id) || {};
    }
    _.each(toStopping, function (obs) {
        if (obs) {
            obs.handle && obs.handle.stop();
            obs.docs && _.each(obs.docs, function (v, i) {
                v && sub.removed(obs.name, i);
            });
            obs.docs = {};
        }
    });
};
<|endoftext|>


<commit_before>/*!
 * ws: a node.js websocket client
 * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
 * MIT Licensed
 */

try {
  module.exports = require('../build/Release/validation');
} catch (e) { try {
  module.exports = require('../build/default/validation');
} catch (e) { try {
  module.exports = require('./Validation.Fallback');
} catch (e) {
  console.error('validation.node seems to not have been built. Run npm install.');
  throw e;
}}}
<commit_msg>fixed bogus validation fallback filename<commit_after>/*!
 * ws: a node.js websocket client
 * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
 * MIT Licensed
 */

try {
  module.exports = require('../build/Release/validation');
} catch (e) { try {
  module.exports = require('../build/default/validation');
} catch (e) { try {
  module.exports = require('./Validation.fallback');
} catch (e) {
  console.error('validation.node seems to not have been built. Run npm install.');
  throw e;
}}}
<|endoftext|>


<commit_before>/* vim:ts=4:sts=4:sw=4:
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Ajax.org Code Editor (ACE).
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *      Fabian Jakobs <fabian AT ajax DOT org>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {
"no use strict";

var lang = require("./lib/lang");

var global = (function() {
    return this;
})();

var options = {
    packaged: false,
    workerPath: "",
    modePath: "",
    themePath: "",
    suffix: ".js"
};

exports.get = function(key) {
    if (!options.hasOwnProperty(key))
        throw new Error("Unknown config key: " + key);

    return options[key];
};

exports.set = function(key, value) {
    if (!options.hasOwnProperty(key))
        throw new Error("Unknown config key: " + key);

    options[key] = value;
};

exports.all = function() {
    return lang.copyObject(options);
};

exports.moduleUrl = function(name) {
    var parts = name.split("/");
    if (parts[0] !== "ace") return;

    var component = parts[1];
    var base = parts[2];

    if (component == "mode") {
        return this.get("modePath") + "/mode-" + base + this.get("suffix");
    } else if (component === "theme") {
        return this.get("themePath") + "/theme-" + base + this.get("suffix");
    } else if (component == "worker") {
        return this.get("workerPath") + "/" + base + this.get("suffix");
    }
};

exports.init = function() {
    options.packaged = require.packaged || module.packaged || (global.define && define.packaged);

    if (!global.document)
        return "";

    var scriptOptions = {};
    var scriptUrl = "";

    var scripts = document.getElementsByTagName("script");
    for (var i=0; i<scripts.length; i++) {
        var script = scripts[i];

        var src = script.src || script.getAttribute("src");
        if (!src) {
            continue;
        }

        var attributes = script.attributes;
        for (var j=0, l=attributes.length; j < l; j++) {
            var attr = attributes[j];
            if (attr.name.indexOf("data-ace-") === 0) {
                scriptOptions[deHyphenate(attr.name.replace(/^data-ace-/, ""))] = attr.value;
            }
        }

        var m = src.match(/^(.*)\/ace(\-\w+)?\.js(\?|$)/);
        if (m)
            scriptUrl = m[1];
    }

    if (scriptUrl) {
        scriptOptions.base = scriptOptions.base || scriptUrl;
        scriptOptions.packaged = true;
    }

    scriptOptions.workerPath = scriptOptions.workerPath || scriptOptions.base;
    scriptOptions.modePath = scriptOptions.modePath || scriptOptions.base;
    scriptOptions.themePath = scriptOptions.themePath || scriptOptions.base;
    delete scriptOptions.base;

    for (var key in scriptOptions)
        if (typeof scriptOptions[key] !== "undefined")
            exports.set(key, scriptOptions[key]);
};

function deHyphenate(str) {
    return str.replace(/-(.)/g, function(m, m1) { return m1.toUpperCase(); });
}

});
<commit_msg>Add worker suffix<commit_after>/* vim:ts=4:sts=4:sw=4:
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Ajax.org Code Editor (ACE).
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *      Fabian Jakobs <fabian AT ajax DOT org>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {
"no use strict";

var lang = require("./lib/lang");

var global = (function() {
    return this;
})();

var options = {
    packaged: false,
    workerPath: "",
    modePath: "",
    themePath: "",
    suffix: ".js"
};

exports.get = function(key) {
    if (!options.hasOwnProperty(key))
        throw new Error("Unknown config key: " + key);

    return options[key];
};

exports.set = function(key, value) {
    if (!options.hasOwnProperty(key))
        throw new Error("Unknown config key: " + key);

    options[key] = value;
};

exports.all = function() {
    return lang.copyObject(options);
};

exports.moduleUrl = function(name) {
    var parts = name.split("/");
    if (parts[0] !== "ace") return;

    var component = parts[1];
    var base = parts[2];

    if (component == "mode") {
        return this.get("modePath") + "/mode-" + base + this.get("suffix");
    } else if (component === "theme") {
        return this.get("themePath") + "/theme-" + base + this.get("suffix");
    } else if (component == "worker") {
        return this.get("workerPath") + "/worker-" + base + this.get("suffix");
    }
};

exports.init = function() {
    options.packaged = require.packaged || module.packaged || (global.define && define.packaged);

    if (!global.document)
        return "";

    var scriptOptions = {};
    var scriptUrl = "";

    var scripts = document.getElementsByTagName("script");
    for (var i=0; i<scripts.length; i++) {
        var script = scripts[i];

        var src = script.src || script.getAttribute("src");
        if (!src) {
            continue;
        }

        var attributes = script.attributes;
        for (var j=0, l=attributes.length; j < l; j++) {
            var attr = attributes[j];
            if (attr.name.indexOf("data-ace-") === 0) {
                scriptOptions[deHyphenate(attr.name.replace(/^data-ace-/, ""))] = attr.value;
            }
        }

        var m = src.match(/^(.*)\/ace(\-\w+)?\.js(\?|$)/);
        if (m)
            scriptUrl = m[1];
    }

    if (scriptUrl) {
        scriptOptions.base = scriptOptions.base || scriptUrl;
        scriptOptions.packaged = true;
    }

    scriptOptions.workerPath = scriptOptions.workerPath || scriptOptions.base;
    scriptOptions.modePath = scriptOptions.modePath || scriptOptions.base;
    scriptOptions.themePath = scriptOptions.themePath || scriptOptions.base;
    delete scriptOptions.base;

    for (var key in scriptOptions)
        if (typeof scriptOptions[key] !== "undefined")
            exports.set(key, scriptOptions[key]);
};

function deHyphenate(str) {
    return str.replace(/-(.)/g, function(m, m1) { return m1.toUpperCase(); });
}

});
<|endoftext|>


<commit_before>import { setActiveSession } from './sessions';
import { keys } from '../utils/object';
import { last } from '../utils/array';
import rpc from '../rpc';
import {
  requestSession,
  sendSessionData
} from '../actions/sessions';
import {
  UI_FONT_SIZE_SET,
  UI_FONT_SIZE_INCR,
  UI_FONT_SIZE_DECR,
  UI_FONT_SIZE_RESET,
  UI_FONT_SMOOTHING_SET,
  UI_MOVE_LEFT,
  UI_MOVE_RIGHT,
  UI_MOVE_TO,
  UI_SHOW_PREFERENCES,
  UI_WINDOW_MOVE
} from '../constants/ui';

export function increaseFontSize () {
  return (dispatch, getState) => {
    dispatch({
      type: UI_FONT_SIZE_INCR,
      effect () {
        const state = getState();
        const old = state.ui.fontSizeOverride || state.ui.fontSize;
        const value = old + 1;
        dispatch({
          type: UI_FONT_SIZE_SET,
          value
        });
      }
    });
  };
}

export function decreaseFontSize () {
  return (dispatch, getState) => {
    dispatch({
      type: UI_FONT_SIZE_DECR,
      effect () {
        const state = getState();
        const old = state.ui.fontSizeOverride || state.ui.fontSize;
        const value = old - 1;
        dispatch({
          type: UI_FONT_SIZE_SET,
          value
        });
      }
    });
  };
}

export function resetFontSize () {
  return {
    type: UI_FONT_SIZE_RESET
  };
}

export function setFontSmoothing () {
  const devicePixelRatio = window.devicePixelRatio;
  const fontSmoothing = devicePixelRatio < 2
    ? 'subpixel-antialiased'
    : 'antialiased';

  return {
    type: UI_FONT_SMOOTHING_SET,
    fontSmoothing
  };
}

export function moveLeft () {
  return (dispatch, getState) => {
    dispatch({
      type: UI_MOVE_LEFT,
      effect () {
        const { sessions } = getState();
        const uid = sessions.activeUid;
        const sessionUids = keys(sessions.sessions);
        const index = sessionUids.indexOf(uid);
        const next = sessionUids[index - 1] || last(sessionUids);
        if (!next || uid === next) {
          console.log('ignoring left move action');
        } else {
          dispatch(setActiveSession(next));
        }
      }
    });
  };
}

export function moveRight () {
  return (dispatch, getState) => {
    dispatch({
      type: UI_MOVE_RIGHT,
      effect () {
        const { sessions } = getState();
        const uid = sessions.activeUid;
        const sessionUids = keys(sessions.sessions);
        const index = sessionUids.indexOf(uid);
        const next = sessionUids[index + 1] || sessionUids[0];
        if (!next || uid === next) {
          console.log('ignoring right move action');
        } else {
          dispatch(setActiveSession(next));
        }
      }
    });
  };
}

export function moveTo (i) {
  return (dispatch, getState) => {
    dispatch({
      type: UI_MOVE_TO,
      index: i,
      effect () {
        const { sessions } = getState();
        const uid = sessions.activeUid;
        const sessionUids = keys(sessions.sessions);
        if (uid === sessionUids[i]) {
          console.log('ignoring same uid');
        } else if (null != sessionUids[i]) {
          dispatch(setActiveSession(sessionUids[i]));
        } else {
          console.log('ignoring inexistent index', i);
        }
      }
    });
  };
}

export function showPreferences () {
  return (dispatch, getState) => {
    dispatch({
      type: UI_SHOW_PREFERENCES,
      effect () {
        dispatch(requestSession());
        // TODO: replace this hack with an async action
        rpc.once('session add', ({ uid }) => {
          rpc.once('session data', () => {
            dispatch(sendSessionData(
              uid,
              ['echo Attempting to open ~/.hyperterm.js with your \$EDITOR', // eslint-disable-line no-useless-escape
               'echo If it fails, open it manually with your favorite editor!',
               'bash -c "exec env $EDITOR ~/.hyperterm.js"',
               ''
              ].join('\n')
            ));
          });
        });
      }
    });
  };
}

export function windowMove () {
  return (dispatch) => {
    dispatch({
      type: UI_WINDOW_MOVE,
      effect () {
        dispatch(setFontSmoothing());
      }
    });
  };
}
<commit_msg>Add a slight timeout to update the fontSmoothing pref (#407)<commit_after>import { setActiveSession } from './sessions';
import { keys } from '../utils/object';
import { last } from '../utils/array';
import rpc from '../rpc';
import {
  requestSession,
  sendSessionData
} from '../actions/sessions';
import {
  UI_FONT_SIZE_SET,
  UI_FONT_SIZE_INCR,
  UI_FONT_SIZE_DECR,
  UI_FONT_SIZE_RESET,
  UI_FONT_SMOOTHING_SET,
  UI_MOVE_LEFT,
  UI_MOVE_RIGHT,
  UI_MOVE_TO,
  UI_SHOW_PREFERENCES,
  UI_WINDOW_MOVE
} from '../constants/ui';

export function increaseFontSize () {
  return (dispatch, getState) => {
    dispatch({
      type: UI_FONT_SIZE_INCR,
      effect () {
        const state = getState();
        const old = state.ui.fontSizeOverride || state.ui.fontSize;
        const value = old + 1;
        dispatch({
          type: UI_FONT_SIZE_SET,
          value
        });
      }
    });
  };
}

export function decreaseFontSize () {
  return (dispatch, getState) => {
    dispatch({
      type: UI_FONT_SIZE_DECR,
      effect () {
        const state = getState();
        const old = state.ui.fontSizeOverride || state.ui.fontSize;
        const value = old - 1;
        dispatch({
          type: UI_FONT_SIZE_SET,
          value
        });
      }
    });
  };
}

export function resetFontSize () {
  return {
    type: UI_FONT_SIZE_RESET
  };
}

export function setFontSmoothing () {
  return (dispatch) => {
    setTimeout(() => {
      const devicePixelRatio = window.devicePixelRatio;
      const fontSmoothing = devicePixelRatio < 2
        ? 'subpixel-antialiased'
        : 'antialiased';

      dispatch({
        type: UI_FONT_SMOOTHING_SET,
        fontSmoothing
      });
    }, 100);
  };
}

export function moveLeft () {
  return (dispatch, getState) => {
    dispatch({
      type: UI_MOVE_LEFT,
      effect () {
        const { sessions } = getState();
        const uid = sessions.activeUid;
        const sessionUids = keys(sessions.sessions);
        const index = sessionUids.indexOf(uid);
        const next = sessionUids[index - 1] || last(sessionUids);
        if (!next || uid === next) {
          console.log('ignoring left move action');
        } else {
          dispatch(setActiveSession(next));
        }
      }
    });
  };
}

export function moveRight () {
  return (dispatch, getState) => {
    dispatch({
      type: UI_MOVE_RIGHT,
      effect () {
        const { sessions } = getState();
        const uid = sessions.activeUid;
        const sessionUids = keys(sessions.sessions);
        const index = sessionUids.indexOf(uid);
        const next = sessionUids[index + 1] || sessionUids[0];
        if (!next || uid === next) {
          console.log('ignoring right move action');
        } else {
          dispatch(setActiveSession(next));
        }
      }
    });
  };
}

export function moveTo (i) {
  return (dispatch, getState) => {
    dispatch({
      type: UI_MOVE_TO,
      index: i,
      effect () {
        const { sessions } = getState();
        const uid = sessions.activeUid;
        const sessionUids = keys(sessions.sessions);
        if (uid === sessionUids[i]) {
          console.log('ignoring same uid');
        } else if (null != sessionUids[i]) {
          dispatch(setActiveSession(sessionUids[i]));
        } else {
          console.log('ignoring inexistent index', i);
        }
      }
    });
  };
}

export function showPreferences () {
  return (dispatch, getState) => {
    dispatch({
      type: UI_SHOW_PREFERENCES,
      effect () {
        dispatch(requestSession());
        // TODO: replace this hack with an async action
        rpc.once('session add', ({ uid }) => {
          rpc.once('session data', () => {
            dispatch(sendSessionData(
              uid,
              ['echo Attempting to open ~/.hyperterm.js with your \$EDITOR', // eslint-disable-line no-useless-escape
               'echo If it fails, open it manually with your favorite editor!',
               'bash -c "exec env $EDITOR ~/.hyperterm.js"',
               ''
              ].join('\n')
            ));
          });
        });
      }
    });
  };
}

export function windowMove () {
  return (dispatch) => {
    dispatch({
      type: UI_WINDOW_MOVE,
      effect () {
        dispatch(setFontSmoothing());
      }
    });
  };
}
<|endoftext|>


<commit_before>module.exports = function(app, express) {

    var stylus  = require('stylus')
      , url     = require('url')
      , expose  = require('express-expose')
      , redis   = require('./redis_helper.js')
      , nib     = require('nib');

    app.configure(function() {
        app.set('views', __dirname + '/../../pub/views');
        app.set('view engine', 'jade');
        app.set('view options', { pretty: true, layout: true });
        app.use(express.bodyParser());
        app.use(express.methodOverride());

        //app.use(stylus.middleware({
            //src    : __dirname + '/../../pub'
          //, debug  : true
          //, compile: function(str, path) {
                //return stylus(str)
                   //.set('filename', path)
                   //.set('compress', true)
                   //.use(nib())
                   //.import('nib');
            //}
        //}));

        app.use(express.cookieParser());

        var RedisStore = require('connect-redis')(express);
        app.use(express.session({secret: 'aloha', store: new RedisStore})); // TODO: remove

        //app.use(express.session({
            //secret: 'secret'
          //, store : redis.store(express)
          //, cookie: { maxAge: 900000 }
        //}));
        app.use(app.router);
        app.use(express.static(__dirname + '/../../pub'));
    });

    app.configure('test', function() {
        app.use(express.logger());
        app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
        app.set('redisdb', 2);
    });

    app.configure('development', function() {
        app.use(express.logger());
        app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
        app.set('redisdb', 1);
    });

    app.configure('production', function() {
        app.use(express.logger());
        app.use(express.errorHandler());
        app.set('redisdb', 0);
    });

    app.dynamicHelpers({
        session: function(req, res) {
            return req.session;
        }
      , flash: function(req, res) {
            return req.flash();
        }
    });
};
<commit_msg>db settings<commit_after>module.exports = function(app, express) {

    var stylus  = require('stylus')
      , url     = require('url')
      , expose  = require('express-expose')
      , redis   = require('./redis_helper.js')
      , nib     = require('nib');

    app.configure(function() {
        app.set('views', __dirname + '/../../pub/views');
        app.set('view engine', 'jade');
        app.set('view options', { pretty: true, layout: true });
        app.use(express.bodyParser());
        app.use(express.methodOverride());

        //app.use(stylus.middleware({
            //src    : __dirname + '/../../pub'
          //, debug  : true
          //, compile: function(str, path) {
                //return stylus(str)
                   //.set('filename', path)
                   //.set('compress', true)
                   //.use(nib())
                   //.import('nib');
            //}
        //}));

        app.use(express.cookieParser());
        app.use(express.session({
            secret: 'secret'
          , store : redis.store(express)
          , cookie: { maxAge: 900000 }
        }));
        app.use(app.router);
        app.use(express.static(__dirname + '/../../pub'));
    });

    app.configure('test', function() {
        app.use(express.logger());
        app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
        app.set('redisdb', 2);
    });

    app.configure('development', function() {
        app.use(express.logger());
        app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
        app.set('redisdb', 1);
    });

    app.configure('production', function() {
        app.use(express.logger());
        app.use(express.errorHandler());
        app.set('redisdb', 0);
    });

    app.dynamicHelpers({
        session: function(req, res) {
            return req.session;
        }
      , flash: function(req, res) {
            return req.flash();
        }
    });
};
<|endoftext|>