<commit_before>var scode = {};

(function(__) {

    __.ext = function() {
        var buff = {}, arg;
        for(var i = 0, j = arguments.length;i < j;i++) {
            arg = arguments[i];
            if(arg) {
                for(var a in arg) {
                    buff[a] = typeof arg[a] === 'object' ? __.ext(buff[a], arg[a]) : arg[a];
                }
            }
        }
        return buff;
    };
    (function() {

        var el = document.createElement('div');
        var isEventSupport = function(name) {
            var el = window
            var ename = 'on'+name.toLowerCase();
            if( ename in el) {
                return true;
            }
            var isSupport = false;
            if(el.setAttribute) {
                el.setAttribute(ename, 'return;');
                isSupport = typeof el[ename] === 'function';
                el.removeAttribute(ename);
            }
            return isSupport;
        }
        var listeners = {
            items:[],
            get: function(type, callback, bind, create) {
                var item;
                for(var i = 0, j = this.items.length;i < j;i++) {
                    var item = this.items[i];
                    if(item&&item[1] === type&&item[2] === callback&&item[2] === bind) {
                        return item[0];
                    }
                }
                if(create) {
                    var listener = function(e) {
                        var e = e || window.event;

                        if(e.type === 'mousewheel') {
                            e.$wheel = Math.abs(e.wheelDelta)/e.wheelDelta;
                        } else if(e.type === 'DOMMouseScroll') {
                            e.$wheel = -Math.abs(e.detail)/e.detail;
                            if(e.axis) {
                                if(e.axis == e.HORIZONTAL_AXIS) {
                                    e.$axis = "horizontal";
                                } else {
                                    e.$axis = "vertical";
                                }
                            } else if(e.wheelDeltaX&&e.wheelDeltaX === e.wheelDelta) {
                                e.$axis = "horizontal";
                            } else {
                                e.$axis = "vertical";
                            }
                        }
                        if(!e.preventDefault){
                            e.preventDefault = function(){
                                e.returnValue = false;
                            };
                        }
                        if(!e.stopPropagation){
                            e.stopPropagation = function(){
                                e.cancelBuble = true;
                            };
                        }
                        e.$stop = function(){
                            e.preventDefault();
                            e.stopPropagation();
                        };
                        callback.call(bind, e);
                    };
                    this.items.push([listener, type, callback, bind]);
                    return listener;
                }
                return null;
            }
        };
        __.on = function(target, type, callback, bind) {
            if(type === 'mousewheel') {
                if(!isEventSupport('mousewheel')) {
                    type = 'DOMMouseScroll';
                }
            }

            bind = bind||null;
            var listener = listeners.get(type, callback, bind, true);
            if(target.addEventListener) {
                target.addEventListener(type, listener, false);
                return {
                    remove: function() {
                        target.removeEventListener(type, listener, false);
                    }
                };
            }
        };
        __.off = function(target, type, callback, bind) {
            bind = bind||null;
            var listener = listeners.get(type, callback, bind);
            if(listener&&target.addEventListener) {
                target.removeEventListener(type, listener, false);
            }
        }
    })();

    var isArray = function(o) {
        return Object.prototype.toString.call(o) === '[object Array]';
    };
    var isFn = function(o) {
        return typeof o === 'function';
    };
    var $merge = function(prev, cur) {
        if(prev&&prev !== cur) {
            var ct = typeof cur;
            var pt = typeof prev;
            if(ct === pt) {
                if(ct === 'function') {
                    var m = function() {
                        this.$base = prev;
                        return cur.apply(this, arguments);
                    };
                    return m;
                }
            }
        }
        return cur;
    };

    var $proto = function(f) {
        f.$proto = true;
        var proto = new f();
        delete f.$proto;
        return proto;
    };
    var Class = function() {
        if(Class.$proto) {
            return;
        }
        if(this instanceof Class) {
            throw Error('Incorect using component Class');
        }
        var _p = $proto(Class);
        var _a = {};

        var _ext = function(obj) {
            if(isFn(obj)) {
                obj = $proto(obj);
            }
            var ii = null;
            for(var i in obj) {
                if(isFn(obj[i])) {
                    ii = i === 'init' ? '__init__' : i;
                    _p[ii] = $merge(_p[ii], obj[i]);
                } else {
                    _a[i] = obj[i];
                }
            }
        };
        //base wraper
        var klass = function() {
            for(var i in _a) {
                this[i] = sjs.clone(_a[i]);
            }
            if(!klass.$proto) {
                if(isFn(this.__init__)) {
                    return this.__init__.apply(this, arguments);
                }
            }
            return this;
        };
        var args = Array.prototype.slice.call(arguments);
        var pos = 0, next, i, l;
        while(args.length) {
            next = args.shift();
            if(!next) {
                continue;
            }
            var type = typeof next;
            if(type === 'function'&&pos === 0) {
                //extend
                _p = $proto(next);

                //find variable
                for(i in _p) {
                    if(!isFn(_p[i])) {
                        _a[i] = _p[i];
                        delete _p[i];
                    }
                }
                continue;
            } else if(type === 'object') {
                if(isArray(next)) {
                    //implement
                    for( i = 0, l = next.length;i < l;i++) {
                        _ext(next[i]);
                    }
                } else {
                    //instance
                    _ext(next);

                    //static
                    next = args.shift()|| {};
                    for(i in next) {
                        klass[i] = next[i];
                    }
                    break;
                }
            }
        }
        klass.prototype = _p;
        klass.constructor = Class;
        return klass;
    };
    __.Class = Class;

})(scode);
<commit_msg>rewrite dom events<commit_after>var scode = {};

(function(__) {

    __.ext = function() {
        var buff = {}, arg;
        for(var i = 0, j = arguments.length;i < j;i++) {
            arg = arguments[i];
            if(arg) {
                for(var a in arg) {
                    buff[a] = typeof arg[a] === 'object' ? __.ext(buff[a], arg[a]) : arg[a];
                }
            }
        }
        return buff;
    };
    (function() {

        var el = document.createElement('div');
        var isEventSupport = function(name) {
            var el = window
            var ename = 'on'+name.toLowerCase();
            if( ename in el) {
                return true;
            }
            var isSupport = false;
            if(el.setAttribute) {
                el.setAttribute(ename, 'return;');
                isSupport = typeof el[ename] === 'function';
                el.removeAttribute(ename);
            }
            return isSupport;
        }
        var _codes = {
            38:'up',
            39:'right',
            40:'down',
            37:'left',
            16:'shift',
            17:'control',
            18:'alt',
            9:'tab',
            13:'enter',
            36:'home',
            35:'end',
            33:'pageup',
            34:'pagedown',
            45:'insert',
            46:'delete',
            27:'escape',
            32:'space',
            8:'backspace'
        };

        var listeners = {
            items:[],
            get: function(type, callback, bind, create) {
                var item;
                for(var i = 0, j = this.items.length;i < j;i++) {
                    var item = this.items[i];
                    if(item&&item[1] === type&&item[2] === callback&&item[2] === bind) {
                        return item[0];
                    }
                }
                if(create) {

                    var listener = function(e) {
                        var e = e||window.event;
                        var type = e.type;

                        e.$shift = event.shiftKey;
                        e.$control = event.ctrlKey;
                        e.$alt = event.altKey;
                        e.$meta = event.metaKey;

                        var target = e.target||e.srcElement;
                        while(target&&target.nodeType === 3) {
                            target = target.parentNode;
                        }
                        e.$target = target;
                        if(type.indexOf('key') === 0) {
                            var code = e.which||e.keyCode;

                            if(_codes[code]) {
                                e.$key = _codes[code];
                            } else if(type == 'keydown'||type == 'keyup') {
                                if(code > 111&&code < 124) {
                                    e.$key = 'f'+(code-111);
                                } else if(code > 95&&code < 106) {
                                    e.$key = code-96;
                                }else{
                                    e.$key = String.fromCharCode(code).toLowerCase();
                                }
                            }
                        } else if(type === 'click'||type === 'dbclick'||type.indexOf('mouse') === 0||type === 'DOMMouseScroll'||type === 'contextmenu') {
                            //mootools
                            var doc = (!document.compatMode||document.compatMode == 'CSS1Compat') ? document.html : document.body;
                            e.$page = {
                                x:(e.pageX !== null) ? e.pageX : e.clientX+document.body.scrollLeft,
                                x:(e.pageY !== null) ? e.pageY : e.clientY+document.body.scrollTop
                            };
                            e.$client = {
                                x:(e.pageX != null) ? e.pageX-window.pageXOffset : e.clientX,
                                y:(e.pageY != null) ? e.pageY-window.pageYOffset : e.clientY
                            };
                            e.$right = (e.which == 3||e.button == 2);

                            if(e.type === 'mousewheel'||e.type === 'DOMMouseScroll') {
                                e.$wheel = (event.wheelDelta) ? event.wheelDelta/120 : -(event.detail||0)/3
                                if(e.axis) {
                                    if(e.axis == e.HORIZONTAL_AXIS) {
                                        e.$axis = "horizontal";
                                    } else {
                                        e.$axis = "vertical";
                                    }
                                } else if(e.wheelDeltaX&&e.wheelDeltaX === e.wheelDelta) {
                                    e.$axis = "horizontal";
                                } else {
                                    e.$axis = "vertical";
                                }
                            }
                        }
                        if(!e.preventDefault) {
                            e.preventDefault = function() {
                                e.returnValue = false;
                            };
                        }
                        if(!e.stopPropagation) {
                            e.stopPropagation = function() {
                                e.cancelBuble = true;
                            };
                        }
                        e.$stop = function() {
                            e.preventDefault();
                            e.stopPropagation();
                        };
                        callback.call(bind, e);
                    };
                    this.items.push([listener, type, callback, bind]);
                    return listener;
                }
                return null;
            }
        };
        __.on = function(target, type, callback, bind) {
            if(type === 'mousewheel') {
                if(!isEventSupport('mousewheel')) {
                    type = 'DOMMouseScroll';
                }
            }

            bind = bind||null;
            var listener = listeners.get(type, callback, bind, true);
            if(target.addEventListener) {
                target.addEventListener(type, listener, false);
                return {
                    remove: function() {
                        target.removeEventListener(type, listener, false);
                    }
                };
            }
        };
        __.off = function(target, type, callback, bind) {
            bind = bind||null;
            var listener = listeners.get(type, callback, bind);
            if(listener&&target.addEventListener) {
                target.removeEventListener(type, listener, false);
            }
        }
    })();

    var isArray = function(o) {
        return Object.prototype.toString.call(o) === '[object Array]';
    };
    var isFn = function(o) {
        return typeof o === 'function';
    };
    var $merge = function(prev, cur) {
        if(prev&&prev !== cur) {
            var ct = typeof cur;
            var pt = typeof prev;
            if(ct === pt) {
                if(ct === 'function') {
                    var m = function() {
                        this.$base = prev;
                        return cur.apply(this, arguments);
                    };
                    return m;
                }
            }
        }
        return cur;
    };

    var $proto = function(f) {
        f.$proto = true;
        var proto = new f();
        delete f.$proto;
        return proto;
    };
    var Class = function() {
        if(Class.$proto) {
            return;
        }
        if(this instanceof Class) {
            throw Error('Incorect using component Class');
        }
        var _p = $proto(Class);
        var _a = {};

        var _ext = function(obj) {
            if(isFn(obj)) {
                obj = $proto(obj);
            }
            var ii = null;
            for(var i in obj) {
                if(isFn(obj[i])) {
                    ii = i === 'init' ? '__init__' : i;
                    _p[ii] = $merge(_p[ii], obj[i]);
                } else {
                    _a[i] = obj[i];
                }
            }
        };
        //base wraper
        var klass = function() {
            for(var i in _a) {
                this[i] = sjs.clone(_a[i]);
            }
            if(!klass.$proto) {
                if(isFn(this.__init__)) {
                    return this.__init__.apply(this, arguments);
                }
            }
            return this;
        };
        var args = Array.prototype.slice.call(arguments);
        var pos = 0, next, i, l;
        while(args.length) {
            next = args.shift();
            if(!next) {
                continue;
            }
            var type = typeof next;
            if(type === 'function'&&pos === 0) {
                //extend
                _p = $proto(next);

                //find variable
                for(i in _p) {
                    if(!isFn(_p[i])) {
                        _a[i] = _p[i];
                        delete _p[i];
                    }
                }
                continue;
            } else if(type === 'object') {
                if(isArray(next)) {
                    //implement
                    for( i = 0, l = next.length;i < l;i++) {
                        _ext(next[i]);
                    }
                } else {
                    //instance
                    _ext(next);

                    //static
                    next = args.shift()|| {};
                    for(i in next) {
                        klass[i] = next[i];
                    }
                    break;
                }
            }
        }
        klass.prototype = _p;
        klass.constructor = Class;
        return klass;
    };
    __.Class = Class;

})(scode);
<|endoftext|>


