<commit_before>var global = require('../../global');

module.exports = function (pox) {

    var _items = pox.items.map(po => {
        return po.items.map(poItem => {
            // var productName = [poItem.product.name, po.refNo].filter(r => r && r.toString().trim() != '').join("\n");
            return {
                product: poItem.product.name,
                prNo: po.refNo,
                quantity: poItem.dealQuantity,
                uom: poItem.dealUom.unit,
                price: poItem.pricePerDealUnit
            };
        });
    });

    _items = [].concat.apply([], _items);

    var items = [];
    _items.reduce(function (res, value) {
        if (!res[value.product]) {
            res[value.product] = {
                product: value.product,
                quantity: 0,
                prNo: "",
                uom: value.uom,
                price: value.price
            };
            items.push(res[value.product])
        }
        res[value.product].quantity += value.quantity;
        res[value.product].prNo = `${res[value.product].prNo}\n${value.prNo}`;
        return res;
    }, {});

    var iso = "FM-PB-00-06-009";
    var number = pox.no;
    var currency = pox.currency.code;
    var supplier = pox.supplier.name;
    var supplierAtt = pox.supplier.PIC;
    var supplierTel = pox.supplier.contact;

    var locale = global.config.locale;

    var moment = require('moment');
    moment.locale(locale.name);

    var header = [{
        text: 'PT. DAN LIRIS',
        style: 'bold'
    }, {
        columns: [{
            width: '50%',
            stack: [
                'Head Office   : ',
                'Kelurahan Banaran',
                'Kecamatan Grogol',
                'Sukoharjo 57193 - INDONESIA',
                'PO.BOX 166 Solo 57100',
                'Telp. (0271) 740888, 714400',
                'Fax. (0271) 735222, 740777'
            ],
            style: ['size07', 'bold']
        }, {
            stack: [{
                text: iso,
                alignment: "right",
                style: ['size08']
            }, {
                text: `Nomor PO : ${number}`,
                alignment: "right",
                style: ['size09', 'bold']
            }]

        }

        ]
    }, {
        alignment: "center",
        text: 'ORDER PEMBELIAN',
        style: ['size09', 'bold']
    },
        '\n'
    ];

    var attention = [{
        columns: [{
            width: '15%',
            text: 'Kepada Yth:',
            style: ['size08']
        }, {
            width: '*',
            text: `${supplier}\n Attn. ${supplierAtt}\n Telp. ${supplierTel}`,
            style: ['size08']
        }, {
            width: '35%',
            stack: [
                `Sukoharjo, ${moment(pox.date).format(locale.date.format)} `, {
                    text: [
                        'Mohon', {
                            text: ' di-fax kembali',
                            style: 'bold'
                        }, ' setelah\n', {
                            text: 'ditandatangani',
                            style: ['bold'],
                            decoration: 'underline'
                        }, ' dan ', {
                            text: 'distempel ',
                            style: ['bold'],
                            decoration: 'underline'
                        }, 'perusahaan.Terima Kasih.'
                    ],
                    style: ['size07']
                }
            ],
            style: ['size08']
        }]
    }];

    var opening = {
        text: [
            '\n', {
                text: 'Dengan hormat,\n'
            }, {
                text: 'Yang bertanda tangan di bawah ini, '
            }, {
                text: 'PT. DAN LIRIS, SOLO',
                style: ['bold']
            }, {
                text: ' (selanjutnya disebut sebagai pihak Pembeli) dan '
            }, {
                text: supplier,
                style: ['bold']
            }, {
                text: ' (selanjutnya disebut sebagai pihak Penjual) saling menyetujui untuk mengadakan kontrak jual beli dengan ketentuan sebagai berikut: '
            },
            '\n\n'
        ],
        style: ['size09', 'justify']
    };

    var thead = [{
        text: 'NAMA DAN JENIS BARANG',
        style: ['size08', 'bold', 'center']
    }, {
        text: 'JUMLAH',
        style: ['size08', 'bold', 'center']
    }, {
        text: 'HARGA SATUAN',
        style: ['size08', 'bold', 'center']
    }, {
        text: 'SUB TOTAL',
        style: ['size08', 'bold', 'center']
    }];

    var tbody = items.map(function (item) {
        return [{
            stack: [{
                text: item.product,
                style: 'bold'
            }, item.prNo],
            style: ['size08']
        }, {
            text: parseFloat(item.quantity).toLocaleString(locale, locale.decimal) + ' ' + item.uom,
            style: ['size08', 'center']
        }, {
            columns: [{
                width: '10%',
                text: `${currency}`
            }, {
                width: '*',
                text: `${parseFloat(item.price).toLocaleString(locale, locale.currency)}`,
                style: ['right']
            }],
            style: ['size08']
        }, {
            columns: [{
                width: '10%',
                text: `${currency}`
            }, {
                width: '*',
                text: `${parseFloat(item.quantity * item.price).toLocaleString(locale, locale.currency)}`,
                style: ['right']
            }],
            style: ['size08']
        }];
    });

    tbody = tbody.length > 0 ? tbody : [
        [{
            text: "tidak ada barang",
            style: ['size08', 'center'],
            colSpan: 4
        }, "", "", ""]
    ];
    var initialValue = {
        price: 0,
        quntity: 0
    };

    var sum = (items.length > 0 ? items : [initialValue])
        .map(item => item.price * item.quantity)
        .reduce(function (prev, curr, index, arr) {
            return prev + curr;
        }, 0);

    var vat = pox.useIncomeTax ? sum * 0.1 : 0;

    var tfoot = [
        [{
            text: 'Jumlah',
            style: ['size08', 'bold', 'right'],
            colSpan: 3
        }, "", "", {
            columns: [{
                width: '10%',
                text: currency
            }, {
                width: '*',
                text: parseFloat(sum).toLocaleString(locale, locale.currency),
                style: ['right']
            }],
            style: ['size08']
        }],
        [{
            text: 'PPN 10%',
            style: ['size08', 'bold', 'right'],
            colSpan: 3
        }, null, null, {
            columns: [{
                width: '10%',
                text: currency
            }, {
                width: '*',
                text: parseFloat(vat).toLocaleString(locale, locale.currency),
                style: ['right']
            }],
            style: ['size08']
        }],
        [{
            text: 'Grand Total',
            style: ['size08', 'bold', 'right'],
            colSpan: 3
        }, null, null, {
            columns: [{
                width: '10%',
                text: currency
            }, {
                width: '*',
                text: parseFloat(sum + vat).toLocaleString(locale, locale.currency),
                style: ['bold', 'right']
            }],
            style: ['size09']
        }]
    ];

    var table = [{
        table: {
            widths: ['*', '15%', '20%', '25%'],
            headerRows: 1,
            body: [].concat([thead], tbody, tfoot)
        }
    }];
    var footer = [
        '\n', {
            stack: [{
                columns: [{
                    width: '40%',
                    columns: [{
                        width: '40%',
                        stack: ['Ongkos Kirim', 'Pembayaran']
                    }, {
                        width: '3%',
                        stack: [':', ':']
                    }, {
                        width: '*',
                        stack: [`Ditanggung ${pox.freightCostBy}`, `${pox.paymentMethod}, ${pox.paymentDueDays} hari setelah terima barang`]
                    }]
                }, {
                    width: '20%',
                    text: ''
                }, {
                    width: '40%',
                    columns: [{
                        width: '45%',
                        stack: ['Delivery', 'Lain-lain']
                    }, {
                        width: '3%',
                        stack: [':', ':']
                    }, {
                        width: '*',
                        stack: [{
                            text: `${moment(pox.expectedDeliveryDate).format(locale.date.format)}`,
                            style: ['bold']
                        }, `${pox.remark}`]
                    }]
                }]
            },
                '\n\n\n', {
                columns: [{
                    width: '35%',
                    stack: ['Pembeli\n\n\n\n\n', {
                        text: pox._createdBy,
                        style: ['bold']
                    }],
                    style: 'center'
                }, {
                    width: '30%',
                    text: ''
                }, {
                    width: '35%',
                    stack: ['Penjual\n\n\n\n\n', {
                        text: supplier,
                        style: ['bold']
                    }],
                    style: 'center'
                }]
            }
            ],
            style: ['size08']
        }
    ];

    var dd = {
        pageSize: 'A5',
        pageOrientation: 'portrait',
        pageMargins: 20,
        content: [].concat(header, attention, opening, table, footer),
        styles: {
            size06: {
                fontSize: 6
            },
            size07: {
                fontSize: 7
            },
            size08: {
                fontSize: 8
            },
            size09: {
                fontSize: 9
            },
            size10: {
                fontSize: 10
            },
            bold: {
                bold: true
            },
            center: {
                alignment: 'center'
            },
            left: {
                alignment: 'left'
            },
            right: {
                alignment: 'right'
            },
            justify: {
                alignment: 'justify'
            }
        }
    };

    return dd;
};<commit_msg>fix file definition of PO External<commit_after>var global = require('../../global');