<commit_before>/**
 * User roles table^ set/unset roles and groups for user
 */
qx.Class.define("ncms.usr.UserRolesTable", {
    extend : sm.table.Table,

    construct : function() {
        var tm = this._createTableModel();
        this.base(arguments, tm, tm.getCustom());
        this.set({statusBarVisible : false});

        this.addListener("dataEdited", function(ev) {
            if (this.__user == null) {
                return;
            }
            var data = ev.getData();
            var role = tm.getRowAssociatedData(data.row);
            if (role["activeInGroup"]) {
                var tmdata = tm.getData();
                tmdata[data.row][data.col] = data.oldValue;
                tm.setData(tmdata);
                return;
            }
            this.__updateUserRole(this.__user, role, data.value);
        }, this);

        var rr = new sm.table.renderer.CustomRowRenderer();
        rr.setBgColorInterceptor(qx.lang.Function.bind(function(rowInfo) {
            return rowInfo.rowData.rowData["activeInGroup"] ? "#FFFF99" : "white";
        }, this));
        this.setDataRowRenderer(rr);

        this._reload(null, null);
    },

    members : {

        __user : null,

        setUser : function(login) {
            if (login == null) {
                this._reload();
                this.__user = null;
                return;
            }

            var req = new sm.io.Request(ncms.Application.ACT.getRestUrl("security.user", {name : login}), "GET", "application/json");
            req.send(function(resp) {
                var data = resp.getContent();
                this._reload(data["roles"], data["groups"]);
                this.__user = login;
            }, this);
        },

        _createTableModel : function() {
            var tm = new sm.model.JsonTableModel();
            this._setJsonTableData(tm, null);
            return tm;
        },

        _reload : function(uroles, ugroups) {
            uroles = uroles || [];
            ugroups = ugroups || [];

            var rolesComparator = function(a, b) {
                return a["name"] < b["name"] ? -1 : (a["name"] > b["name"] ? 1 : 0)
            };

            var roles = [];
            var groups = [];

            var req = new sm.io.Request(ncms.Application.ACT.getUrl("security.roles"), "GET", "application/json");
            req.setAsynchronous(false);
            req.send(function(resp) {
                var data = resp.getContent() || [];
                for (var i = 0; i < data.length; ++i) {
                    roles.push(qx.lang.Object.mergeWith(data[i], {type: "role"}));
                }
            }, this);
            roles.sort(rolesComparator);

            req = new sm.io.Request(ncms.Application.ACT.getUrl("security.groups"), "GET", "application/json");
            req.setAsynchronous(false);
            req.send(function(resp) {
                var data = resp.getContent() || [];
                for (var i = 0; i < data.length; ++i) {
                    groups.push(qx.lang.Object.mergeWith(data[i], {type: "group"}));
                }
            }, this);
            groups.sort(rolesComparator);

            var items = [].concat(groups).concat(roles);
            var data = [];
            var dataMap = {};
            var i, dataItem;
            for (i = 0; i < items.length; ++i) {
                var item = items[i];
                dataItem = [[item["name"], item["type"], item["description"], false], item];
                data[i] = dataItem;
                dataMap[item["name"]] = dataItem; // cache
            }

            // update user roles and groups
            for (i = 0; i < uroles.length; ++i) {
                dataItem = dataMap[uroles[i]];
                if (dataItem) {
                    dataItem[0][3] = dataItem[1]["active"] = true;
                }
            }
            for (i = 0; i < ugroups.length; ++i) {
                var groupItem = dataMap[ugroups[i]];
                if (groupItem) {
                    groupItem[0][3] = groupItem[1]["active"] = true;
                    var groles = groupItem[1]["roles"] || [];
                    for(var j = 0; j < groles.length; ++j) {
                        dataItem = dataMap[groles[j]];
                        if (dataItem) {
                            dataItem[1]["activeInGroup"] = true;
                        }
                    }
                }
            }

            if (this.isEditing()) {
                this.stopEditing();
            }
            this.getSelectionModel().resetSelection();
            this._setJsonTableData(this.getTableModel(), data);

        },

        //overriden
        _setJsonTableData : function(tm, items) {
            var data = {
                "title" : "",
                "columns" : [
                    {
                        "title" : this.tr("Role/Group").toString(),
                        "id" : "id",
                        "width" : "1*"
                    },
                    {
                        "title" : this.tr("Type").toString(),
                        "id" : "type",
                        "width" : 150
                    },
                    {
                        "title" : this.tr("Description").toString(),
                        "id" : "description",
                        "width" : "2*"
                    },
                    {
                        "title" : this.tr("Assign").toString(),
                        "id" : "active",
                        "type" : "boolean",
                        "editable" : true,
                        "width" : "1*"
                    }

                ],
                "items" : items ? items : []
            };
            tm.setJsonData(data);
        },

        __updateUserRole : function(user, data, value) {
            if (user == null) {
                return;
            }

            var params = {name : user};
            params[data["type"]] = data["name"];
            var req = new sm.io.Request(ncms.Application.ACT.getRestUrl("security.user." + data["type"], params), value ? "PUT" : "DELETE", "application/json");
            req.send(function(resp){
                this.setUser(user);
            }, this);
        }
    },

    destruct : function() {
        this.__user = null;
    }
});
<commit_msg>user roles table improvements<commit_after>/**
 * User roles table^ set/unset roles and groups for user
 */
qx.Class.define("ncms.usr.UserRolesTable", {
    extend : sm.table.Table,

    construct : function() {
        var tm = this._createTableModel();
        this.base(arguments, tm, tm.getCustom());
        this.set({statusBarVisible : false});

        this.addListener("dataEdited", function(ev) {
            if (this.__user == null) {
                return;
            }
            var data = ev.getData();
            var role = tm.getRowAssociatedData(data.row);
            if (role["activeInGroup"]) {
                var tmdata = tm.getData();
                tmdata[data.row][data.col] = data.oldValue;
                tm.setData(tmdata);
                return;
            }
            this.__updateUserRole(this.__user, role, data.value);
        }, this);

        var rr = new sm.table.renderer.CustomRowRenderer();
        rr.setBgColorInterceptor(qx.lang.Function.bind(function(rowInfo) {
            return rowInfo.rowData.rowData["activeInGroup"] ? "#FFFF99" : "white";
        }, this));
        this.setDataRowRenderer(rr);

        this._reload(null, null);
    },

    members : {

        __user : null,

        setUser : function(login) {
            if (login == null) {
                this._reload();
                this.__user = null;
                return;
            }

            var req = new sm.io.Request(ncms.Application.ACT.getRestUrl("security.user", {name : login}), "GET", "application/json");
            req.send(function(resp) {
                var data = resp.getContent();
                this._reload(data["roles"], data["groups"]);
                this.__user = login;
            }, this);
        },

        _createTableModel : function() {
            var tm = new sm.model.JsonTableModel();
            this._setJsonTableData(tm, null);
            return tm;
        },

        _reload : function(uroles, ugroups) {
            uroles = uroles || [];
            ugroups = ugroups || [];

            var rolesComparator = function(a, b) {
                return a["name"] < b["name"] ? -1 : (a["name"] > b["name"] ? 1 : 0)
            };

            var roles = [];
            var groups = [];

            var req = new sm.io.Request(ncms.Application.ACT.getUrl("security.roles"), "GET", "application/json");
            req.setAsynchronous(false);
            req.send(function(resp) {
                var data = resp.getContent() || [];
                for (var i = 0; i < data.length; ++i) {
                    roles.push(qx.lang.Object.mergeWith(data[i], {type: "role"}));
                }
            }, this);
            roles.sort(rolesComparator);

            req = new sm.io.Request(ncms.Application.ACT.getUrl("security.groups"), "GET", "application/json");
            req.setAsynchronous(false);
            req.send(function(resp) {
                var data = resp.getContent() || [];
                for (var i = 0; i < data.length; ++i) {
                    groups.push(qx.lang.Object.mergeWith(data[i], {type: "group"}));
                }
            }, this);
            groups.sort(rolesComparator);

            var items = [].concat(groups).concat(roles);
            var data = [];
            var dataMap = {};
            var i, dataItem;
            for (i = 0; i < items.length; ++i) {
                var item = items[i];
                dataItem = [[false, item["name"], item["type"], item["description"]], item];
                data[i] = dataItem;
                dataMap[item["name"]] = dataItem; // cache
            }

            // update user roles and groups
            for (i = 0; i < uroles.length; ++i) {
                dataItem = dataMap[uroles[i]];
                if (dataItem) {
                    dataItem[0][0] = dataItem[1]["active"] = true;
                }
            }
            for (i = 0; i < ugroups.length; ++i) {
                var groupItem = dataMap[ugroups[i]];
                if (groupItem) {
                    groupItem[0][0] = groupItem[1]["active"] = true;
                    var groles = groupItem[1]["roles"] || [];
                    for(var j = 0; j < groles.length; ++j) {
                        dataItem = dataMap[groles[j]];
                        if (dataItem) {
                            dataItem[1]["activeInGroup"] = true;
                        }
                    }
                }
            }

            if (this.isEditing()) {
                this.stopEditing();
            }
            this.getSelectionModel().resetSelection();
            this._setJsonTableData(this.getTableModel(), data);

        },

        //overriden
        _setJsonTableData : function(tm, items) {
            var data = {
                "title" : "",
                "columns" : [
                    {
                        "title" : this.tr("Assign").toString(),
                        "id" : "active",
                        "type" : "boolean",
                        "editable" : true,
                        "width" : 75
                    },
                    {
                        "title" : this.tr("Role/Group").toString(),
                        "id" : "id",
                        "width" : "1*"
                    },
                    {
                        "title" : this.tr("Type").toString(),
                        "id" : "type",
                        "width" : 150
                    },
                    {
                        "title" : this.tr("Description").toString(),
                        "id" : "description",
                        "width" : "2*"
                    }
                ],
                "items" : items ? items : []
            };
            tm.setJsonData(data);
        },

        __updateUserRole : function(user, data, value) {
            if (user == null) {
                return;
            }

            var params = {name : user};
            params[data["type"]] = data["name"];
            var req = new sm.io.Request(ncms.Application.ACT.getRestUrl("security.user." + data["type"], params), value ? "PUT" : "DELETE", "application/json");
            req.send(function(resp){
                this.setUser(user);
            }, this);
        }
    },

    destruct : function() {
        this.__user = null;
    }
});
<|endoftext|>


<commit_before>//
// Vlad Seryakov 2014
//

// Status of the current account
bkjs.koAuth = ko.observable(0);
bkjs.koAdmin = ko.observable(0);
bkjs.koName = ko.observable("");
bkjs.koState = {};
bkjs.koTemplates = {};
bkjs.koAliases = {};
bkjs.koModels = {};
bkjs.koViewModels = [];

bkjs.koInit = function()
{
    ko.applyBindings(bkjs);
    bkjs.login(function(err) {
        bkjs.koCheckLogin(err);
    });
}

bkjs.koCheckLogin = function(err, path)
{
    bkjs.koName(bkjs.account.name || "");
    bkjs.koAuth(bkjs.loggedIn);
    bkjs.koAdmin(bkjs.loggedIn && bkjs.checkAccountType(bkjs.account, bkjs.adminType || "admin"));
    $(bkjs).trigger(bkjs.loggedIn ? "bkjs.login" : "bkjs.nologin", [err, path]);
    if (!err && typeof bkjs.koShow == "function") bkjs.koShow();
}

bkjs.koLogin = function(data, event)
{
    bkjs.showLogin(bkjs.koLoginOptions, function(err) {
        bkjs.koCheckLogin(err);
        if (!err) bkjs.hideLogin();
    });
}

bkjs.koLogout = function(data, event)
{
    bkjs.logout(function() {
        bkjs.koAuth(0);
        bkjs.koAdmin(0);
        bkjs.koName("");
        $(bkjs).trigger('bkjs.logout');
        if (bkjs.koLogoutUrl) window.location.href = bkjs.koLogoutUrl;
    });
}

bkjs.koVal = ko.utils.unwrapObservable;

bkjs.koGet = function(name, dflt)
{
    if (typeof name != "string") name = String(name);
    name = name.replace(/[^a-zA-z0-9_]/g, "_");
    var val = bkjs.koState[name];
    if (typeof val == "undefined") {
        bkjs.koState[name] = val = Array.isArray(dflt) ? ko.observableArray(dflt) : ko.observable(dflt);
    }
    return val;
}

bkjs.koSet = function(name, val, quiet)
{
    if (!name) return;
    if (!Array.isArray(name)) name = [ name ];
    for (var i in name) {
        var key = name[i], old;
        if (typeof key != "string") continue;
        key = key.replace(/[^a-zA-z0-9_]/g, "_");
        if (ko.isObservable(bkjs.koState[key])) {
            old = bkjs.koState[key]();
            bkjs.koState[key](val);
        } else {
            old = bkjs.koState[key];
            bkjs.koState[key] = Array.isArray(val) ? ko.observableArray(val) : ko.observable(val);
        }
        if (!quiet) bkjs.koEvent(key, { name: name, key: key, value: val, old: old });
    }
}

bkjs.koSetObject = function(obj, options)
{
    if (!obj || typeof obj != "object") obj = {};
    for (var p in obj) {
        if (typeof options[p] != "undefined") continue;
        if (ko.isObservable(obj[p])) obj[p](undefined); else obj[p] = undefined;
    }
    for (var n in options) {
        if (ko.isObservable(obj[n])) obj[n](bkjs.koVal(options[n])); else obj[n] = bkjs.koVal(options[n]);
    }
    return obj;
}

bkjs.koUpdateObject = function(obj, options)
{
    if (!obj || typeof obj != "object") obj = {};
    for (var p in options) {
        if (ko.isObservable(obj[p])) obj[p](bkjs.koVal(options[p])); else obj[p] = bkjs.koVal(options[p]);
    }
    return obj;
}

bkjs.koEvent = function(name, data)
{
    var event = bkjs.toCamel(name);
    event = "on" + event.substr(0, 1).toUpperCase() + event.substr(1);
    $(bkjs).trigger("bkjs.event", [name, event, data]);
}

bkjs.koViewModel = function(params, componentInfo)
{
    this.params = {};
    for (var p in params) this.params[p] = params[p];
    $(bkjs).on("bkjs.event." + this.koName, $.proxy(this._handleEvent, this));
}

bkjs.koViewModel.prototype._handleEvent = function(ev, name, event, data)
{
    if (bkjs.debug) console.log("handleEvent:", this.koName, name, event, data)
    if (typeof this[event] == "function") this[event](data);
    if (typeof this.handleEvent == "function") this.handleEvent(name, data);
}

bkjs.koViewModel.prototype.dispose = function()
{
    if (bkjs.debug) console.log("dispose:", this.koName);
    bkjs.koEvent("model.disposed", { name: this.koName, params: this.params, vm: this });
    $(bkjs).off("bkjs.event." + this.koName, $.proxy(this._handleEvent, this));
    if (typeof this.onDispose == "function") this.onDispose();
    delete this.params;
    bkjs.koViewModels.splice(bkjs.koViewModels.indexOf(this));
}

bkjs.koCreateModel = function(name)
{
    if (!name) throw new Error("model name is required");
    bkjs.koModels[name] = function(params, componentInfo) {
        this.koName = name;
        bkjs.koViewModel.call(this, params, componentInfo);
    };
    bkjs.inherits(bkjs.koModels[name], bkjs.koViewModel);
    bkjs.koEvent("model.created", { name: name, vm: bkjs.koModels[name] });
    return bkjs.koModels[name];
}

bkjs.koCreateModelAlias = function(name, alias)
{
    bkjs.koAliases[bkjs.toCamel(alias)] = bkjs.toCamel(name);
}

bkjs.koFindModel = function(name)
{
    name = bkjs.toCamel(name);
    var tmpl = bkjs.koTemplates[name];
    if (!tmpl) {
        name = bkjs.koAliases[name];
        if (name) tmpl = bkjs.koTemplates[name];
    }
    if (!tmpl) return null;
    return {
        template: tmpl[0] !== "<" ? bkjs.strDecompress(tmpl, "base64") : tmpl,
        viewModel: {
            createViewModel: function(params, componentInfo) {
                var vm;
                if (typeof bkjs.koModels[name] == "function") {
                    vm = new bkjs.koModels[name](params, componentInfo);
                    if (typeof vm.onCreate == "function") vm.onCreate(params, componentInfo);
                    bkjs.koViewModels.push(vm);
                }
                if (bkjs.debug) console.log("createViewModel:", name);
                bkjs.koEvent("component.created", { name: name, params: params, model: vm, info: componentInfo });
                return vm;
            }
        },
    };
}