module.exports = function (pox) {

    var _items = pox.items.map(po => {
        return po.items.map(poItem => {
            // var productName = [poItem.product.name, po.refNo].filter(r => r && r.toString().trim() != '').join("\n");
            return {
                product: poItem.product.name,
                prNo: po.refNo,
                quantity: poItem.dealQuantity,
                uom: poItem.dealUom.unit,
                price: poItem.pricePerDealUnit,
                remark: poItem.remark
            };
        });
    });

    _items = [].concat.apply([], _items);

    var items = [];
    _items.reduce(function (res, value) {
        if (!res[value.product]) {
            res[value.product] = {
                product: value.product,
                quantity: 0,
                prNo: "",
                uom: value.uom,
                price: value.price
            };
            items.push(res[value.product])
        }
        res[value.product].quantity += value.quantity;
        res[value.product].prNo = `${res[value.product].prNo}\n${value.prNo}`;
        return res;
    }, {});

    var iso = "FM-PB-00-06-009";
    var number = pox.no;
    var currency = pox.currency.code;
    var supplier = pox.supplier.name;
    var supplierAtt = pox.supplier.PIC;
    var supplierTel = pox.supplier.contact;

    var locale = global.config.locale;

    var moment = require('moment');
    moment.locale(locale.name);

    var header = [{
        text: 'PT. DAN LIRIS',
        style: 'bold'
    }, {
        columns: [{
            width: '50%',
            stack: [
                'Head Office   : ',
                'Kelurahan Banaran',
                'Kecamatan Grogol',
                'Sukoharjo 57193 - INDONESIA',
                'PO.BOX 166 Solo 57100',
                'Telp. (0271) 740888, 714400',
                'Fax. (0271) 735222, 740777'
            ],
            style: ['size07', 'bold']
        }, {
            stack: [{
                text: iso,
                alignment: "right",
                style: ['size08']
            }, {
                text: `Nomor PO : ${number}`,
                alignment: "right",
                style: ['size09', 'bold']
            }]

        }

        ]
    }, {
        alignment: "center",
        text: 'ORDER PEMBELIAN',
        style: ['size09', 'bold']
    },
        '\n'
    ];

    var attention = [{
        columns: [{
            width: '15%',
            text: 'Kepada Yth:',
            style: ['size08']
        }, {
            width: '*',
            text: `${supplier}\n Attn. ${supplierAtt}\n Telp. ${supplierTel}`,
            style: ['size08']
        }, {
            width: '35%',
            stack: [
                `Sukoharjo, ${moment(pox.date).format(locale.date.format)} `, {
                    text: [
                        'Mohon', {
                            text: ' di-fax kembali',
                            style: 'bold'
                        }, ' setelah\n', {
                            text: 'ditandatangani',
                            style: ['bold'],
                            decoration: 'underline'
                        }, ' dan ', {
                            text: 'distempel ',
                            style: ['bold'],
                            decoration: 'underline'
                        }, 'perusahaan.Terima Kasih.'
                    ],
                    style: ['size07']
                }
            ],
            style: ['size08']
        }]
    }];

    var opening = {
        text: [
            '\n', {
                text: 'Dengan hormat,\n'
            }, {
                text: 'Yang bertanda tangan di bawah ini, '
            }, {
                text: 'PT. DAN LIRIS, SOLO',
                style: ['bold']
            }, {
                text: ' (selanjutnya disebut sebagai pihak Pembeli) dan '
            }, {
                text: supplier,
                style: ['bold']
            }, {
                text: ' (selanjutnya disebut sebagai pihak Penjual) saling menyetujui untuk mengadakan kontrak jual beli dengan ketentuan sebagai berikut: '
            },
            '\n\n'
        ],
        style: ['size09', 'justify']
    };

    var thead = [{
        text: 'NAMA DAN JENIS BARANG',
        style: ['size08', 'bold', 'center']
    }, {
        text: 'JUMLAH',
        style: ['size08', 'bold', 'center']
    }, {
        text: 'HARGA SATUAN',
        style: ['size08', 'bold', 'center']
    }, {
        text: 'SUB TOTAL',
        style: ['size08', 'bold', 'center']
    }];

    var tbody = items.map(function (item) {
        return [{
            stack: [{
                text: item.product,
                style: 'bold'
            },item.remark, item.prNo],
            style: ['size08']
        }, {
            text: parseFloat(item.quantity).toLocaleString(locale, locale.decimal) + ' ' + item.uom,
            style: ['size08', 'center']
        }, {
            columns: [{
                width: '10%',
                text: `${currency}`
            }, {
                width: '*',
                text: `${parseFloat(item.price).toLocaleString(locale, locale.currency)}`,
                style: ['right']
            }],
            style: ['size08']
        }, {
            columns: [{
                width: '10%',
                text: `${currency}`
            }, {
                width: '*',
                text: `${parseFloat(item.quantity * item.price).toLocaleString(locale, locale.currency)}`,
                style: ['right']
            }],
            style: ['size08']
        }];
    });

    tbody = tbody.length > 0 ? tbody : [
        [{
            text: "tidak ada barang",
            style: ['size08', 'center'],
            colSpan: 4
        }, "", "", ""]
    ];
    var initialValue = {
        price: 0,
        quntity: 0
    };

    var sum = (items.length > 0 ? items : [initialValue])
        .map(item => item.price * item.quantity)
        .reduce(function (prev, curr, index, arr) {
            return prev + curr;
        }, 0);

    var vat = pox.useIncomeTax ? sum * 0.1 : 0;

    var tfoot = [
        [{
            text: 'Jumlah',
            style: ['size08', 'bold', 'right'],
            colSpan: 3
        }, "", "", {
            columns: [{
                width: '10%',
                text: currency
            }, {
                width: '*',
                text: parseFloat(sum).toLocaleString(locale, locale.currency),
                style: ['right']
            }],
            style: ['size08']
        }],
        [{
            text: 'PPN 10%',
            style: ['size08', 'bold', 'right'],
            colSpan: 3
        }, null, null, {
            columns: [{
                width: '10%',
                text: currency
            }, {
                width: '*',
                text: parseFloat(vat).toLocaleString(locale, locale.currency),
                style: ['right']
            }],
            style: ['size08']
        }],
        [{
            text: 'Grand Total',
            style: ['size08', 'bold', 'right'],
            colSpan: 3
        }, null, null, {
            columns: [{
                width: '10%',
                text: currency
            }, {
                width: '*',
                text: parseFloat(sum + vat).toLocaleString(locale, locale.currency),
                style: ['bold', 'right']
            }],
            style: ['size09']
        }]
    ];

    var table = [{
        table: {
            widths: ['*', '15%', '20%', '25%'],
            headerRows: 1,
            body: [].concat([thead], tbody, tfoot)
        }
    }];
    var footer = [
        '\n', {
            stack: [{
                columns: [{
                    width: '40%',
                    columns: [{
                        width: '40%',
                        stack: ['Ongkos Kirim', 'Pembayaran']
                    }, {
                        width: '3%',
                        stack: [':', ':']
                    }, {
                        width: '*',
                        stack: [`Ditanggung ${pox.freightCostBy}`, `${pox.paymentMethod}, ${pox.paymentDueDays} hari setelah terima barang`]
                    }]
                }, {
                    width: '20%',
                    text: ''
                }, {
                    width: '40%',
                    columns: [{
                        width: '45%',
                        stack: ['Delivery', 'Lain-lain']
                    }, {
                        width: '3%',
                        stack: [':', ':']
                    }, {
                        width: '*',
                        stack: [{
                            text: `${moment(pox.expectedDeliveryDate).format(locale.date.format)}`,
                            style: ['bold']
                        }, `${pox.remark}`]
                    }]
                }]
            },
                '\n\n\n', {
                columns: [{
                    width: '35%',
                    stack: ['Pembeli\n\n\n\n\n', {
                        text: pox._createdBy,
                        style: ['bold']
                    }],
                    style: 'center'
                }, {
                    width: '30%',
                    text: ''
                }, {
                    width: '35%',
                    stack: ['Penjual\n\n\n\n\n', {
                        text: supplier,
                        style: ['bold']
                    }],
                    style: 'center'
                }]
            }
            ],
            style: ['size08']
        }
    ];

    var dd = {
        pageSize: 'A5',
        pageOrientation: 'portrait',
        pageMargins: 20,
        content: [].concat(header, attention, opening, table, footer),
        styles: {
            size06: {
                fontSize: 6
            },
            size07: {
                fontSize: 7
            },
            size08: {
                fontSize: 8
            },
            size09: {
                fontSize: 9
            },
            size10: {
                fontSize: 10
            },
            bold: {
                bold: true
            },
            center: {
                alignment: 'center'
            },
            left: {
                alignment: 'left'
            },
            right: {
                alignment: 'right'
            },
            justify: {
                alignment: 'justify'
            }
        }
    };

    return dd;
};<|endoftext|>