// Run a method in all active view models, returns result from each in an object
bkjs.koRunMethod = function(method)
{
    var rc = {};
    var args = Array.prototype.slice.apply(arguments).slice(1);
    for (var i in bkjs.koViewModels) {
        var m = bkjs.koViewModels[i];
        if (typeof m[method] == "function") rc[m.name] = m[method].apply(m, args);
    }
    return rc;
}

ko.bindingHandlers.hidden = {
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        var isCurrentlyHidden = !(element.style.display == "");
        if (value && !isCurrentlyHidden) element.style.display = "none"; else
        if ((!value) && isCurrentlyHidden) element.style.display = "";
    }
};

// Web bundle naming convention
ko.components.loaders.unshift({
    getConfig: function(name, callback) {
        callback(bkjs.koFindModel(name));
    }
});

<commit_msg>decompress only once<commit_after>//
// Vlad Seryakov 2014
//

// Status of the current account
bkjs.koAuth = ko.observable(0);
bkjs.koAdmin = ko.observable(0);
bkjs.koName = ko.observable("");
bkjs.koState = {};
bkjs.koTemplates = {};
bkjs.koAliases = {};
bkjs.koModels = {};
bkjs.koViewModels = [];

bkjs.koInit = function()
{
    ko.applyBindings(bkjs);
    bkjs.login(function(err) {
        bkjs.koCheckLogin(err);
    });
}

bkjs.koCheckLogin = function(err, path)
{
    bkjs.koName(bkjs.account.name || "");
    bkjs.koAuth(bkjs.loggedIn);
    bkjs.koAdmin(bkjs.loggedIn && bkjs.checkAccountType(bkjs.account, bkjs.adminType || "admin"));
    $(bkjs).trigger(bkjs.loggedIn ? "bkjs.login" : "bkjs.nologin", [err, path]);
    if (!err && typeof bkjs.koShow == "function") bkjs.koShow();
}

bkjs.koLogin = function(data, event)
{
    bkjs.showLogin(bkjs.koLoginOptions, function(err) {
        bkjs.koCheckLogin(err);
        if (!err) bkjs.hideLogin();
    });
}

bkjs.koLogout = function(data, event)
{
    bkjs.logout(function() {
        bkjs.koAuth(0);
        bkjs.koAdmin(0);
        bkjs.koName("");
        $(bkjs).trigger('bkjs.logout');
        if (bkjs.koLogoutUrl) window.location.href = bkjs.koLogoutUrl;
    });
}

bkjs.koVal = ko.utils.unwrapObservable;

bkjs.koGet = function(name, dflt)
{
    if (typeof name != "string") name = String(name);
    name = name.replace(/[^a-zA-z0-9_]/g, "_");
    var val = bkjs.koState[name];
    if (typeof val == "undefined") {
        bkjs.koState[name] = val = Array.isArray(dflt) ? ko.observableArray(dflt) : ko.observable(dflt);
    }
    return val;
}

bkjs.koSet = function(name, val, quiet)
{
    if (!name) return;
    if (!Array.isArray(name)) name = [ name ];
    for (var i in name) {
        var key = name[i], old;
        if (typeof key != "string") continue;
        key = key.replace(/[^a-zA-z0-9_]/g, "_");
        if (ko.isObservable(bkjs.koState[key])) {
            old = bkjs.koState[key]();
            bkjs.koState[key](val);
        } else {
            old = bkjs.koState[key];
            bkjs.koState[key] = Array.isArray(val) ? ko.observableArray(val) : ko.observable(val);
        }
        if (!quiet) bkjs.koEvent(key, { name: name, key: key, value: val, old: old });
    }
}

bkjs.koSetObject = function(obj, options)
{
    if (!obj || typeof obj != "object") obj = {};
    for (var p in obj) {
        if (typeof options[p] != "undefined") continue;
        if (ko.isObservable(obj[p])) obj[p](undefined); else obj[p] = undefined;
    }
    for (var n in options) {
        if (ko.isObservable(obj[n])) obj[n](bkjs.koVal(options[n])); else obj[n] = bkjs.koVal(options[n]);
    }
    return obj;
}

bkjs.koUpdateObject = function(obj, options)
{
    if (!obj || typeof obj != "object") obj = {};
    for (var p in options) {
        if (ko.isObservable(obj[p])) obj[p](bkjs.koVal(options[p])); else obj[p] = bkjs.koVal(options[p]);
    }
    return obj;
}

bkjs.koEvent = function(name, data)
{
    var event = bkjs.toCamel(name);
    event = "on" + event.substr(0, 1).toUpperCase() + event.substr(1);
    $(bkjs).trigger("bkjs.event", [name, event, data]);
}

bkjs.koViewModel = function(params, componentInfo)
{
    this.params = {};
    for (var p in params) this.params[p] = params[p];
    $(bkjs).on("bkjs.event." + this.koName, $.proxy(this._handleEvent, this));
}

bkjs.koViewModel.prototype._handleEvent = function(ev, name, event, data)
{
    if (bkjs.debug) console.log("handleEvent:", this.koName, name, event, data)
    if (typeof this[event] == "function") this[event](data);
    if (typeof this.handleEvent == "function") this.handleEvent(name, data);
}

bkjs.koViewModel.prototype.dispose = function()
{
    if (bkjs.debug) console.log("dispose:", this.koName);
    bkjs.koEvent("model.disposed", { name: this.koName, params: this.params, vm: this });
    $(bkjs).off("bkjs.event." + this.koName, $.proxy(this._handleEvent, this));
    if (typeof this.onDispose == "function") this.onDispose();
    delete this.params;
    bkjs.koViewModels.splice(bkjs.koViewModels.indexOf(this));
}

bkjs.koCreateModel = function(name)
{
    if (!name) throw new Error("model name is required");
    bkjs.koModels[name] = function(params, componentInfo) {
        this.koName = name;
        bkjs.koViewModel.call(this, params, componentInfo);
    };
    bkjs.inherits(bkjs.koModels[name], bkjs.koViewModel);
    bkjs.koEvent("model.created", { name: name, vm: bkjs.koModels[name] });
    return bkjs.koModels[name];
}

bkjs.koCreateModelAlias = function(name, alias)
{
    bkjs.koAliases[bkjs.toCamel(alias)] = bkjs.toCamel(name);
}

bkjs.koFindModel = function(name)
{
    name = bkjs.toCamel(name);
    var tmpl = bkjs.koTemplates[name];
    if (!tmpl) {
        name = bkjs.koAliases[name];
        if (name) tmpl = bkjs.koTemplates[name];
    }
    if (!tmpl) return null;
    if (tmpl[0] !== "<") {
        bkjs.koTemplates[name] = tmpl = bkjs.strDecompress(tmpl, "base64");
    }
    return {
        template: tmpl,
        viewModel: {
            createViewModel: function(params, componentInfo) {
                var vm;
                if (typeof bkjs.koModels[name] == "function") {
                    vm = new bkjs.koModels[name](params, componentInfo);
                    if (typeof vm.onCreate == "function") vm.onCreate(params, componentInfo);
                    bkjs.koViewModels.push(vm);
                }
                if (bkjs.debug) console.log("createViewModel:", name);
                bkjs.koEvent("component.created", { name: name, params: params, model: vm, info: componentInfo });
                return vm;
            }
        },
    };
}

// Run a method in all active view models, returns result from each in an object
bkjs.koRunMethod = function(method)
{
    var rc = {};
    var args = Array.prototype.slice.apply(arguments).slice(1);
    for (var i in bkjs.koViewModels) {
        var m = bkjs.koViewModels[i];
        if (typeof m[method] == "function") rc[m.name] = m[method].apply(m, args);
    }
    return rc;
}

ko.bindingHandlers.hidden = {
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        var isCurrentlyHidden = !(element.style.display == "");
        if (value && !isCurrentlyHidden) element.style.display = "none"; else
        if ((!value) && isCurrentlyHidden) element.style.display = "";
    }
};

// Web bundle naming convention
ko.components.loaders.unshift({
    getConfig: function(name, callback) {
        callback(bkjs.koFindModel(name));
    }
});

<|endoftext|>


<commit_before>/*
 *
 * XSLT STUFF
 *
 */
function createXSLT(xsl) {
    var p = new XSLTProcessor();
    p.importStylesheet(xsl);
    return p;
}

var xml2htmlStylesheet = null;

// Wykonuje block z załadowanymi arkuszami stylów
function withStylesheets(code_block, onError)
{
    if (!xml2htmlStylesheet) {
    	$.blockUI({message: 'Ładowanie arkuszy stylów...'});
    	$.ajax({
        	url: STATIC_URL + 'xsl/wl2html_client.xsl?20110112',
        	dataType: 'xml',
        	timeout: 10000,
        	success: function(data) {
            	xml2htmlStylesheet = createXSLT(data);
                $.unblockUI();
				code_block();

            },
			error: onError
        })
    }
	else {
		code_block();
	}
}

// Wykonuje block z załadowanymi kanonicznymi motywami
function withThemes(code_block, onError)
{
    if (typeof withThemes.canon == 'undefined') {
        $.ajax({
            url: '/themes',
            dataType: 'text',
            success: function(data) {
                withThemes.canon = data.split('\n');
                code_block(withThemes.canon);
            },
            error: function() {
                withThemes.canon = null;
                code_block(withThemes.canon);
            }
        })
    }
    else {
        code_block(withThemes.canon);
    }
}

function xml2html(options) {
    withStylesheets(function() {
        var xml = options.xml.replace(/\/(\s+)/g, '
$1');
        xml = xml.replace(/([^a-zA-Z0-9ąćęłńóśźżĄĆĘŁŃÓŚŹŻ\s<>«»\\*_!,:;?%."'=#()\/-]+)/g, '<alien>$1</alien>');
        var parser = new DOMParser();
        var serializer = new XMLSerializer();
        var doc = parser.parseFromString(xml, 'text/xml');
        var error = $('parsererror', doc);

        if (error.length == 0) {
            doc = xml2htmlStylesheet.transformToFragment(doc, document);
            console.log(doc.firstChild);

        if(doc.firstChild === null) {
            options.error("Błąd w przetwarzaniu XML.");
                return;
            }

            error = $('parsererror', doc);
        }

        if (error.length > 0 && options.error) {
            source = $('sourcetext', doc);
            source_text = source.text();
            source.text('');
            options.error(error.text(), source_text);
        } else {
            options.success(doc.firstChild);

            withThemes(function(canonThemes) {
                if (canonThemes != null) {
                    $('.theme-text-list').addClass('canon').each(function(){
                        var themes = $(this).html().split(',');
                        for (i in themes) {
                            themes[i] = $.trim(themes[i]);
                            if (canonThemes.indexOf(themes[i]) == -1)
                                themes[i] = '' + themes[i] + ""
                        }
                        $(this).html(themes.join(', '));
                    });
                }
            });
        }
    }, function() { options.error && options.error('Nie udało się załadować XSLT'); });
}

/* USEFULL CONSTANTS */
const ELEMENT_NODE 					 = 1;
const ATTRIBUTE_NODE                 = 2;
const TEXT_NODE                      = 3;
const CDATA_SECTION_NODE             = 4;
const ENTITY_REFERENCE_NODE          = 5;
const ENTITY_NODE                    = 6;
const PROCESSING_INSTRUCTION_NODE    = 7;
const COMMENT_NODE                   = 8;
const DOCUMENT_NODE                  = 9;
const DOCUMENT_TYPE_NODE             = 10;
const DOCUMENT_FRAGMENT_NODE         = 11;
const NOTATION_NODE                  = 12;
const XATTR_RE = /^x-attr-name-(.*)$/;

const ELEM_START = 1;
const ELEM_END = 2;
const NS_END = 3;

const NAMESPACES = {
	// namespaces not listed here will be assigned random names
	"http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
	"http://purl.org/dc/elements/1.1/": "dc",
	"http://www.w3.org/XML/1998/namespace": "xml"
};

function HTMLSerializer() {
	// empty constructor
}

HTMLSerializer.prototype._prepare = function() {
	this.stack = [];

	// XML namespace is implicit
	this.nsMap = {"http://www.w3.org/XML/1998/namespace": "xml"};

	this.result = "";
	this.nsCounter = 1;
}

HTMLSerializer.prototype._pushElement = function(element) {
	this.stack.push({
		"type": ELEM_START,
		"node": element
	});
}

HTMLSerializer.prototype._pushChildren = function(element) {
	for(var i = element.childNodes.length-1; i >= 0; i--)
		this._pushElement(element.childNodes.item(i));
}

HTMLSerializer.prototype._pushTagEnd = function(tagName) {
	this.stack.push({
		"type": ELEM_END,
		"tagName": tagName
	});
}

HTMLSerializer.prototype._verseBefore = function(node) {
    /* true if previous element is a previous verse of a stanza */
    var parent = node.parentNode;
    if (!parent || !parent.hasAttribute('x-node') || parent.getAttribute('x-node') != 'strofa')
        return false;

	var prev = node.previousSibling;

	while((prev !== null) && (prev.nodeType != ELEMENT_NODE)) {
		prev = prev.previousSibling;
	}

	return (prev !== null) && prev.hasAttribute('x-verse');
}

HTMLSerializer.prototype._nodeIgnored = function(node) {
    return node.getAttribute('x-node') == 'wers';
}

HTMLSerializer.prototype._ignoredWithWhitespace = function(node) {
    while (node.nodeType == ELEMENT_NODE && this._nodeIgnored(node) && node.childNodes.length > 0)
        node = node.childNodes[0];
    if (node.nodeType == TEXT_NODE)
        return node.nodeValue.match(/^\s/)
    else return false;
}

HTMLSerializer.prototype.serialize = function(rootElement, stripOuter)
{
	var self = this;
	self._prepare();

	if(!stripOuter)
		self._pushElement(rootElement);
	else
		self._pushChildren(rootElement);

    var text_buffer = '';

	while(self.stack.length > 0) {
		var token = self.stack.pop();

        if(token.type === ELEM_END) {
            self.result += text_buffer;
            text_buffer = '';
            if (token.tagName != '')
                self.result += "</" + token.tagName + ">";
            continue;
        };

		if(token.type === NS_END) {
			self._unassignNamespace(token.namespace);
			continue;
		}

		switch(token.node.nodeType) {
			case ELEMENT_NODE:
				if(token.node.hasAttribute('x-pass-thru')
				 || token.node.hasAttribute('data-pass-thru')) {
					self._pushChildren(token.node);
					break;
				}

				if(!token.node.hasAttribute('x-node'))
					break;

				var xnode = token.node.getAttribute('x-node');

				if(xnode === 'out-of-flow-text') {
					self._pushChildren(token.node);
					break;
				}

                if(token.node.hasAttribute('x-verse') && self._verseBefore(token.node)) {
                    self.result += '/';
                    // add whitespace if there's none
                    if (!(text_buffer.match(/^\s/) || self._ignoredWithWhitespace(token.node)))
                        self.result += ' ';
                }

                self.result += text_buffer;
                text_buffer = '';
				self._serializeElement(token.node);
				break;
			case TEXT_NODE:
				self.result += text_buffer;
				text_buffer = token.node.nodeValue;
				break;
		};
	};
    self.result += text_buffer;

	return this.result;
}

/*
 * TODO: this doesn't support prefix redefinitions
 */
HTMLSerializer.prototype._unassignNamespace = function(nsData) {
	this.nsMap[nsData.uri] = undefined;
};

HTMLSerializer.prototype._assignNamespace = function(uri) {
	if(uri === null) {
		// default namespace
		return ({"prefix": "", "uri": "", "fresh": false});
	}

	if(this.nsMap[uri] === undefined) {
		// this prefix hasn't been defined yet in current context
		var prefix = NAMESPACES[uri];

		if (prefix === undefined) { // not predefined
			prefix = "ns" + this.nsCounter;
			this.nsCounter += 1;
		}

		this.nsMap[uri] = prefix;
		return ({
			"prefix": prefix,
			"uri": uri,
			"fresh": true
		});
	}

	return ({"prefix": this.nsMap[uri], "uri": uri, "fresh": false});
};

HTMLSerializer.prototype._join = function(prefix, name) {
	if(!!prefix)
		return prefix + ":" + name;
	return name;
};

HTMLSerializer.prototype._rjoin = function(prefix, name) {
	if(!!name)
		return prefix + ":" + name;
	return prefix;
};

HTMLSerializer.prototype._serializeElement = function(node) {
    var self = this;

    if (self._nodeIgnored(node)) {
        self._pushTagEnd('');
        self._pushChildren(node);
    }
    else {
    	var ns = node.getAttribute('x-ns');
    	var nsPrefix = null;
    	var newNamespaces = [];

    	var nsData = self._assignNamespace(node.getAttribute('x-ns'));

    	if(nsData.fresh) {
    		newNamespaces.push(nsData);
    		self.stack.push({
    			"type": NS_END,
    			"namespace": nsData
    		});
    	}

    	var tagName = self._join(nsData.prefix, node.getAttribute('x-node'));

    	/* retrieve attributes */
    	var attributeIDs = [];
    	for (var i = 0; i < node.attributes.length; i++) {
    		var attr = node.attributes.item(i);

    		// check if name starts with "x-attr-name"
    		var m = attr.name.match(XATTR_RE);
    		if (m !== null)
    			attributeIDs.push(m[1]);
    	};

    	/* print out */

    	self.result += '<' + tagName;

    	$.each(attributeIDs, function() {
    		var nsData = self._assignNamespace(node.getAttribute('x-attr-ns-'+this));

    		if(nsData.fresh) {
    			newNamespaces.push(nsData);
    			self.stack.push({
    				"type": NS_END,
    				"namespace": nsData
    			});
    		};

    		self.result += ' ' + self._join(nsData.prefix, node.getAttribute('x-attr-name-'+this));
    		self.result += '="'+node.getAttribute('x-attr-value-'+this) +'"';
    	});

    	/* print new namespace declarations */
    	$.each(newNamespaces, function() {
    		self.result += " " + self._rjoin("xmlns", this.prefix);
    		self.result += '="' + this.uri + '"';
    	});

    	if (node.childNodes.length > 0) {
    		self.result += ">";
    		self._pushTagEnd(tagName);
    		self._pushChildren(node);
    	}
    	else {
    		self.result += "/>";
    	};
    }
};

function html2text(params) {
	try {
		var s = new HTMLSerializer();
		params.success( s.serialize(params.element, params.stripOuter) );
	} catch(e) {
		params.error("Nie udało się zserializować tekstu:" + e)
	}
}
<commit_msg>#1557: colouring ampersand breaks XML<commit_after>/*
 *
 * XSLT STUFF
 *
 */
function createXSLT(xsl) {
    var p = new XSLTProcessor();
    p.importStylesheet(xsl);
    return p;
}

var xml2htmlStylesheet = null;

// Wykonuje block z załadowanymi arkuszami stylów
function withStylesheets(code_block, onError)
{
    if (!xml2htmlStylesheet) {
    	$.blockUI({message: 'Ładowanie arkuszy stylów...'});
    	$.ajax({
        	url: STATIC_URL + 'xsl/wl2html_client.xsl?20110112',
        	dataType: 'xml',
        	timeout: 10000,
        	success: function(data) {
            	xml2htmlStylesheet = createXSLT(data);
                $.unblockUI();
				code_block();

            },
			error: onError
        })
    }
	else {
		code_block();
	}
}

// Wykonuje block z załadowanymi kanonicznymi motywami
function withThemes(code_block, onError)
{
    if (typeof withThemes.canon == 'undefined') {
        $.ajax({
            url: '/themes',
            dataType: 'text',
            success: function(data) {
                withThemes.canon = data.split('\n');
                code_block(withThemes.canon);
            },
            error: function() {
                withThemes.canon = null;
                code_block(withThemes.canon);
            }
        })
    }
    else {
        code_block(withThemes.canon);
    }
}

function xml2html(options) {
    withStylesheets(function() {
        var xml = options.xml.replace(/\/(\s+)/g, '
$1');
        xml = xml.replace(/([^a-zA-Z0-9ąćęłńóśźżĄĆĘŁŃÓŚŹŻ\s<>«»\\*_!,:;?&%."'=#()\/-]+)/g, '<alien>$1</alien>');
        var parser = new DOMParser();
        var serializer = new XMLSerializer();
        var doc = parser.parseFromString(xml, 'text/xml');
        var error = $('parsererror', doc);

        if (error.length == 0) {
            doc = xml2htmlStylesheet.transformToFragment(doc, document);
            console.log(doc.firstChild);

        if(doc.firstChild === null) {
            options.error("Błąd w przetwarzaniu XML.");
                return;
            }

            error = $('parsererror', doc);
        }

        if (error.length > 0 && options.error) {
            source = $('sourcetext', doc);
            source_text = source.text();
            source.text('');
            options.error(error.text(), source_text);
        } else {
            options.success(doc.firstChild);

            withThemes(function(canonThemes) {
                if (canonThemes != null) {
                    $('.theme-text-list').addClass('canon').each(function(){
                        var themes = $(this).html().split(',');
                        for (i in themes) {
                            themes[i] = $.trim(themes[i]);
                            if (canonThemes.indexOf(themes[i]) == -1)
                                themes[i] = '' + themes[i] + ""
                        }
                        $(this).html(themes.join(', '));
                    });
                }
            });
        }
    }, function() { options.error && options.error('Nie udało się załadować XSLT'); });
}

/* USEFULL CONSTANTS */
const ELEMENT_NODE 					 = 1;
const ATTRIBUTE_NODE                 = 2;
const TEXT_NODE                      = 3;
const CDATA_SECTION_NODE             = 4;
const ENTITY_REFERENCE_NODE          = 5;
const ENTITY_NODE                    = 6;
const PROCESSING_INSTRUCTION_NODE    = 7;
const COMMENT_NODE                   = 8;
const DOCUMENT_NODE                  = 9;
const DOCUMENT_TYPE_NODE             = 10;
const DOCUMENT_FRAGMENT_NODE         = 11;
const NOTATION_NODE                  = 12;
const XATTR_RE = /^x-attr-name-(.*)$/;

const ELEM_START = 1;
const ELEM_END = 2;
const NS_END = 3;

const NAMESPACES = {
	// namespaces not listed here will be assigned random names
	"http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
	"http://purl.org/dc/elements/1.1/": "dc",
	"http://www.w3.org/XML/1998/namespace": "xml"
};

function HTMLSerializer() {
	// empty constructor
}

HTMLSerializer.prototype._prepare = function() {
	this.stack = [];

	// XML namespace is implicit
	this.nsMap = {"http://www.w3.org/XML/1998/namespace": "xml"};

	this.result = "";
	this.nsCounter = 1;
}

HTMLSerializer.prototype._pushElement = function(element) {
	this.stack.push({
		"type": ELEM_START,
		"node": element
	});
}

HTMLSerializer.prototype._pushChildren = function(element) {
	for(var i = element.childNodes.length-1; i >= 0; i--)
		this._pushElement(element.childNodes.item(i));
}

HTMLSerializer.prototype._pushTagEnd = function(tagName) {
	this.stack.push({
		"type": ELEM_END,
		"tagName": tagName
	});
}

HTMLSerializer.prototype._verseBefore = function(node) {
    /* true if previous element is a previous verse of a stanza */
    var parent = node.parentNode;
    if (!parent || !parent.hasAttribute('x-node') || parent.getAttribute('x-node') != 'strofa')
        return false;

	var prev = node.previousSibling;

	while((prev !== null) && (prev.nodeType != ELEMENT_NODE)) {
		prev = prev.previousSibling;
	}

	return (prev !== null) && prev.hasAttribute('x-verse');
}

HTMLSerializer.prototype._nodeIgnored = function(node) {
    return node.getAttribute('x-node') == 'wers';
}

HTMLSerializer.prototype._ignoredWithWhitespace = function(node) {
    while (node.nodeType == ELEMENT_NODE && this._nodeIgnored(node) && node.childNodes.length > 0)
        node = node.childNodes[0];
    if (node.nodeType == TEXT_NODE)
        return node.nodeValue.match(/^\s/)
    else return false;
}

HTMLSerializer.prototype.serialize = function(rootElement, stripOuter)
{
	var self = this;
	self._prepare();

	if(!stripOuter)
		self._pushElement(rootElement);
	else
		self._pushChildren(rootElement);

    var text_buffer = '';

	while(self.stack.length > 0) {
		var token = self.stack.pop();

        if(token.type === ELEM_END) {
            self.result += text_buffer;
            text_buffer = '';
            if (token.tagName != '')
                self.result += "</" + token.tagName + ">";
            continue;
        };

		if(token.type === NS_END) {
			self._unassignNamespace(token.namespace);
			continue;
		}

		switch(token.node.nodeType) {
			case ELEMENT_NODE:
				if(token.node.hasAttribute('x-pass-thru')
				 || token.node.hasAttribute('data-pass-thru')) {
					self._pushChildren(token.node);
					break;
				}

				if(!token.node.hasAttribute('x-node'))
					break;

				var xnode = token.node.getAttribute('x-node');

				if(xnode === 'out-of-flow-text') {
					self._pushChildren(token.node);
					break;
				}

                if(token.node.hasAttribute('x-verse') && self._verseBefore(token.node)) {
                    self.result += '/';
                    // add whitespace if there's none
                    if (!(text_buffer.match(/^\s/) || self._ignoredWithWhitespace(token.node)))
                        self.result += ' ';
                }

                self.result += text_buffer;
                text_buffer = '';
				self._serializeElement(token.node);
				break;
			case TEXT_NODE:
				self.result += text_buffer;
				text_buffer = token.node.nodeValue;
				break;
		};
	};
    self.result += text_buffer;

	return this.result;
}

/*
 * TODO: this doesn't support prefix redefinitions
 */
HTMLSerializer.prototype._unassignNamespace = function(nsData) {
	this.nsMap[nsData.uri] = undefined;
};

HTMLSerializer.prototype._assignNamespace = function(uri) {
	if(uri === null) {
		// default namespace
		return ({"prefix": "", "uri": "", "fresh": false});
	}

	if(this.nsMap[uri] === undefined) {
		// this prefix hasn't been defined yet in current context
		var prefix = NAMESPACES[uri];

		if (prefix === undefined) { // not predefined
			prefix = "ns" + this.nsCounter;
			this.nsCounter += 1;
		}

		this.nsMap[uri] = prefix;
		return ({
			"prefix": prefix,
			"uri": uri,
			"fresh": true
		});
	}

	return ({"prefix": this.nsMap[uri], "uri": uri, "fresh": false});
};

HTMLSerializer.prototype._join = function(prefix, name) {
	if(!!prefix)
		return prefix + ":" + name;
	return name;
};

HTMLSerializer.prototype._rjoin = function(prefix, name) {
	if(!!name)
		return prefix + ":" + name;
	return prefix;
};

HTMLSerializer.prototype._serializeElement = function(node) {
    var self = this;

    if (self._nodeIgnored(node)) {
        self._pushTagEnd('');
        self._pushChildren(node);
    }
    else {
    	var ns = node.getAttribute('x-ns');
    	var nsPrefix = null;
    	var newNamespaces = [];

    	var nsData = self._assignNamespace(node.getAttribute('x-ns'));

    	if(nsData.fresh) {
    		newNamespaces.push(nsData);
    		self.stack.push({
    			"type": NS_END,
    			"namespace": nsData
    		});
    	}

    	var tagName = self._join(nsData.prefix, node.getAttribute('x-node'));

    	/* retrieve attributes */
    	var attributeIDs = [];
    	for (var i = 0; i < node.attributes.length; i++) {
    		var attr = node.attributes.item(i);

    		// check if name starts with "x-attr-name"
    		var m = attr.name.match(XATTR_RE);
    		if (m !== null)
    			attributeIDs.push(m[1]);
    	};

    	/* print out */

    	self.result += '<' + tagName;

    	$.each(attributeIDs, function() {
    		var nsData = self._assignNamespace(node.getAttribute('x-attr-ns-'+this));

    		if(nsData.fresh) {
    			newNamespaces.push(nsData);
    			self.stack.push({
    				"type": NS_END,
    				"namespace": nsData
    			});
    		};

    		self.result += ' ' + self._join(nsData.prefix, node.getAttribute('x-attr-name-'+this));
    		self.result += '="'+node.getAttribute('x-attr-value-'+this) +'"';
    	});

    	/* print new namespace declarations */
    	$.each(newNamespaces, function() {
    		self.result += " " + self._rjoin("xmlns", this.prefix);
    		self.result += '="' + this.uri + '"';
    	});

    	if (node.childNodes.length > 0) {
    		self.result += ">";
    		self._pushTagEnd(tagName);
    		self._pushChildren(node);
    	}
    	else {
    		self.result += "/>";
    	};
    }
};

function html2text(params) {
	try {
		var s = new HTMLSerializer();
		params.success( s.serialize(params.element, params.stripOuter) );
	} catch(e) {
		params.error("Nie udało się zserializować tekstu:" + e)
	}
}
<|endoftext|>


<commit_before>// @flow
import React, {PureComponent} from 'react'
import {Text, MultiAvatar, Icon, Usernames, Markdown, Box, ClickableBox, NativeListView} from '../../common-adapters/index.native'
import {globalStyles, globalColors, statusBarHeight} from '../../styles'
import {RowConnector} from './row'
import {debounce} from 'lodash'

import type {Props, RowProps} from './'

const AddNewRow = ({onNewChat}: {onNewChat: () => void}) => (
  <Box
    style={{...globalStyles.flexBoxRow, alignItems: 'center', flexShrink: 0, justifyContent: 'center', minHeight: 48}}>
    <ClickableBox style={{...globalStyles.flexBoxColumn, padding: 8}} onClick={onNewChat}>
      <Box style={{...globalStyles.flexBoxRow, alignItems: 'center', justifyContent: 'center'}}>
        <Icon type='iconfont-new' style={{color: globalColors.blue, marginRight: 9}} />
        <Text type='BodyBigLink'>New chat</Text>
      </Box>
    </ClickableBox>
  </Box>
)

// All this complexity isn't great but the current implementation of avatar forces us to juggle all these colors and
// forces us to explicitly choose undefined/the background/ etc. This can be cleaned up when avatar is simplified
function rowBorderColor (idx: number, isLastParticipant: boolean, backgroundColor: string) {
  // Only color the foreground items
  if (isLastParticipant) {
    return undefined
  }

  // We don't want a border if we're a single avatar
  return !idx && isLastParticipant ? undefined : backgroundColor
}

const Avatars = ({participants, youNeedToRekey, participantNeedToRekey, isMuted, hasUnread, isSelected, backgroundColor}) => {
  const avatarCount = Math.min(2, participants.count())

  let icon
  if (isMuted) {
    icon = <Icon type={isSelected ? 'icon-shh-active-16' : 'icon-shh-16'} style={avatarMutedIconStyle} />
  } else if (participantNeedToRekey || youNeedToRekey) {
    icon = <Icon type={isSelected ? 'icon-chat-addon-lock-active-8' : 'icon-chat-addon-lock-8'} style={avatarLockIconStyle} />
  }

  const avatarProps = participants.slice(0, 2).map((username, idx) => ({
    borderColor: rowBorderColor(idx, idx === (avatarCount - 1), backgroundColor),
    loadingColor: globalColors.blue3_40,
    size: 32,
    style: {
      opacity: youNeedToRekey || participantNeedToRekey ? 0.4 : 1,
    },
    username,
  })).toArray()

  return (
    <Box style={{...globalStyles.flexBoxRow, alignItems: 'flex-end', backgroundColor, justifyContent: 'flex-start', maxWidth: 55, minWidth: 55, paddingLeft: 4}}>
      <Box style={{position: 'relative'}}>
        <MultiAvatar singleSize={40} multiSize={32} avatarProps={avatarProps} style={{alignSelf: 'center', backgroundColor}} />
        {icon}
      </Box>
    </Box>
  )
}

const TopLine = ({hasUnread, showBold, participants, subColor, timestamp, usernameColor}) => {
  const boldOverride = showBold ? globalStyles.fontBold : null
  return (
    <Box style={{...globalStyles.flexBoxRow, alignItems: 'center', maxHeight: 18, minHeight: 18}}>
      <Box style={{...globalStyles.flexBoxRow, flex: 1, maxHeight: 18, minHeight: 18, position: 'relative'}}>
        <Box style={{...globalStyles.flexBoxColumn, bottom: 0, justifyContent: 'flex-start', left: 0, position: 'absolute', right: 0, top: 0}}>
          <Usernames
            inline={true}
            plainText={true}
            type='BodySemibold'
            style={{...boldOverride, color: usernameColor}}
            containerStyle={{color: usernameColor, paddingRight: 7}}
            users={participants.map(p => ({username: p})).toArray()}
            title={participants.join(', ')} />
        </Box>
      </Box>
      <Text type='BodySmall' style={{...boldOverride, color: subColor, lineHeight: 18}}>{timestamp}</Text>
      {hasUnread && <Box style={unreadDotStyle} />}
    </Box>
  )
}

const BottomLine = ({participantNeedToRekey, youNeedToRekey, isMuted, showBold, subColor, snippet, backgroundColor}) => {
  const boldOverride = showBold ? globalStyles.fontBold : null

  let content

  if (youNeedToRekey) {
    content = <Text type='BodySmallSemibold' backgroundMode='Terminal' style={{alignSelf: 'flex-start', backgroundColor: globalColors.red, borderRadius: 2, color: globalColors.white, fontSize: 10, paddingLeft: 2, paddingRight: 2}}>REKEY NEEDED</Text>
  } else if (participantNeedToRekey) {
    content = <Text type='BodySmall' backgroundMode='Terminal' style={{color: subColor}}>Waiting for participants to rekey</Text>
  } else if (snippet && !isMuted) {
    content = <Markdown preview={true} style={{...boldOverride, color: subColor, fontSize: 12, lineHeight: 16}}>{snippet}</Markdown>
  } else {
    return null
  }

  return (
    <Box style={{...globalStyles.flexBoxRow, backgroundColor, flexGrow: 1, maxHeight: 16, minHeight: 16, position: 'relative'}}>
      <Box style={{...globalStyles.flexBoxRow, alignItems: 'flex-start', bottom: 0, justifyContent: 'flex-start', left: 0, position: 'absolute', right: 0, top: 0}}>
        {content}
      </Box>
    </Box>
  )
}

const _Row = (props: RowProps) => {
  return (
    <ClickableBox onClick={() => props.onSelectConversation(props.conversationIDKey)} style={{backgroundColor: props.backgroundColor}}>
      <Box
        style={{...rowContainerStyle, backgroundColor: props.backgroundColor}}>
        <Avatars
          backgroundColor={props.backgroundColor}
          hasUnread={props.hasUnread}
          isMuted={props.isMuted}
          isSelected={props.isSelected}
          participantNeedToRekey={props.participantNeedToRekey}
          participants={props.participants}
          youNeedToRekey={props.youNeedToRekey}
        />
        <Box style={{
          ...conversationRowStyle,
          backgroundColor: props.backgroundColor,
          borderBottomColor: !props.isSelected ? globalColors.black_10 : props.backgroundColor,
        }}>
          <TopLine
            hasUnread={props.hasUnread}
            participants={props.participants}
            showBold={props.showBold}
            subColor={props.subColor}
            timestamp={props.timestamp}
            usernameColor={props.usernameColor}
          />
          <BottomLine
            backgroundColor={props.backgroundColor}
            isMuted={props.isMuted}
            participantNeedToRekey={props.participantNeedToRekey}
            showBold={props.showBold}
            snippet={props.snippet}
            subColor={props.subColor}
            youNeedToRekey={props.youNeedToRekey}
          />
        </Box>
      </Box>
    </ClickableBox>
  )
}

const Row = RowConnector(_Row)

class ConversationList extends PureComponent<void, Props, {dataSource: any}> {
  state = {dataSource: null}

  _itemRenderer = conversationIDKey => <Row conversationIDKey={conversationIDKey} key={conversationIDKey} />

  _ds = new NativeListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})

  _setupDataSource = (props: Props) => {
    this.setState({dataSource: this._ds.cloneWithRows(props.rows.toArray())})
  }

  componentWillReceiveProps (nextProps: Props) {
    if (this.props.rows !== nextProps.rows) {
      this._setupDataSource(nextProps)

      if (nextProps.rows.count()) {
        const conversationIDKey = nextProps.rows.get(0)
        this.props.onUntrustedInboxVisible(conversationIDKey, 20)
      }
    }
  }

  _onChangeVisibleRows = debounce((visibleRows) => {
    const idxs = Object.keys(visibleRows.s1)

    if (idxs.length) {
      const idx = parseInt(idxs[0], 10)
      const conversationIDKey = this.props.rows.get(idx)
      this.props.onUntrustedInboxVisible(conversationIDKey, idxs.length)
    }
  }, 1000)

  componentWillMount () {
    this.props.loadInbox()
    this._setupDataSource(this.props)
  }

  render () {
    return (
      <Box style={boxStyle}>
        <AddNewRow onNewChat={this.props.onNewChat} />
        <NativeListView
          enableEmptySections={true}
          style={listStyle}
          dataSource={this.state.dataSource}
          onChangeVisibleRows={this._onChangeVisibleRows}
          renderRow={this._itemRenderer} />
      </Box>
    )
  }
}

const boxStyle = {
  ...globalStyles.flexBoxColumn,
  backgroundColor: globalColors.darkBlue4,
  flex: 1,
  paddingTop: statusBarHeight,
}

const listStyle = {
  ...globalStyles.flexBoxColumn,
  flex: 1,
}

const unreadDotStyle = {
  backgroundColor: globalColors.orange,
  borderRadius: 3,
  height: 6,
  marginLeft: 4,
  width: 6,
}

const avatarMutedIconStyle = {
  bottom: 0,
  position: 'absolute',
  right: 0,
  zIndex: 1,
}

const avatarLockIconStyle = {
  bottom: 0,
  position: 'absolute',
  right: 0,
  zIndex: 1,
}

const conversationRowStyle = {
  ...globalStyles.flexBoxColumn,
  borderBottomWidth: 1,
  flexGrow: 1,
  justifyContent: 'center',
  maxHeight: 56,
  minHeight: 56,
  paddingRight: 8,
}

const rowContainerStyle = {
  ...globalStyles.flexBoxRow,
  ...globalStyles.clickable,
  alignItems: 'center',
  flexGrow: 1,
  maxHeight: 56,
  minHeight: 56,
}

export default ConversationList
<commit_msg>Show snippets when muted on mobile<commit_after>// @flow
import React, {PureComponent} from 'react'
import {Text, MultiAvatar, Icon, Usernames, Markdown, Box, ClickableBox, NativeListView} from '../../common-adapters/index.native'
import {globalStyles, globalColors, statusBarHeight} from '../../styles'
import {RowConnector} from './row'
import {debounce} from 'lodash'

import type {Props, RowProps} from './'

const AddNewRow = ({onNewChat}: {onNewChat: () => void}) => (
  <Box
    style={{...globalStyles.flexBoxRow, alignItems: 'center', flexShrink: 0, justifyContent: 'center', minHeight: 48}}>
    <ClickableBox style={{...globalStyles.flexBoxColumn, padding: 8}} onClick={onNewChat}>
      <Box style={{...globalStyles.flexBoxRow, alignItems: 'center', justifyContent: 'center'}}>
        <Icon type='iconfont-new' style={{color: globalColors.blue, marginRight: 9}} />
        <Text type='BodyBigLink'>New chat</Text>
      </Box>
    </ClickableBox>
  </Box>
)

// All this complexity isn't great but the current implementation of avatar forces us to juggle all these colors and
// forces us to explicitly choose undefined/the background/ etc. This can be cleaned up when avatar is simplified
function rowBorderColor (idx: number, isLastParticipant: boolean, backgroundColor: string) {
  // Only color the foreground items
  if (isLastParticipant) {
    return undefined
  }

  // We don't want a border if we're a single avatar
  return !idx && isLastParticipant ? undefined : backgroundColor
}

const Avatars = ({participants, youNeedToRekey, participantNeedToRekey, isMuted, hasUnread, isSelected, backgroundColor}) => {
  const avatarCount = Math.min(2, participants.count())

  let icon
  if (isMuted) {
    icon = <Icon type={isSelected ? 'icon-shh-active-16' : 'icon-shh-16'} style={avatarMutedIconStyle} />
  } else if (participantNeedToRekey || youNeedToRekey) {
    icon = <Icon type={isSelected ? 'icon-chat-addon-lock-active-8' : 'icon-chat-addon-lock-8'} style={avatarLockIconStyle} />
  }

  const avatarProps = participants.slice(0, 2).map((username, idx) => ({
    borderColor: rowBorderColor(idx, idx === (avatarCount - 1), backgroundColor),
    loadingColor: globalColors.blue3_40,
    size: 32,
    style: {
      opacity: youNeedToRekey || participantNeedToRekey ? 0.4 : 1,
    },
    username,
  })).toArray()

  return (
    <Box style={{...globalStyles.flexBoxRow, alignItems: 'flex-end', backgroundColor, justifyContent: 'flex-start', maxWidth: 55, minWidth: 55, paddingLeft: 4}}>
      <Box style={{position: 'relative'}}>
        <MultiAvatar singleSize={40} multiSize={32} avatarProps={avatarProps} style={{alignSelf: 'center', backgroundColor}} />
        {icon}
      </Box>
    </Box>
  )
}

const TopLine = ({hasUnread, showBold, participants, subColor, timestamp, usernameColor}) => {
  const boldOverride = showBold ? globalStyles.fontBold : null
  return (
    <Box style={{...globalStyles.flexBoxRow, alignItems: 'center', maxHeight: 18, minHeight: 18}}>
      <Box style={{...globalStyles.flexBoxRow, flex: 1, maxHeight: 18, minHeight: 18, position: 'relative'}}>
        <Box style={{...globalStyles.flexBoxColumn, bottom: 0, justifyContent: 'flex-start', left: 0, position: 'absolute', right: 0, top: 0}}>
          <Usernames
            inline={true}
            plainText={true}
            type='BodySemibold'
            style={{...boldOverride, color: usernameColor}}
            containerStyle={{color: usernameColor, paddingRight: 7}}
            users={participants.map(p => ({username: p})).toArray()}
            title={participants.join(', ')} />
        </Box>
      </Box>
      <Text type='BodySmall' style={{...boldOverride, color: subColor, lineHeight: 18}}>{timestamp}</Text>
      {hasUnread && <Box style={unreadDotStyle} />}
    </Box>
  )
}

const BottomLine = ({participantNeedToRekey, youNeedToRekey, isMuted, showBold, subColor, snippet, backgroundColor}) => {
  const boldOverride = showBold ? globalStyles.fontBold : null

  let content

  if (youNeedToRekey) {
    content = <Text type='BodySmallSemibold' backgroundMode='Terminal' style={{alignSelf: 'flex-start', backgroundColor: globalColors.red, borderRadius: 2, color: globalColors.white, fontSize: 10, paddingLeft: 2, paddingRight: 2}}>REKEY NEEDED</Text>
  } else if (participantNeedToRekey) {
    content = <Text type='BodySmall' backgroundMode='Terminal' style={{color: subColor}}>Waiting for participants to rekey</Text>
  } else if (snippet) {
    content = <Markdown preview={true} style={{...boldOverride, color: subColor, fontSize: 12, lineHeight: 16}}>{snippet}</Markdown>
  } else {
    return null
  }

  return (
    <Box style={{...globalStyles.flexBoxRow, backgroundColor, flexGrow: 1, maxHeight: 16, minHeight: 16, position: 'relative'}}>
      <Box style={{...globalStyles.flexBoxRow, alignItems: 'flex-start', bottom: 0, justifyContent: 'flex-start', left: 0, position: 'absolute', right: 0, top: 0}}>
        {content}
      </Box>
    </Box>
  )
}

const _Row = (props: RowProps) => {
  return (
    <ClickableBox onClick={() => props.onSelectConversation(props.conversationIDKey)} style={{backgroundColor: props.backgroundColor}}>
      <Box
        style={{...rowContainerStyle, backgroundColor: props.backgroundColor}}>
        <Avatars
          backgroundColor={props.backgroundColor}
          hasUnread={props.hasUnread}
          isMuted={props.isMuted}
          isSelected={props.isSelected}
          participantNeedToRekey={props.participantNeedToRekey}
          participants={props.participants}
          youNeedToRekey={props.youNeedToRekey}
        />
        <Box style={{
          ...conversationRowStyle,
          backgroundColor: props.backgroundColor,
          borderBottomColor: !props.isSelected ? globalColors.black_10 : props.backgroundColor,
        }}>
          <TopLine
            hasUnread={props.hasUnread}
            participants={props.participants}
            showBold={props.showBold}
            subColor={props.subColor}
            timestamp={props.timestamp}
            usernameColor={props.usernameColor}
          />
          <BottomLine
            backgroundColor={props.backgroundColor}
            isMuted={props.isMuted}
            participantNeedToRekey={props.participantNeedToRekey}
            showBold={props.showBold}
            snippet={props.snippet}
            subColor={props.subColor}
            youNeedToRekey={props.youNeedToRekey}
          />
        </Box>
      </Box>
    </ClickableBox>
  )
}

const Row = RowConnector(_Row)

class ConversationList extends PureComponent<void, Props, {dataSource: any}> {
  state = {dataSource: null}

  _itemRenderer = conversationIDKey => <Row conversationIDKey={conversationIDKey} key={conversationIDKey} />

  _ds = new NativeListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})

  _setupDataSource = (props: Props) => {
    this.setState({dataSource: this._ds.cloneWithRows(props.rows.toArray())})
  }

  componentWillReceiveProps (nextProps: Props) {
    if (this.props.rows !== nextProps.rows) {
      this._setupDataSource(nextProps)

      if (nextProps.rows.count()) {
        const conversationIDKey = nextProps.rows.get(0)
        this.props.onUntrustedInboxVisible(conversationIDKey, 20)
      }
    }
  }

  _onChangeVisibleRows = debounce((visibleRows) => {
    const idxs = Object.keys(visibleRows.s1)

    if (idxs.length) {
      const idx = parseInt(idxs[0], 10)
      const conversationIDKey = this.props.rows.get(idx)
      this.props.onUntrustedInboxVisible(conversationIDKey, idxs.length)
    }
  }, 1000)

  componentWillMount () {
    this.props.loadInbox()
    this._setupDataSource(this.props)
  }

  render () {
    return (
      <Box style={boxStyle}>
        <AddNewRow onNewChat={this.props.onNewChat} />
        <NativeListView
          enableEmptySections={true}
          style={listStyle}
          dataSource={this.state.dataSource}
          onChangeVisibleRows={this._onChangeVisibleRows}
          renderRow={this._itemRenderer} />
      </Box>
    )
  }
}

const boxStyle = {
  ...globalStyles.flexBoxColumn,
  backgroundColor: globalColors.darkBlue4,
  flex: 1,
  paddingTop: statusBarHeight,
}

const listStyle = {
  ...globalStyles.flexBoxColumn,
  flex: 1,
}

const unreadDotStyle = {
  backgroundColor: globalColors.orange,
  borderRadius: 3,
  height: 6,
  marginLeft: 4,
  width: 6,
}

const avatarMutedIconStyle = {
  bottom: 0,
  position: 'absolute',
  right: 0,
  zIndex: 1,
}

const avatarLockIconStyle = {
  bottom: 0,
  position: 'absolute',
  right: 0,
  zIndex: 1,
}

const conversationRowStyle = {
  ...globalStyles.flexBoxColumn,
  borderBottomWidth: 1,
  flexGrow: 1,
  justifyContent: 'center',
  maxHeight: 56,
  minHeight: 56,
  paddingRight: 8,
}

const rowContainerStyle = {
  ...globalStyles.flexBoxRow,
  ...globalStyles.clickable,
  alignItems: 'center',
  flexGrow: 1,
  maxHeight: 56,
  minHeight: 56,
}

export default ConversationList
<|endoftext|>