<commit_before>/* https://stress.js.org/1.0/ */
!function(t,e,s,a){function i(){var e=t("body");e.find("[data-init-stress]").each(function(e){var s=t(this).attr("data-init-stress");""===s.trim()?t(this).stress():(s=parseInt(s),s>=2&&12>=s&&t(this).stress({total:s}))})}var l="stress",n="plugin_"+l,o=["active","complete","fail"],r=function(t,e){this.element=t,this.options={total:2},this.init(e)};r.prototype={init:function(e){if(t.extend(this.options,e),this.options.total<=1||this.options.total>12)return void alert("total must be > 1 and <= 12");var s=[6,4,3,2,2,1,1,1,1,1,1],a=["","","","five-cols","","seven-cols","eight-cols","nine-cols","ten-cols","eleven-cols",""],i='

';this.element.addClass("row "+a[this.options.total-2]),this.element.attr("data-stress","stress");for(var l="",n=1;n<=this.options.total;n++)l+=i;this.element.html(l);var r=parseInt(this.element.attr("data-now")?this.element.attr("data-now"):0),d=this.element.attr("data-status"),p=this.element.attr("data-title-on-now");if(0>r||r>this.options.total)return void alert("data-now must be >= 0 and <= "+this.options.total);if(0!=r&&d){if(-1==o.indexOf(d))return void alert("invalid data-status");for(var m=r-1,n=1;m>=n;n++)this.element.find(".stress-step").eq(n-1).addClass("complete");if(this.element.find(".stress-step").eq(m).addClass(d),p){var h=this.element.find(".stress-step").eq(m).find(".stress-circle");h.attr("data-toggle","tooltip"),h.attr("data-placement","top"),h.attr("data-title",p),h.attr("data-html","true"),h.attr("title",p),this.element.find('[data-toggle="tooltip"]').tooltip()}"complete"==d&&r<this.options.total&&this.element.find(".stress-step").eq(m).addClass("not-last")}},update:function(t,e){if(this.element.attr("data-stress")&&"stress"==this.element.attr("data-stress"))if("now"==t){var s=parseInt(this.element.attr("data-now")?this.element.attr("data-now"):0);s>0&&(s-=1,this.element.find(".stress-step").eq(s).find('.stress-circle[data-toggle="tooltip"]').tooltip("destroy"),this.element.find(".stress-step").eq(s).find(".stress-circle").removeAttr("data-toggle data-placement data-title data-original-title data-html title"));var a=parseInt(e),i=this.element.attr("data-status"),l=this.element.attr("data-title-on-now");if(0>a||a>this.options.total)return void alert("data-now must be >= 0 and <= "+this.options.total);if(this.element.attr("data-now",a),0==a)return void this.element.find(".stress-step").removeClass("complete fail active not-last");if(i){if(-1==o.indexOf(i))return void alert("invalid data-status");var n=a-1;this.element.find(".stress-step").eq(n).find('.stress-circle[data-toggle="tooltip"]').tooltip("destroy"),this.element.find(".stress-step").eq(n).find(".stress-circle").removeAttr("data-toggle data-placement data-title data-original-title data-html title"),this.element.find(".stress-step").removeClass("complete fail active not-last");for(var r=1;n>=r;r++)this.element.find(".stress-step").eq(r-1).addClass("complete");if(this.element.find(".stress-step").eq(n).addClass(i),l){var d=this.element.find(".stress-step").eq(n).find(".stress-circle");d.attr("data-toggle","tooltip"),d.attr("data-placement","top"),d.attr("data-title",l),d.attr("data-html","true"),d.attr("title",l),this.element.find('[data-toggle="tooltip"]').tooltip()}"complete"==i&&a<this.options.total&&this.element.find(".stress-step").eq(n).addClass("not-last")}}else if("status"==t){var s=parseInt(this.element.attr("data-now")?this.element.attr("data-now"):0);s>0&&(s-=1,this.element.find(".stress-step").eq(s).find('.stress-circle[data-toggle="tooltip"]').tooltip("destroy"),this.element.find(".stress-step").eq(s).find(".stress-circle").removeAttr("data-toggle data-placement data-title data-original-title data-html title"));var a=parseInt(this.element.attr("data-now")?this.element.attr("data-now"):0),i=e,l=this.element.attr("data-title-on-now");if(0>a||a>this.options.total)return void alert("data-now must be >= 0 and <= "+this.options.total);if(i){if(-1==o.indexOf(i))return void alert("invalid data-status");if(this.element.attr("data-status",i),0==a)return;var n=a-1;this.element.find(".stress-step").eq(n).find('.stress-circle[data-toggle="tooltip"]').tooltip("destroy"),this.element.find(".stress-step").eq(n).find(".stress-circle").removeAttr("data-toggle data-placement data-title data-original-title data-html title"),this.element.find(".stress-step").removeClass("complete fail active not-last");for(var r=1;n>=r;r++)this.element.find(".stress-step").eq(r-1).addClass("complete");if(this.element.find(".stress-step").eq(n).addClass(i),l){var d=this.element.find(".stress-step").eq(n).find(".stress-circle");d.attr("data-toggle","tooltip"),d.attr("data-placement","top"),d.attr("data-title",l),d.attr("data-html","true"),d.attr("title",l),this.element.find('[data-toggle="tooltip"]').tooltip()}"complete"==i&&a<this.options.total&&this.element.find(".stress-step").eq(n).addClass("not-last")}}else if("title-on-now"==t){var a=parseInt(this.element.attr("data-now")?this.element.attr("data-now"):0),l=e;if(0>a||a>this.options.total)return void alert("data-now must be >= 0 and <= "+this.options.total);if(l){if(this.element.attr("data-title-on-now",l),0==a)return;var n=a-1,d=this.element.find(".stress-step").eq(n).find(".stress-circle");d.attr("data-toggle")&&d.attr("data-placement")?(d.attr("data-title",l),d.attr("data-html","true"),d.attr("title",l),this.element.find(".stress-step").eq(n).find('.stress-circle[data-toggle="tooltip"]').tooltip("fixTitle")):(d.attr("data-toggle","tooltip"),d.attr("data-placement","top"),d.attr("data-title",l),d.attr("data-html","true"),d.attr("title",l),this.element.find(".stress-step").eq(n).find('.stress-circle[data-toggle="tooltip"]').tooltip())}}},"delete":function(){this.element.removeAttr("data-stress"),this.element.removeClass("row five-cols seven-cols eight-cols nine-cols ten-cols eleven-cols"),this.element.html("")},Title:function(t,e){if(t=parseInt(t?t:0),0>t||t>this.options.total)return void alert("step must be >= 0 and <= "+this.options.total);if(0!=t&&(t-=1,e)){var s=this.element.find(".stress-step").eq(t).find(".stress-circle");s.attr("data-toggle")&&s.attr("data-placement")?(s.attr("data-title",e),s.attr("data-html","true"),s.attr("title",e),this.element.find(".stress-step").eq(t).find('.stress-circle[data-toggle="tooltip"]').tooltip("fixTitle")):(s.attr("data-toggle","tooltip"),s.attr("data-placement","top"),s.attr("data-title",e),s.attr("data-html","true"),s.attr("title",e),this.element.find(".stress-step").eq(t).find('.stress-circle[data-toggle="tooltip"]').tooltip()),t+1==this.element.attr("data-now")&&this.element.attr("data-title-on-now",e)}},deleteTitle:function(t){return t=parseInt(t?t:0),0>t||t>this.options.total?void alert("step must be >= 0 and <= "+this.options.total):void(0!=t&&(t-=1,this.element.find(".stress-step").eq(t).find('.stress-circle[data-toggle="tooltip"]').tooltip("destroy"),this.element.find(".stress-step").eq(t).find(".stress-circle").removeAttr("data-toggle data-placement data-title data-original-title data-html title"),t+1==this.element.attr("data-now")&&this.element.removeAttr("data-title-on-now")))}},t.fn[l]=function(t){var e=this.data(n);return e instanceof r?"undefined"!=typeof t&&e.init(t):(e=new r(this,t),this.data(n,e)),e},i()}(jQuery,window,document);<commit_msg>Version 1.0<commit_after>/* https://stress.js.org/1.0/ */
!function(t,e,s,a){function i(){var e=t("body");e.find("[data-init-stress]").each(function(e){var s=t(this).attr("data-init-stress");""===s.trim()?t(this).stress():(s=parseInt(s),s>=2&&12>=s&&t(this).stress({total:s}))})}var l="stress",n="plugin_"+l,o=["active","complete","fail"],r=function(t,e){this.element=t,this.options={total:2},this.init(e)};r.prototype={init:function(e){if(t.extend(this.options,e),this.options.total<=1||this.options.total>12)return void alert("total must be > 1 and <= 12");var s=[6,4,3,2,2,1,1,1,1,1,1],a=["","","","five-cols","","seven-cols","eight-cols","nine-cols","ten-cols","eleven-cols",""],i='

';this.element.addClass("row "+a[this.options.total-2]),this.element.attr("data-stress","stress");for(var l="",n=1;n<=this.options.total;n++)l+=i;this.element.html(l);var r=parseInt(this.element.attr("data-now")?this.element.attr("data-now"):0),d=this.element.attr("data-status"),p=this.element.attr("data-title-on-now");if(0>r||r>this.options.total)return void alert("data-now must be >= 0 and <= "+this.options.total);if(0!=r&&d){if(-1==o.indexOf(d))return void alert("invalid data-status");for(var m=r-1,n=1;m>=n;n++)this.element.find(".stress-step").eq(n-1).addClass("complete");if(this.element.find(".stress-step").eq(m).addClass(d),p){var h=this.element.find(".stress-step").eq(m).find(".stress-circle");h.attr("data-toggle","tooltip"),h.attr("data-placement","top"),h.attr("data-title",p),h.attr("data-html","true"),h.attr("title",p),this.element.find('[data-toggle="tooltip"]').tooltip()}"complete"==d&&r<this.options.total&&this.element.find(".stress-step").eq(m).addClass("not-last")}},update:function(t,e){if(this.element.attr("data-stress")&&"stress"==this.element.attr("data-stress"))if("now"==t){var s=parseInt(e),a=this.element.attr("data-status"),i=this.element.attr("data-title-on-now");if(0>s||s>this.options.total)return void alert("data-now must be >= 0 and <= "+this.options.total);var l=parseInt(this.element.attr("data-now")?this.element.attr("data-now"):0);if(l>0&&(l-=1,this.element.find(".stress-step").eq(l).find('.stress-circle[data-toggle="tooltip"]').tooltip("destroy"),this.element.find(".stress-step").eq(l).find(".stress-circle").removeAttr("data-toggle data-placement data-title data-original-title data-html title")),this.element.attr("data-now",s),0==s)return void this.element.find(".stress-step").removeClass("complete fail active not-last");if(a){if(-1==o.indexOf(a))return void alert("invalid data-status");var n=s-1;this.element.find(".stress-step").eq(n).find('.stress-circle[data-toggle="tooltip"]').tooltip("destroy"),this.element.find(".stress-step").eq(n).find(".stress-circle").removeAttr("data-toggle data-placement data-title data-original-title data-html title"),this.element.find(".stress-step").removeClass("complete fail active not-last");for(var r=1;n>=r;r++)this.element.find(".stress-step").eq(r-1).addClass("complete");if(this.element.find(".stress-step").eq(n).addClass(a),i){var d=this.element.find(".stress-step").eq(n).find(".stress-circle");d.attr("data-toggle","tooltip"),d.attr("data-placement","top"),d.attr("data-title",i),d.attr("data-html","true"),d.attr("title",i),this.element.find('[data-toggle="tooltip"]').tooltip()}"complete"==a&&s<this.options.total&&this.element.find(".stress-step").eq(n).addClass("not-last")}}else if("status"==t){var s=parseInt(this.element.attr("data-now")?this.element.attr("data-now"):0),a=e;if(0>s||s>this.options.total)return void alert("data-now must be >= 0 and <= "+this.options.total);if(a){if(-1==o.indexOf(a))return void alert("invalid data-status");if(this.element.attr("data-status",a),0==s)return;var n=s-1;this.element.find(".stress-step").removeClass("complete fail active not-last");for(var r=1;n>=r;r++)this.element.find(".stress-step").eq(r-1).addClass("complete");this.element.find(".stress-step").eq(n).addClass(a),"complete"==a&&s<this.options.total&&this.element.find(".stress-step").eq(n).addClass("not-last")}}else if("title-on-now"==t){var s=parseInt(this.element.attr("data-now")?this.element.attr("data-now"):0),i=e;if(0>s||s>this.options.total)return void alert("data-now must be >= 0 and <= "+this.options.total);if(i){if(this.element.attr("data-title-on-now",i),0==s)return;var n=s-1,d=this.element.find(".stress-step").eq(n).find(".stress-circle");d.attr("data-toggle")&&d.attr("data-placement")?(d.attr("data-title",i),d.attr("data-html","true"),d.attr("title",i),this.element.find(".stress-step").eq(n).find('.stress-circle[data-toggle="tooltip"]').tooltip("fixTitle")):(d.attr("data-toggle","tooltip"),d.attr("data-placement","top"),d.attr("data-title",i),d.attr("data-html","true"),d.attr("title",i),this.element.find(".stress-step").eq(n).find('.stress-circle[data-toggle="tooltip"]').tooltip())}}},"delete":function(){this.element.removeAttr("data-stress"),this.element.removeClass("row five-cols seven-cols eight-cols nine-cols ten-cols eleven-cols"),this.element.html("")},Title:function(t,e){if(t=parseInt(t?t:0),0>t||t>this.options.total)return void alert("step must be >= 0 and <= "+this.options.total);if(0!=t&&(t-=1,e)){var s=this.element.find(".stress-step").eq(t).find(".stress-circle");s.attr("data-toggle")&&s.attr("data-placement")?(s.attr("data-title",e),s.attr("data-html","true"),s.attr("title",e),this.element.find(".stress-step").eq(t).find('.stress-circle[data-toggle="tooltip"]').tooltip("fixTitle")):(s.attr("data-toggle","tooltip"),s.attr("data-placement","top"),s.attr("data-title",e),s.attr("data-html","true"),s.attr("title",e),this.element.find(".stress-step").eq(t).find('.stress-circle[data-toggle="tooltip"]').tooltip()),t+1==this.element.attr("data-now")&&this.element.attr("data-title-on-now",e)}},deleteTitle:function(t){return t=parseInt(t?t:0),0>t||t>this.options.total?void alert("step must be >= 0 and <= "+this.options.total):void(0!=t&&(t-=1,this.element.find(".stress-step").eq(t).find('.stress-circle[data-toggle="tooltip"]').tooltip("destroy"),this.element.find(".stress-step").eq(t).find(".stress-circle").removeAttr("data-toggle data-placement data-title data-original-title data-html title"),t+1==this.element.attr("data-now")&&this.element.removeAttr("data-title-on-now")))}},t.fn[l]=function(t){var e=this.data(n);return e instanceof r?"undefined"!=typeof t&&e.init(t):(e=new r(this,t),this.data(n,e)),e},i()}(jQuery,window,document);<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import PropTypes from "prop-types";
import React from "react";
import TinyMCE from "react-tinymce";

import formatMessage from "../format-message";
import * as contentInsertion from "./contentInsertion";
import indicatorRegion from "./indicatorRegion";
import indicate from "../common/indicate";
import Bridge from "../bridge";
import CanvasContentTray, {trayProps} from './plugins/shared/CanvasContentTray'
import StatusBar from './StatusBar';

const editorWrappers = new WeakMap();

function showMenubar(el, show) {
  const $menubar = el.querySelector('.tox-menubar')
  $menubar && ($menubar.style.display = show ? '' : 'none')
  if (show) {
    focusFirstMenuButton(el)
  }
}

function focusFirstMenuButton(el) {
  const $firstMenu = el.querySelector('.tox-mbtn')
  $firstMenu && $firstMenu.focus()
}

function accessiblizeMenubar(el, editor) {
  // hide the menubar
  showMenubar(el, false)

  // when typed w/in the editor's edit area
  editor.addShortcut('Alt+F9', '', () => {
    showMenubar(el, true)
  })
  // when typed somewhere else w/in RCEWrapper
  el.addEventListener('keyup', (e) => {
    if (e.altKey && e.code === 'F9') {
      showMenubar(el, true)
    }
  })
}

export default class RCEWrapper extends React.Component {
  static getByEditor(editor) {
    return editorWrappers.get(editor);
  }

  constructor(props) {
    super(props);

    // interface consistent with editorBox
    this.get_code = this.getCode;
    this.set_code = this.setCode;
    this.insert_code = this.insertCode;

    // test override points
    this.indicator = false;

    this._elementRef = null;
  }

  // getCode and setCode naming comes from tinyMCE
  // kind of strange but want to be consistent
  getCode() {
    return this.isHidden()
      ? this.textareaValue()
      : this.mceInstance().getContent();
  }

  setCode(newContent) {
    this.mceInstance().setContent(newContent);
  }

  indicateEditor(element) {
    const editor = this.mceInstance();
    if (this.indicator) {
      this.indicator(editor, element);
    } else if (!this.isHidden()) {
      indicate(indicatorRegion(editor, element));
    }
  }

  contentInserted(element) {
    this.indicateEditor(element);
    this.checkImageLoadError(element);
  }

  checkImageLoadError(element) {
    if (!element || element.tagName !== "IMG") {
      return;
    }
    if (!element.complete) {
      element.onload = () => this.checkImageLoadError(element);
      return;
    }
    // checking naturalWidth in a future event loop run prevents a race
    // condition between the onload callback and naturalWidth being set.
    setTimeout(() => {
      if (element.naturalWidth === 0) {
        element.style.border = "1px solid #000";
        element.style.padding = "2px";
      }
    }, 0);
  }

  insertCode(code) {
    const editor = this.mceInstance();
    const element = contentInsertion.insertContent(editor, code);
    this.contentInserted(element);
  }

  insertImage(image) {
    const editor = this.mceInstance();
    const element = contentInsertion.insertImage(editor, image);
    if (element && element.complete) {
      this.contentInserted(element);
    } else if (element) {
      element.onload = () => this.contentInserted(element);
      element.onerror = () => this.checkImageLoadError(element);
    }
  }

  insertLink(link) {
    const editor = this.mceInstance();
    const element = contentInsertion.insertLink(editor, link);
    this.contentInserted(element);
  }

  existingContentToLink() {
    const editor = this.mceInstance();
    return contentInsertion.existingContentToLink(editor);
  }

  existingContentToLinkIsImg() {
    const editor = this.mceInstance();
    return contentInsertion.existingContentToLinkIsImg(editor);
  }

  mceInstance() {
    const editors = this.props.tinymce.editors || [];
    return editors.filter(ed => ed.id === this.props.textareaId)[0];
  }

  onTinyMCEInstance(command) {
    if (command == "mceRemoveEditor") {
      let editor = this.mceInstance();
      if (editor) {
        editor.execCommand("mceNewDocument");
      } // makes sure content can't persist past removal
    }
    this.props.tinymce.execCommand(command, false, this.props.textareaId);
  }

  destroy() {
    this._destroyCalled = true;
    this.onTinyMCEInstance("mceRemoveEditor");
    this.unhandleTextareaChange();
    this.props.handleUnmount && this.props.handleUnmount();
  }

  onRemove() {
    Bridge.detachEditor(this);
    this.props.onRemove && this.props.onRemove(this);
  }

  getTextarea() {
    return document.getElementById(this.props.textareaId);
  }

  textareaValue() {
    return this.getTextarea().value;
  }

  toggle = () => {
    if (this.isHidden()) {
      this.setCode(this.textareaValue());
      this.getTextarea().setAttribute('aria-hidden', true);
    } else {
      this.getTextarea().removeAttribute('aria-hidden');
    }
    this.onTinyMCEInstance("mceToggleEditor");
  }

  focus() {
    this.onTinyMCEInstance("mceFocus");
  }

  is_dirty() {
    const content = this.isHidden()
      ? this.textareaValue()
      : this.mceInstance().getContent();
    return content !== this.cleanInitialContent();
  }

  cleanInitialContent() {
    if (!this._cleanInitialContent) {
      const el = window.document.createElement("div");
      el.innerHTML = this.props.defaultContent;
      const serializer = this.mceInstance().serializer;
      this._cleanInitialContent = serializer.serialize(el, { getInner: true });
    }
    return this._cleanInitialContent;
  }

  isHidden() {
    return this.mceInstance().isHidden();
  }

  onFocus() {
    Bridge.focusEditor(this);
    this.props.onFocus && this.props.onFocus(this);
  }

  call(methodName, ...args) {
    // since exists? has a ? and cant be a regular function just return true
    // rather than calling as a fn on the editor
    if (methodName === "exists?") {
      return true;
    }
    return this[methodName](...args);
  }

  annotateEditor(_e, editor) {
    editor.rceWrapper = this;
  }

  onInit(_e, editor) {
    accessiblizeMenubar(this._elementRef, editor)
  }

  componentWillUnmount() {
    if (!this._destroyCalled) {
      this.destroy();
    }
  }

  wrapOptions(options = {}) {
    const setupCallback = options.setup;

    return {
      ...options,

      block_formats: [
        `${formatMessage('Header')}=h2`,
        `${formatMessage('Subheader')}=h3`,
        `${formatMessage('Small header')}=h4`,
        `${formatMessage('Preformatted')}=pre`,
        `${formatMessage('Paragraph')}=p`
      ].join('; '),

      setup: editor => {
        editorWrappers.set(editor, this);
        Bridge.trayProps.set(editor, this.props.trayProps)
        if (typeof setupCallback === "function") {
          setupCallback(editor);
        }
      },

      toolbar: [
        'fontsizeselect formatselect | bold italic underline forecolor backcolor superscript ' +
        'subscript | align bullist outdent indent | ' +
        'instructure_links instructure_image instructure_record | ' +
        'removeformat table instructure_equation instructure_equella'
      ],

      toolbar_drawer: 'floating'
    }
  }

  handleTextareaChange = () => {
    if (this.isHidden()) {
      this.setCode(this.textareaValue());
    }
  };

  unhandleTextareaChange() {
    if (this._textareaEl) {
      this._textareaEl.removeEventListener("change", this.handleTextareaChange);
    }
  }

  registerTextareaChange() {
    const el = this.getTextarea();
    if (this._textareaEl !== el) {
      this.unhandleTextareaChange();
      el.addEventListener("change", this.handleTextareaChange);
      this._textareaEl = el;
    }
  }

  componentDidMount() {
    this.registerTextareaChange();
  }

  componentDidUpdate() {
    this.registerTextareaChange();
  }

  render() {
    const {trayProps, ...mceProps} = this.props
    mceProps.editorOptions.statusbar = false

    return (

 this._elementRef = el}>
        <TinyMCE
          id={mceProps.textareaId}
          tinymce={mceProps.tinymce}
          className={mceProps.textareaClassName}
          onPreInit={this.annotateEditor.bind(this)}
          onInit={this.onInit.bind(this)}
          onClick={this.onFocus.bind(this)}
          onKeypress={this.onFocus.bind(this)}
          onActivate={this.onFocus.bind(this)}
          onRemove={this.onRemove.bind(this)}
          content={mceProps.defaultContent}
          config={this.wrapOptions(mceProps.editorOptions)}
        />

        <StatusBar onToggleHtml={this.toggle} />
        <CanvasContentTray bridge={Bridge} {...trayProps} />

    );
  }
}

RCEWrapper.propTypes = {
  defaultContent: PropTypes.string,
  editorOptions: PropTypes.object,
  handleUnmount: PropTypes.func,
  language: PropTypes.string,
  onFocus: PropTypes.func,
  onRemove: PropTypes.func,
  textareaClassName: PropTypes.string,
  textareaId: PropTypes.string,
  tinymce: PropTypes.object,
  trayProps
};

RCEWrapper.defaultProps = {
  trayProps: null
}
<commit_msg>Enable toolbar kb shortcut from everywhere in the rce<commit_after>/*
 * Copyright (C) 2018 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import PropTypes from "prop-types";
import React from "react";
import TinyMCE from "react-tinymce";

import formatMessage from "../format-message";
import * as contentInsertion from "./contentInsertion";
import indicatorRegion from "./indicatorRegion";
import indicate from "../common/indicate";
import Bridge from "../bridge";
import CanvasContentTray, {trayProps} from './plugins/shared/CanvasContentTray'
import StatusBar from './StatusBar';

const editorWrappers = new WeakMap();

function showMenubar(el, show) {
  const $menubar = el.querySelector('.tox-menubar')
  $menubar && ($menubar.style.display = show ? '' : 'none')
  if (show) {
    focusFirstMenuButton(el)
  }
}

function focusToolbar(el) {
  const $firstToolbarButton = el.querySelector('.tox-tbtn')
  $firstToolbarButton  && $firstToolbarButton.focus()
}

function focusFirstMenuButton(el) {
  const $firstMenu = el.querySelector('.tox-mbtn')
  $firstMenu && $firstMenu.focus()
}

function initKeyboardShortcuts(el, editor) {
  // hide the menubar
  showMenubar(el, false)

  // when typed w/in the editor's edit area
  editor.addShortcut('Alt+F9', '', () => {
    showMenubar(el, true)
  })
  // when typed somewhere else w/in RCEWrapper
  el.addEventListener('keyup', e => {
    if (e.altKey && e.code === 'F9') {
      showMenubar(el, true)
    }
  })

  // toolbar help
  el.addEventListener('keyup', e => {
    if (e.altKey && e.code === 'F10') {
      focusToolbar(el)
    }
  })
}

export default class RCEWrapper extends React.Component {
  static getByEditor(editor) {
    return editorWrappers.get(editor);
  }

  constructor(props) {
    super(props);

    // interface consistent with editorBox
    this.get_code = this.getCode;
    this.set_code = this.setCode;
    this.insert_code = this.insertCode;

    // test override points
    this.indicator = false;

    this._elementRef = null;
  }

  // getCode and setCode naming comes from tinyMCE
  // kind of strange but want to be consistent
  getCode() {
    return this.isHidden()
      ? this.textareaValue()
      : this.mceInstance().getContent();
  }

  setCode(newContent) {
    this.mceInstance().setContent(newContent);
  }

  indicateEditor(element) {
    const editor = this.mceInstance();
    if (this.indicator) {
      this.indicator(editor, element);
    } else if (!this.isHidden()) {
      indicate(indicatorRegion(editor, element));
    }
  }

  contentInserted(element) {
    this.indicateEditor(element);
    this.checkImageLoadError(element);
  }

  checkImageLoadError(element) {
    if (!element || element.tagName !== "IMG") {
      return;
    }
    if (!element.complete) {
      element.onload = () => this.checkImageLoadError(element);
      return;
    }
    // checking naturalWidth in a future event loop run prevents a race
    // condition between the onload callback and naturalWidth being set.
    setTimeout(() => {
      if (element.naturalWidth === 0) {
        element.style.border = "1px solid #000";
        element.style.padding = "2px";
      }
    }, 0);
  }

  insertCode(code) {
    const editor = this.mceInstance();
    const element = contentInsertion.insertContent(editor, code);
    this.contentInserted(element);
  }

  insertImage(image) {
    const editor = this.mceInstance();
    const element = contentInsertion.insertImage(editor, image);
    if (element && element.complete) {
      this.contentInserted(element);
    } else if (element) {
      element.onload = () => this.contentInserted(element);
      element.onerror = () => this.checkImageLoadError(element);
    }
  }

  insertLink(link) {
    const editor = this.mceInstance();
    const element = contentInsertion.insertLink(editor, link);
    this.contentInserted(element);
  }

  existingContentToLink() {
    const editor = this.mceInstance();
    return contentInsertion.existingContentToLink(editor);
  }

  existingContentToLinkIsImg() {
    const editor = this.mceInstance();
    return contentInsertion.existingContentToLinkIsImg(editor);
  }

  mceInstance() {
    const editors = this.props.tinymce.editors || [];
    return editors.filter(ed => ed.id === this.props.textareaId)[0];
  }

  onTinyMCEInstance(command) {
    if (command == "mceRemoveEditor") {
      let editor = this.mceInstance();
      if (editor) {
        editor.execCommand("mceNewDocument");
      } // makes sure content can't persist past removal
    }
    this.props.tinymce.execCommand(command, false, this.props.textareaId);
  }

  destroy() {
    this._destroyCalled = true;
    this.onTinyMCEInstance("mceRemoveEditor");
    this.unhandleTextareaChange();
    this.props.handleUnmount && this.props.handleUnmount();
  }

  onRemove() {
    Bridge.detachEditor(this);
    this.props.onRemove && this.props.onRemove(this);
  }

  getTextarea() {
    return document.getElementById(this.props.textareaId);
  }

  textareaValue() {
    return this.getTextarea().value;
  }

  toggle = () => {
    if (this.isHidden()) {
      this.setCode(this.textareaValue());
      this.getTextarea().setAttribute('aria-hidden', true);
    } else {
      this.getTextarea().removeAttribute('aria-hidden');
    }
    this.onTinyMCEInstance("mceToggleEditor");
  }

  focus() {
    this.onTinyMCEInstance("mceFocus");
  }

  is_dirty() {
    const content = this.isHidden()
      ? this.textareaValue()
      : this.mceInstance().getContent();
    return content !== this.cleanInitialContent();
  }

  cleanInitialContent() {
    if (!this._cleanInitialContent) {
      const el = window.document.createElement("div");
      el.innerHTML = this.props.defaultContent;
      const serializer = this.mceInstance().serializer;
      this._cleanInitialContent = serializer.serialize(el, { getInner: true });
    }
    return this._cleanInitialContent;
  }

  isHidden() {
    return this.mceInstance().isHidden();
  }

  onFocus() {
    Bridge.focusEditor(this);
    this.props.onFocus && this.props.onFocus(this);
  }

  call(methodName, ...args) {
    // since exists? has a ? and cant be a regular function just return true
    // rather than calling as a fn on the editor
    if (methodName === "exists?") {
      return true;
    }
    return this[methodName](...args);
  }

  annotateEditor(_e, editor) {
    editor.rceWrapper = this;
  }

  onInit(_e, editor) {
    initKeyboardShortcuts(this._elementRef, editor)
  }

  componentWillUnmount() {
    if (!this._destroyCalled) {
      this.destroy();
    }
  }

  wrapOptions(options = {}) {
    const setupCallback = options.setup;

    return {
      ...options,

      block_formats: [
        `${formatMessage('Header')}=h2`,
        `${formatMessage('Subheader')}=h3`,
        `${formatMessage('Small header')}=h4`,
        `${formatMessage('Preformatted')}=pre`,
        `${formatMessage('Paragraph')}=p`
      ].join('; '),

      setup: editor => {
        editorWrappers.set(editor, this);
        Bridge.trayProps.set(editor, this.props.trayProps)
        if (typeof setupCallback === "function") {
          setupCallback(editor);
        }
      },

      toolbar: [
        'fontsizeselect formatselect | bold italic underline forecolor backcolor superscript ' +
        'subscript | align bullist outdent indent | ' +
        'instructure_links instructure_image instructure_record | ' +
        'removeformat table instructure_equation instructure_equella'
      ],

      toolbar_drawer: 'floating'
    }
  }

  handleTextareaChange = () => {
    if (this.isHidden()) {
      this.setCode(this.textareaValue());
    }
  };

  unhandleTextareaChange() {
    if (this._textareaEl) {
      this._textareaEl.removeEventListener("change", this.handleTextareaChange);
    }
  }

  registerTextareaChange() {
    const el = this.getTextarea();
    if (this._textareaEl !== el) {
      this.unhandleTextareaChange();
      el.addEventListener("change", this.handleTextareaChange);
      this._textareaEl = el;
    }
  }

  componentDidMount() {
    this.registerTextareaChange();
  }

  componentDidUpdate() {
    this.registerTextareaChange();
  }

  render() {
    const {trayProps, ...mceProps} = this.props
    mceProps.editorOptions.statusbar = false

    return (

 this._elementRef = el}>
        <TinyMCE
          id={mceProps.textareaId}
          tinymce={mceProps.tinymce}
          className={mceProps.textareaClassName}
          onPreInit={this.annotateEditor.bind(this)}
          onInit={this.onInit.bind(this)}
          onClick={this.onFocus.bind(this)}
          onKeypress={this.onFocus.bind(this)}
          onActivate={this.onFocus.bind(this)}
          onRemove={this.onRemove.bind(this)}
          content={mceProps.defaultContent}
          config={this.wrapOptions(mceProps.editorOptions)}
        />

        <StatusBar onToggleHtml={this.toggle} />
        <CanvasContentTray bridge={Bridge} {...trayProps} />

    );
  }
}

RCEWrapper.propTypes = {
  defaultContent: PropTypes.string,
  editorOptions: PropTypes.object,
  handleUnmount: PropTypes.func,
  language: PropTypes.string,
  onFocus: PropTypes.func,
  onRemove: PropTypes.func,
  textareaClassName: PropTypes.string,
  textareaId: PropTypes.string,
  tinymce: PropTypes.object,
  trayProps
};

RCEWrapper.defaultProps = {
  trayProps: null
}
<|endoftext|>


<commit_before>// @flow
import React from "react";

import { richestMimetype, transforms, displayOrder } from "@nteract/transforms";

type Props = {
  expanded: boolean,
  displayOrder: Array<string>,
  transforms: Object,
  bundle: Object,
  metadata: Object,
  theme: string,
  models?: Object
};

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: false };
  }
  componentDidCatch(error, info) {
    this.setState({ error, info });
  }
  render() {
    if (this.state.error) {
      return (

 Error: {this.state.error.toString()}

 {this.state.info.toString()}

 {this.state.info.componentStack.toString()}

      );
    }
    return this.props.children;
  }
}

export default class RichestMime extends React.Component<Props> {
  static defaultProps = {
    transforms,
    displayOrder,
    theme: "light",
    metadata: {},
    bundle: {},
    models: {}
  };

  shouldComponentUpdate(nextProps: Props): boolean {
    // eslint-disable-line class-methods-use-this
    if (
      nextProps &&
      nextProps.theme &&
      this.props &&
      nextProps.theme !== this.props.theme
    ) {
      return true;
    }
    // return false;
    return true;
  }

  render(): ?React$Element<any> | null {
    const mimetype = richestMimetype(
      this.props.bundle,
      this.props.displayOrder,
      this.props.transforms
    );

    if (!mimetype) {
      // If no mimetype is supported, don't return a component
      return null;
    }

    const Transform = this.props.transforms[mimetype];
    const data = this.props.bundle[mimetype];
    const metadata = this.props.metadata[mimetype];
    return (
      <ErrorBoundary>
        <Transform
          expanded={this.props.expanded}
          data={data}
          metadata={metadata}
          theme={this.props.theme}
          models={this.props.models}
        />
      </ErrorBoundary>
    );
  }
}
<commit_msg>add type information to ErrorBoundary<commit_after>// @flow
import React from "react";

import { richestMimetype, transforms, displayOrder } from "@nteract/transforms";

type Props = {
  expanded: boolean,
  displayOrder: Array<string>,
  transforms: Object,
  bundle: Object,
  metadata: Object,
  theme: string,
  models?: Object
};

class ErrorBoundary extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { error: false };
  }
  componentDidCatch(error: Error, info: React.ErrorInfo) {
    this.setState({ error, info });
  }
  render() {
    if (this.state.error) {
      return (

 Error: {this.state.error.toString()}

 {this.state.info.toString()}

 {this.state.info.componentStack.toString()}

      );
    }
    return this.props.children;
  }
}

export default class RichestMime extends React.Component<Props> {
  static defaultProps = {
    transforms,
    displayOrder,
    theme: "light",
    metadata: {},
    bundle: {},
    models: {}
  };

  shouldComponentUpdate(nextProps: Props): boolean {
    // eslint-disable-line class-methods-use-this
    if (
      nextProps &&
      nextProps.theme &&
      this.props &&
      nextProps.theme !== this.props.theme
    ) {
      return true;
    }
    // return false;
    return true;
  }

  render(): ?React$Element<any> | null {
    const mimetype = richestMimetype(
      this.props.bundle,
      this.props.displayOrder,
      this.props.transforms
    );

    if (!mimetype) {
      // If no mimetype is supported, don't return a component
      return null;
    }

    const Transform = this.props.transforms[mimetype];
    const data = this.props.bundle[mimetype];
    const metadata = this.props.metadata[mimetype];
    return (
      <ErrorBoundary>
        <Transform
          expanded={this.props.expanded}
          data={data}
          metadata={metadata}
          theme={this.props.theme}
          models={this.props.models}
        />
      </ErrorBoundary>
    );
  }
}
<|endoftext|>


<commit_before>import createSchema from 'part:@sanity/base/schema-creator'
import {SlateInput} from '@sanity/form-builder'
import blockArray from './block-array'
import moment from 'moment'

// Example of a custom slugify function that
// makes a slug-string and prefixes it with something from the
// schema and then calls the default slugify function.
function slugifyWithPrefix(prefix) {
  return function (type, slug, slugify) {
    let newSlug = slug
    if (slug.substring(0, prefix.length) === `${prefix}`) {
      newSlug = slug.substring(prefix.length, slug.length)
    }
    return slugify(type, `${prefix}-${newSlug}`)
      .substring(0, type.options.maxLength)
  }
}

const headerMarks = [
  'strong',
  'em'
]

const defaultMarks = [
  'strong',
  'em',
  'underline',
  'overline',
  'line-through',
  'code'
]

export default createSchema({
  name: 'example-blog',
  types: [
    ...blockArray,
    {
      name: 'blogpost',
      type: 'object',
      title: 'Blogpost',
      preview: {
        select: {
          title: 'title',
          createdAt: '_createdAt',
          lead: 'lead',
          imageUrl: 'mainImage.asset.url',
          author: 'authorRef.name'
        },
        prepare(value) {
          const timeSince = moment(value.createdAt).fromNow()
          return Object.assign({}, value, {
            subtitle: value.author ? `By ${value.author}, ${timeSince}` : timeSince,
            description: value.lead
          })
        }
      },
      fields: [
        {
          name: 'selectMultipleStrings',
          title: 'Select multiple strings',
          type: 'array',
          options: {
            direction: 'vertical',
            list: [
              {title: 'Red', value: 'red'},
              {title: 'Green', value: 'green'},
              {title: 'Blue', value: 'blue'},
              {title: 'Black', value: 'black'},
            ]
          },
          of: [{
            type: 'string'
          }]
        },
        {
          name: 'title',
          title: 'Title',
          type: 'string',
          required: true
        },
        {
          name: 'publishAt',
          title: 'Publish at',
          type: 'date',
          description: 'Blogpost goes live at this date/time',
          options: {
            dateFormat: 'YYYY-MM-DD',
            timeFormat: 'HH:mm',
            timeStep: 60,
            calendarTodayLabel: 'Today',
            inputUtc: false,
            inputDate: true,
            inputTime: true
          }
        },
        {
          name: 'rating',
          title: 'Rating',
          type: 'number',
          options: {
            range: {min: 0, max: 10, step: 0.2}
          },
          required: true
        },
        {
          name: 'mainImage',
          title: 'Main image',
          type: 'image',
          options: {
            hotspot: true
          },
          fields: [
            {
              name: 'caption',
              type: 'string',
              title: 'Caption'
            }
          ]
        },
        {
          name: 'pngImage',
          title: 'PNG image',
          type: 'image',
          options: {
            hotspot: true,
            accept: 'image/png'
          }
        },
        {
          name: 'slug',
          title: 'Slug',
          description: 'The unique identifier for the blogpost in links and urls',
          type: 'slug',
          required: true,
          options: {
            source: 'title',
            maxLength: 64,
            slugifyFn: slugifyWithPrefix('creepy')
          }
        },
        {
          name: 'tags',
          title: 'tags',
          type: 'array',
          options: {
            layout: 'tags'
          },
          of: [
            {type: 'string'}
          ]
        },
        {
          name: 'arrayOfStrings',
          title: 'Array of strings',
          type: 'array',
          of: [
            {type: 'string'}
          ]
        },

        {
          name: 'priority',
          title: 'Priority',
          type: 'number'
        },
        {
          name: 'checked',
          title: 'Checked',
          type: 'boolean'
        },
        {
          name: 'body',
          type: 'array',
          title: 'Blocks',
          inputComponent: SlateInput,
          of: [
            {
              title: 'Normal',
              type: 'block',
              default: true,
              marks: defaultMarks
            },
            {
              title: 'Heading 1',
              type: 'block',
              style: 'h1',
              marks: headerMarks
            },
            {
              title: 'Heading 2',
              type: 'block',
              style: 'h2',
              marks: headerMarks
            },
            {
              title: 'Heading 3',
              type: 'block',
              style: 'h3',
              marks: headerMarks
            },
            {
              title: 'Bullet list',
              type: 'block',
              listItem: 'bullet',
              marks: defaultMarks
            },
            {
              title: 'Numbered list',
              type: 'block',
              listItem: 'number',
              marks: defaultMarks
            },
            {
              title: 'Author',
              type: 'author',
            },
            {
              title: 'Inline string',
              type: 'string',
              options: {
                inline: true
              }
            }
          ]
        },
        {
          name: 'imageGallery',
          title: 'Image gallery',
          type: 'array',
          options: {
            layout: 'grid',
            sortable: true
          },
          of: [
            {
              title: 'Image',
              type: 'image',
              preview: {
                select: {
                  imageUrl: 'asset.url',
                  title: 'caption'
                }
              },
              fields: [
                {
                  name: 'caption',
                  type: 'string',
                  title: 'Caption',
                  options: {
                    isHighlighted: true
                  }
                }
              ]
            }
          ]
        },
        {
          name: 'select',
          title: 'Select string',
          type: 'string',
          options: {
            list: [
              {
                title: 'One (1)',
                value: 'one'
              },
              {
                title: 'Two (2)',
                value: 'two'
              },
              {
                title: 'Three (3)',
                value: 'three'
              }
            ]
          }
        },
        {
          name: 'selectObjectOfString',
          title: 'Select string in object',
          description: '',
          type: 'string',
          options: {
            list: ['one', 'two', 'three']
          }
        },
        {
          name: 'radioSelect',
          title: 'Select (layout: radio)',
          type: 'string',
          options: {
            layout: 'radio',
            list: [
              {
                title: 'One (1)',
                value: 'one'
              },
              {
                title: 'Two (2)',
                value: 'two'
              },
              {
                title: 'Three (3)',
                value: 'three'
              }
            ]
          }
        },
        {
          name: 'lead',
          title: 'Lead',
          type: 'text',
          format: 'html',
          required: true
        },
        {
          name: 'email',
          title: 'Email',
          type: 'email'
        },
        {
          name: 'location',
          title: 'Location',
          type: 'geopoint'
        },
        {
          name: 'content',
          title: 'Content tags',
          type: 'array',
          of: [
            {
              title: 'String',
              type: 'string'
            }
          ]
        },
        {
          name: 'firstAuthor',
          title: 'Author',
          type: 'reference',
          to: {type: 'author'},
          required: true
        },
        {
          name: 'coauthors',
          title: 'Co authors',
          type: 'array',
          of: [{
            type: 'reference',
            to: {type: 'author'}
          }],
          required: true
        },
        {
          name: 'extraAuthors',
          title: 'Additional authors',
          description: 'Note: This is an inline array of authors',
          type: 'array',
          of: [{type: 'author'}],
          required: true
        }
      ]
    },
    {
      name: 'author',
      type: 'object',
      title: 'Author',
      preview: {
        select: {title: 'name', imageUrl: 'image.asset.url'}
      },
      fields: [
        {
          name: 'name',
          title: 'Name',
          type: 'string'
        },
        {
          name: 'image',
          title: 'Image',
          type: 'image'
        },
        {
          name: 'awards',
          title: 'Awards',
          type: 'array',
          of: [
            {
              type: 'string'
            }
          ]
        },
        {
          name: 'relatedAuthors',
          title: 'Related authors',
          type: 'array',
          of: [
            {
              type: 'reference',
              to: {type: 'author'}
            }
          ]
        }
      ]
    }
  ]
})
<commit_msg>[example-studio] Reorder blogpost schema<commit_after>import createSchema from 'part:@sanity/base/schema-creator'
import {SlateInput} from '@sanity/form-builder'
import blockArray from './block-array'
import moment from 'moment'

// Example of a custom slugify function that
// makes a slug-string and prefixes it with something from the
// schema and then calls the default slugify function.
function slugifyWithPrefix(prefix) {
  return function (type, slug, slugify) {
    let newSlug = slug
    if (slug.substring(0, prefix.length) === `${prefix}`) {
      newSlug = slug.substring(prefix.length, slug.length)
    }
    return slugify(type, `${prefix}-${newSlug}`)
      .substring(0, type.options.maxLength)
  }
}

const headerMarks = [
  'strong',
  'em'
]

const defaultMarks = [
  'strong',
  'em',
  'underline',
  'overline',
  'line-through',
  'code'
]

export default createSchema({
  name: 'example-blog',
  types: [
    ...blockArray,
    {
      name: 'blogpost',
      type: 'object',
      title: 'Blogpost',
      preview: {
        select: {
          title: 'title',
          createdAt: '_createdAt',
          lead: 'lead',
          imageUrl: 'mainImage.asset.url',
          author: 'authorRef.name'
        },
        prepare(value) {
          const timeSince = moment(value.createdAt).fromNow()
          return Object.assign({}, value, {
            subtitle: value.author ? `By ${value.author}, ${timeSince}` : timeSince,
            description: value.lead
          })
        }
      },
      fields: [
        {
          name: 'title',
          title: 'Title',
          type: 'string',
          required: true
        },
        {
          name: 'publishAt',
          title: 'Publish at',
          type: 'date',
          description: 'Blogpost goes live at this date/time',
          options: {
            dateFormat: 'YYYY-MM-DD',
            timeFormat: 'HH:mm',
            timeStep: 60,
            calendarTodayLabel: 'Today',
            inputUtc: false,
            inputDate: true,
            inputTime: true
          }
        },
        {
          name: 'mainImage',
          title: 'Main image',
          type: 'image',
          options: {
            hotspot: true
          },
          fields: [
            {
              name: 'caption',
              type: 'string',
              title: 'Caption'
            }
          ]
        },
        {
          name: 'rating',
          title: 'Rating',
          type: 'number',
          options: {
            range: {min: 0, max: 10, step: 0.2}
          },
          required: true
        },
        {
          name: 'selectMultipleStrings',
          title: 'Select multiple strings',
          type: 'array',
          options: {
            direction: 'vertical',
            list: [
              {title: 'Red', value: 'red'},
              {title: 'Green', value: 'green'},
              {title: 'Blue', value: 'blue'},
              {title: 'Black', value: 'black'},
            ]
          },
          of: [{
            type: 'string'
          }]
        },
        {
          name: 'pngImage',
          title: 'PNG image',
          type: 'image',
          options: {
            hotspot: true,
            accept: 'image/png'
          }
        },
        {
          name: 'slug',
          title: 'Slug',
          description: 'The unique identifier for the blogpost in links and urls',
          type: 'slug',
          required: true,
          options: {
            source: 'title',
            maxLength: 64,
            slugifyFn: slugifyWithPrefix('creepy')
          }
        },
        {
          name: 'tags',
          title: 'tags',
          type: 'array',
          options: {
            layout: 'tags'
          },
          of: [
            {type: 'string'}
          ]
        },
        {
          name: 'arrayOfStrings',
          title: 'Array of strings',
          type: 'array',
          of: [
            {type: 'string'}
          ]
        },

        {
          name: 'priority',
          title: 'Priority',
          type: 'number'
        },
        {
          name: 'checked',
          title: 'Checked',
          type: 'boolean'
        },
        {
          name: 'body',
          type: 'array',
          title: 'Blocks',
          inputComponent: SlateInput,
          of: [
            {
              title: 'Normal',
              type: 'block',
              default: true,
              marks: defaultMarks
            },
            {
              title: 'Heading 1',
              type: 'block',
              style: 'h1',
              marks: headerMarks
            },
            {
              title: 'Heading 2',
              type: 'block',
              style: 'h2',
              marks: headerMarks
            },
            {
              title: 'Heading 3',
              type: 'block',
              style: 'h3',
              marks: headerMarks
            },
            {
              title: 'Bullet list',
              type: 'block',
              listItem: 'bullet',
              marks: defaultMarks
            },
            {
              title: 'Numbered list',
              type: 'block',
              listItem: 'number',
              marks: defaultMarks
            },
            {
              title: 'Author',
              type: 'author',
            },
            {
              title: 'Inline string',
              type: 'string',
              options: {
                inline: true
              }
            }
          ]
        },
        {
          name: 'imageGallery',
          title: 'Image gallery',
          type: 'array',
          options: {
            layout: 'grid',
            sortable: true
          },
          of: [
            {
              title: 'Image',
              type: 'image',
              preview: {
                select: {
                  imageUrl: 'asset.url',
                  title: 'caption'
                }
              },
              fields: [
                {
                  name: 'caption',
                  type: 'string',
                  title: 'Caption',
                  options: {
                    isHighlighted: true
                  }
                }
              ]
            }
          ]
        },
        {
          name: 'select',
          title: 'Select string',
          type: 'string',
          options: {
            list: [
              {
                title: 'One (1)',
                value: 'one'
              },
              {
                title: 'Two (2)',
                value: 'two'
              },
              {
                title: 'Three (3)',
                value: 'three'
              }
            ]
          }
        },
        {
          name: 'selectObjectOfString',
          title: 'Select string in object',
          description: '',
          type: 'string',
          options: {
            list: ['one', 'two', 'three']
          }
        },
        {
          name: 'radioSelect',
          title: 'Select (layout: radio)',
          type: 'string',
          options: {
            layout: 'radio',
            list: [
              {
                title: 'One (1)',
                value: 'one'
              },
              {
                title: 'Two (2)',
                value: 'two'
              },
              {
                title: 'Three (3)',
                value: 'three'
              }
            ]
          }
        },
        {
          name: 'lead',
          title: 'Lead',
          type: 'text',
          format: 'html',
          required: true
        },
        {
          name: 'email',
          title: 'Email',
          type: 'email'
        },
        {
          name: 'location',
          title: 'Location',
          type: 'geopoint'
        },
        {
          name: 'content',
          title: 'Content tags',
          type: 'array',
          of: [
            {
              title: 'String',
              type: 'string'
            }
          ]
        },
        {
          name: 'firstAuthor',
          title: 'Author',
          type: 'reference',
          to: {type: 'author'},
          required: true
        },
        {
          name: 'coauthors',
          title: 'Co authors',
          type: 'array',
          of: [{
            type: 'reference',
            to: {type: 'author'}
          }],
          required: true
        },
        {
          name: 'extraAuthors',
          title: 'Additional authors',
          description: 'Note: This is an inline array of authors',
          type: 'array',
          of: [{type: 'author'}],
          required: true
        }
      ]
    },
    {
      name: 'author',
      type: 'object',
      title: 'Author',
      preview: {
        select: {title: 'name', imageUrl: 'image.asset.url'}
      },
      fields: [
        {
          name: 'name',
          title: 'Name',
          type: 'string'
        },
        {
          name: 'image',
          title: 'Image',
          type: 'image'
        },
        {
          name: 'awards',
          title: 'Awards',
          type: 'array',
          of: [
            {
              type: 'string'
            }
          ]
        },
        {
          name: 'relatedAuthors',
          title: 'Related authors',
          type: 'array',
          of: [
            {
              type: 'reference',
              to: {type: 'author'}
            }
          ]
        }
      ]
    }
  ]
})
<|endoftext|>


<commit_before>const { BranchUpdate, owningDataSource } = require('./branch-update');
const DataSource = require('../schema/data-source');
const log = require('@cardstack/logger')('cardstack/indexers');
const { flatten } = require('lodash');

log.registerFormatter('t', require('../table-log-formatter'));

module.exports = class RunningIndexers {
  constructor(seedSchema, client) {
    this.seedSchema = seedSchema;
    this.client = client;
    this.dataSources = {};
    this.branches = {};
  }

  destroy() {
    for (let branchUpdate of Object.values(this.branches)) {
      branchUpdate.destroy();
    }
    for (let dataSource of Object.values(this.dataSources)) {
      dataSource.teardown();
    }
  }

  _findIndexer(dataSource) {
    if (dataSource.indexer) {
      owningDataSource.set(dataSource.indexer, dataSource);
      return dataSource.indexer;
    }
  }

  async _loadSchemaModels() {
    let newIndexers = [...this.seedSchema.dataSources.values()]
        .map(this._findIndexer.bind(this))
        .filter(Boolean);
    while (newIndexers.length > 0) {
      let newSchemaModels = await this._activateIndexers(newIndexers);
      newIndexers = newSchemaModels.map(model => {
        log.debug("new schema model %s %s", model.type, model.id);
        if (model.type === 'data-sources' && !this.dataSources[model.id]) {
          log.debug("Discovered data source %s", model.id);
          let dataSource = new DataSource(model, this.seedSchema.plugins);
          this.dataSources[model.id] = dataSource;
          return this._findIndexer(dataSource);
        }
      }).filter(Boolean);
    }
  }

  async _activateIndexers(indexers) {
    log.debug("=Activating indexers=\n%t", () => indexers.map(i => {
      let source = owningDataSource.get(i);
      return [ source.sourceType, source.id ];
    }));
    let newSchemaModels = [];
    await Promise.all(indexers.map(async indexer => {
      for (let branch of await indexer.branches()) {
        if (!this.branches[branch]) {
          log.debug("Discovered branch %s", branch);
          this.branches[branch] = new BranchUpdate(branch, this.seedSchema, this.client);
        }
        newSchemaModels.push(await this.branches[branch].addIndexer(indexer));
      }
    }));
    return flatten(newSchemaModels);
  }

  async update(realTime, hints) {
    await this._loadSchemaModels();
    await Promise.all(Object.values(this.branches).map(branch => branch.update(realTime, hints)));
    return await this._schemas();
  }

  async schemas() {
    await this._loadSchemaModels();
    return await this._schemas();
  }

  async _schemas() {
    let schemas = Object.create(null);
    for (let [branch, branchUpdate] of Object.entries(this.branches)) {
      schemas[branch] = await branchUpdate.schema();
    }
    return schemas;
  }
};
<commit_msg>don't double-index seed data sources<commit_after>const { BranchUpdate, owningDataSource } = require('./branch-update');
const DataSource = require('../schema/data-source');
const log = require('@cardstack/logger')('cardstack/indexers');
const { flatten } = require('lodash');

log.registerFormatter('t', require('../table-log-formatter'));

module.exports = class RunningIndexers {
  constructor(seedSchema, client) {
    this.seedSchema = seedSchema;
    this.client = client;
    this.ownedDataSources = {};
    this.seenDataSources = {};
    this.branches = {};
  }

  destroy() {
    for (let branchUpdate of Object.values(this.branches)) {
      branchUpdate.destroy();
    }
    for (let dataSource of Object.values(this.ownedDataSources)) {
      dataSource.teardown();
    }
  }

  _findIndexer(dataSource) {
    this.seenDataSources[dataSource.id] = true;
    if (dataSource.indexer) {
      owningDataSource.set(dataSource.indexer, dataSource);
      return dataSource.indexer;
    }
  }

  async _loadSchemaModels() {
    let newIndexers = [...this.seedSchema.dataSources.values()]
        .map(this._findIndexer.bind(this))
        .filter(Boolean);
    while (newIndexers.length > 0) {
      let newSchemaModels = await this._activateIndexers(newIndexers);
      newIndexers = newSchemaModels.map(model => {
        log.debug("new schema model %s %s", model.type, model.id);
        if (model.type === 'data-sources' && !this.seenDataSources[model.id]) {
          log.debug("Discovered data source %s", model.id);
          let dataSource = new DataSource(model, this.seedSchema.plugins);
          this.ownedDataSources[model.id] = dataSource;
          return this._findIndexer(dataSource);
        }
      }).filter(Boolean);
    }
  }

  async _activateIndexers(indexers) {
    log.debug("=Activating indexers=\n%t", () => indexers.map(i => {
      let source = owningDataSource.get(i);
      return [ source.sourceType, source.id ];
    }));
    let newSchemaModels = [];
    await Promise.all(indexers.map(async indexer => {
      for (let branch of await indexer.branches()) {
        if (!this.branches[branch]) {
          log.debug("Discovered branch %s", branch);
          this.branches[branch] = new BranchUpdate(branch, this.seedSchema, this.client);
        }
        newSchemaModels.push(await this.branches[branch].addIndexer(indexer));
      }
    }));
    return flatten(newSchemaModels);
  }

  async update(realTime, hints) {
    await this._loadSchemaModels();
    await Promise.all(Object.values(this.branches).map(branch => branch.update(realTime, hints)));
    return await this._schemas();
  }

  async schemas() {
    await this._loadSchemaModels();
    return await this._schemas();
  }

  async _schemas() {
    let schemas = Object.create(null);
    for (let [branch, branchUpdate] of Object.entries(this.branches)) {
      schemas[branch] = await branchUpdate.schema();
    }
    return schemas;
  }
};
<|endoftext|>


<commit_before>function CartoDBLayerGroupViewBase (layerGroupModel, nativeMap) {
  this.interaction = [];
  this.nativeMap = nativeMap;

  layerGroupModel.on('change:urls', this._reload, this);
  layerGroupModel.onLayerVisibilityChanged(this._reload.bind(this));
  /** dirty raster hack **/
  var layers = layerGroupModel._layersCollection.models;
  for(ele in layers){
    if ((layers[ele].attributes.layer_name && layers[ele].attributes.layer_name.indexOf('_raster') > 0) ||
      (!!layersData[layers[ele].attributes.order].options
        && !!layersData[layers[ele].attributes.order].options.table_name
        && !!layersData[layers[ele].attributes.order].options.table_name.indexOf('_raster') != -1))
      layers[ele].attributes.type = 'raster_tileo';
  }

  this._reload();
}

CartoDBLayerGroupViewBase.prototype = {
  _reload: function () {
    throw new Error('_reload must be implemented');
  },

  _reloadInteraction: function () {
    this._clearInteraction();

    this.model.forEachGroupedLayer(function (layerModel, layerIndex) {
      if (layerModel.isVisible()) {
        this._enableInteraction(layerIndex);
      }
    }, this);
  },

  _clearInteraction: function () {
    for (var layerIndex in this.interaction) {
      if (this.interaction.hasOwnProperty(layerIndex) &&
        this.interaction[layerIndex]) {
        this.interaction[layerIndex].remove();
        this.interaction[layerIndex] = null;
      }
    }
  },

  _enableInteraction: function (layerIndexInLayerGroup) {
    var self = this;
    var tilejson = this._generateTileJSON(layerIndexInLayerGroup);
    if (tilejson) {
      var previousLayerInteraction = this.interaction[layerIndexInLayerGroup];
      if (previousLayerInteraction) {
        previousLayerInteraction.remove();
      }

      this.interaction[layerIndexInLayerGroup] = this.interactionClass()
        .map(this.nativeMap)
        .tilejson(tilejson)
        .on('on', function (o) {
          if (self._interactionDisabled) return;
          o.layer = layerIndexInLayerGroup;
          self._manageOnEvents(self.nativeMap, o);
        })
        .on('off', function (o) {
          if (self._interactionDisabled) return;
          o = o || {};
          o.layer = layerIndexInLayerGroup;
          self._manageOffEvents(self.nativeMap, o);
        });
    }
  },

  _generateTileJSON: function (layerIndexInLayerGroup) {
    if (this.model.hasURLs()) {
      return {
        tilejson: '2.0.0',
        scheme: 'xyz',
        grids: this.model.getGridURLTemplatesWithSubdomains(layerIndexInLayerGroup),
        tiles: this.model.getTileURLTemplatesWithSubdomains(),
        formatter: function (options, data) { return data; }
      };
    }
  },

  error: function (e) {},

  tilesOk: function () {}
};

module.exports = CartoDBLayerGroupViewBase;
<commit_msg>fix error that prevented vector layers be treated as rasters<commit_after>function CartoDBLayerGroupViewBase (layerGroupModel, nativeMap) {
  this.interaction = [];
  this.nativeMap = nativeMap;

  layerGroupModel.on('change:urls', this._reload, this);
  layerGroupModel.onLayerVisibilityChanged(this._reload.bind(this));
  /** dirty raster hack **/
  var layers = layerGroupModel._layersCollection.models;
  for(ele in layers){
    if ((layers[ele].attributes.layer_name && layers[ele].attributes.layer_name.indexOf('_raster') > 0) ||
      (!!layersData[layers[ele].attributes.order].options &&
       !!layersData[layers[ele].attributes.order].options.table_name &&
         layersData[layers[ele].attributes.order].options.table_name.indexOf('_raster') != -1))
      layers[ele].attributes.type = 'raster_tileo';
  }

  this._reload();
}

CartoDBLayerGroupViewBase.prototype = {
  _reload: function () {
    throw new Error('_reload must be implemented');
  },

  _reloadInteraction: function () {
    this._clearInteraction();

    this.model.forEachGroupedLayer(function (layerModel, layerIndex) {
      if (layerModel.isVisible()) {
        this._enableInteraction(layerIndex);
      }
    }, this);
  },

  _clearInteraction: function () {
    for (var layerIndex in this.interaction) {
      if (this.interaction.hasOwnProperty(layerIndex) &&
        this.interaction[layerIndex]) {
        this.interaction[layerIndex].remove();
        this.interaction[layerIndex] = null;
      }
    }
  },

  _enableInteraction: function (layerIndexInLayerGroup) {
    var self = this;
    var tilejson = this._generateTileJSON(layerIndexInLayerGroup);
    if (tilejson) {
      var previousLayerInteraction = this.interaction[layerIndexInLayerGroup];
      if (previousLayerInteraction) {
        previousLayerInteraction.remove();
      }

      this.interaction[layerIndexInLayerGroup] = this.interactionClass()
        .map(this.nativeMap)
        .tilejson(tilejson)
        .on('on', function (o) {
          if (self._interactionDisabled) return;
          o.layer = layerIndexInLayerGroup;
          self._manageOnEvents(self.nativeMap, o);
        })
        .on('off', function (o) {
          if (self._interactionDisabled) return;
          o = o || {};
          o.layer = layerIndexInLayerGroup;
          self._manageOffEvents(self.nativeMap, o);
        });
    }
  },

  _generateTileJSON: function (layerIndexInLayerGroup) {
    if (this.model.hasURLs()) {
      return {
        tilejson: '2.0.0',
        scheme: 'xyz',
        grids: this.model.getGridURLTemplatesWithSubdomains(layerIndexInLayerGroup),
        tiles: this.model.getTileURLTemplatesWithSubdomains(),
        formatter: function (options, data) { return data; }
      };
    }
  },

  error: function (e) {},

  tilesOk: function () {}
};

module.exports = CartoDBLayerGroupViewBase;
<|endoftext|>


<commit_before>"use strict";

var Root = require('../root');
var Json = require('./json');

function one(type, enter, exit) {
	var value = null;
	enter = enter || function () {};
	exit = exit || function () {};

	return one.create(function () {
		if (arguments.length === 0) {
			return value;
		} else {
			var prev = value;
			var next = arguments[0];

			if (next !== null && type && !next.is(type)) {
				throw new Error('Type mismatch');
			}

			value = next;

			if (prev !== next) {
				if (prev !== null) {
					exit.call(this, prev);
				}

				if (next !== null) {
					enter.call(this, next);
				}
			}

			return this;
		}
	});
}

one.create = function (left) {
	left.is = function (right) {
		return right === one;
	};

	left.bind = function () {
		return one.create(Function.prototype.bind.apply(left, arguments));
	};

	left.fixup = function (entity) {
		left(entity);
	};

	left.revert = function (entity) {
		left(null);
	};

	return left;
};

function many(type, enter, exit) {
	var value = [];
	enter = enter || function () {};
	exit = exit || function () {};

	return many.create(function () {
		if (arguments.length === 0) {
			return value.slice();
		} else {
			var prev = value;
			var next = arguments[0];

    		if (!isArray(next) || type && next.some(function (entity) { return !entity.is(type); })) {
    			throw new Error('Type mismatch');
    		}

			value = next;

    		prev.filter(function (entity) {
    			return next.indexOf(entity) === -1;
    		}).forEach(exit, this);

    		next.filter(function (entity) {
    			return prev.indexOf(entity) === -1;
    		}).forEach(enter, this);

    		return this;
		}
	}, type);
}

many.create = function (left) {
	left.is = function (right) {
		return right === many;
	};

	left.bind = function () {
		return many.create(Function.prototype.bind.apply(left, arguments));
	};

	left.add = function (entity) {
		left(left().filter(function (any) {
			return any !== entity;
		}).concat(entity));
	};

	left.remove = function (entity) {
		left(left().filter(function (any) {
			return any !== entity;
		}));
	};

	left.fixup = function (entity) {
		left.add(entity);
	};

	left.revert = function (entity) {
		left.remove(entity);
	};

	return left;
};

function isArray(value) {
	return Object.prototype.toString.call(value) === '[object Array]';
}

function add(array, value) {
	var tmp = many();
	tmp(array);
	tmp.add(value);
	return tmp();
}

function remove(array, value) {
	var tmp = many();
	tmp(array);
	tmp.remove(value);
	return tmp();
}

function reset(ref, value) {
	ref.revert(value);
}

function fixup(ref, value) {
	ref.fixup(value);
}

module.exports = Json(Root.create()).
    override('field', function (base) {
        return function (name, init) {
            this.fields(name);

            return base(name, init);
        };
    }).
    def('hasOne', function (type, name, inverse) {
    	return this.lazy(name, function () {
    		var value;

    		if (inverse) {
	    		return one(type, function (enter) {
	    			fixup(enter.ref(inverse), this);
	    		}, function (exit) {
    				reset(exit.ref(inverse), this);
	    		});
    		} else {
	    		return one(type);
    		}
    	});
    }).
    def('hasMany', function (type, name, inverse) {
    	return this.lazy(name, function () {
    		var value;

    		if (inverse) {
    			return many(type, function (enter) {
    				fixup(enter.ref(inverse), this);
    			}, function (exit) {
    				reset(exit.ref(inverse), this);
    			});
    		} else {
    			return many(type);
    		}
    	});
    });

<commit_msg>Refactored everything into magic closures<commit_after>"use strict";

var Root = require('../root');
var Json = require('./json');

function isArray(value) {
	return Object.prototype.toString.call(value) === '[object Array]';
}

function one(type, inverse) {
	var value = null;

	return one.create(function () {
		if (arguments.length === 0) {
			return value;
		} else {
			var prev = value;
			var next = arguments[0];

			if (next !== null && type && !next.is(type)) {
				throw new Error('Type mismatch');
			}

			value = next;

			if (inverse && prev !== next) {
				if (prev !== null) {
					prev.ref(inverse).revert(this);
				}

				if (next !== null) {
					next.ref(inverse).fixup(this);
				}
			}

			return this;
		}
	});
}

one.create = function (left) {
	left.is = function (right) {
		return right === one;
	};

	left.bind = function () {
		return one.create(Function.prototype.bind.apply(left, arguments));
	};

	left.fixup = function (entity) {
		left(entity);
	};

	left.revert = function (entity) {
		left(null);
	};

	return left;
};

function many(type, inverse) {
	var value = [];

	return many.create(function () {
		if (arguments.length === 0) {
			return value.slice();
		} else {
			var prev = value;
			var next = arguments[0];

    		if (!isArray(next) || type && next.some(function (entity) { return !entity.is(type); })) {
    			throw new Error('Type mismatch');
    		}

			value = next;

			if (inverse) {
	    		prev.filter(function (entity) {
    				return next.indexOf(entity) === -1;
    			}).forEach(function (entity) {
    				entity.ref(inverse).revert(this);
    			}, this);

	    		next.filter(function (entity) {
    				return prev.indexOf(entity) === -1;
    			}).forEach(function (entity) {
    				entity.ref(inverse).fixup(this);
    			}, this);
    		}

    		return this;
		}
	}, type);
}

many.create = function (left) {
	left.is = function (right) {
		return right === many;
	};

	left.bind = function () {
		return many.create(Function.prototype.bind.apply(left, arguments));
	};

	left.add = function (entity) {
		left(left().filter(function (any) {
			return any !== entity;
		}).concat(entity));
	};

	left.remove = function (entity) {
		left(left().filter(function (any) {
			return any !== entity;
		}));
	};

	left.fixup = function (entity) {
		left.add(entity);
	};

	left.revert = function (entity) {
		left.remove(entity);
	};

	return left;
};

module.exports = Json(Root.create()).
    override('field', function (base) {
        return function (name, init) {
            this.fields(name);

            return base(name, init);
        };
    }).
    def('hasOne', function (type, name, inverse) {
    	return this.lazy(name, function () {
    		return one(type, inverse);
    	});
    }).
    def('hasMany', function (type, name, inverse) {
    	return this.lazy(name, function () {
   			return many(type, inverse);
    	});
    });

<|endoftext|>