<commit_before>"use strict";
var columnDefs = [
    { field: 'product', chartDataType: 'category' },
    { field: 'book', chartDataType: 'category' },

    { field: 'current', type: 'measure' },
    { field: 'previous', type: 'measure' },
    { headerName: 'PL 1', field: 'pl1', type: 'measure' },
    { headerName: 'PL 2', field: 'pl2', type: 'measure' },
    { headerName: 'Gain-DX', field: 'gainDx', type: 'measure' },
    { headerName: 'SX / PX', field: 'sxPx', type: 'measure' },

    { field: 'trade', type: 'measure' },
    { field: 'submitterID', type: 'measure' },
    { field: 'submitterDealID', type: 'measure', minWidth: 170 },

    { field: 'portfolio' },
    { field: 'dealType' },
    { headerName: 'Bid', field: 'bidFlag' }
];

var chartRef;

var gridOptions = {
    columnDefs: columnDefs,
    defaultColDef: {
        editable: true,
        sortable: true,
        flex: 1,
        minWidth: 150,
        filter: true,
        resizable: true
    },
    columnTypes: {
        measure: {
            chartDataType: 'series',
            cellClass: 'number',
            valueFormatter: numberCellFormatter,
            cellRenderer: 'agAnimateShowChangeCellRenderer'
        }
    },
    animateRows: true,
    enableCharts: true,
    suppressAggFuncInHeader: true,
    getRowNodeId: function(data) { return data.trade; },
    onFirstDataRendered: function(params) {
        var createRangeChartParams = {
            cellRange: {
                columns: ['product', 'current', 'previous', 'pl1', 'pl2', 'gainDx', 'sxPx']
            },
            chartType: 'groupedColumn',
            chartContainer: document.querySelector('#myChart'),
            suppressChartRanges: true,
            aggFunc: 'sum'
        };

        chartRef = params.api.createRangeChart(createRangeChartParams);
    },
    processChartOptions: function(params) {
        var opts = params.options;

        opts.legend.position = 'bottom';
        opts.yAxis.label.formatter = yAxisLabelFormatter;
        opts.seriesDefaults.tooltip.enabled = true;
        opts.seriesDefaults.fill.colors = ['#c16068', '#a2bf8a', '#ebcc87', '#80a0c3', '#b58dae', '#85c0d1'];
        opts.seriesDefaults.stroke.colors = ['#874349', '#718661', '#a48f5f', '#5a7088', '#7f637a', '#5d8692'];

        opts.seriesDefaults.tooltip.renderer = function(params) {
            var value = '$' + params.datum[params.yKey].toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
            var title = params.title || params.yName;
            return '
' + title + ': ' + value + '
';
        };

        return opts;
    },
    getChartToolbarItems: function() {
        return []; // hide toolbar items
    }
};

function createChart(type) {
    // destroy existing chart
    if (chartRef) {
        chartRef.destroyChart();
    }

    var params = {
        cellRange: {
            columns: ['product', 'current', 'previous', 'pl1', 'pl2', 'gainDx', 'sxPx']
        },
        chartContainer: document.querySelector('#myChart'),
        chartType: type,
        suppressChartRanges: true,
        aggFunc: 'sum'
    };

    chartRef = gridOptions.api.createRangeChart(params);
}

function numberCellFormatter(params) {
    return Math.floor(params.value).toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
}

function yAxisLabelFormatter(params) {
    var n = params.value;
    if (n < 1e3) return n;
    if (n >= 1e3 && n < 1e6) return +(n / 1e3).toFixed(1) + "K";
    if (n >= 1e6 && n < 1e9) return +(n / 1e6).toFixed(1) + "M";
    if (n >= 1e9 && n < 1e12) return +(n / 1e9).toFixed(1) + "B";
    if (n >= 1e12) return +(n / 1e12).toFixed(1) + "T";
}

// after page is loaded, create the grid
document.addEventListener("DOMContentLoaded", function() {
    var eGridDiv = document.querySelector('#myGrid');
    new agGrid.Grid(eGridDiv, gridOptions);
});

var worker;
(function startWorker() {
    worker = new Worker(__basePath + 'dataUpdateWorker.js');
    worker.onmessage = function(e) {
        if (e.data.type === 'setRowData') {
            gridOptions.api.setRowData(e.data.records);
        }
        if (e.data.type === 'updateData') {
            gridOptions.api.applyTransactionAsync({ update: e.data.records });
        }
    };

    worker.postMessage('start');
})();

function onStartLoad() {
    worker.postMessage('start');
}

function onStopMessages() {
    worker.postMessage('stop');
}
<commit_msg>AG-2821 - fixed application created charts example<commit_after>"use strict";
var columnDefs = [
    { field: 'product', chartDataType: 'category' },
    { field: 'book', chartDataType: 'category' },

    { field: 'current', type: 'measure' },
    { field: 'previous', type: 'measure' },
    { headerName: 'PL 1', field: 'pl1', type: 'measure' },
    { headerName: 'PL 2', field: 'pl2', type: 'measure' },
    { headerName: 'Gain-DX', field: 'gainDx', type: 'measure' },
    { headerName: 'SX / PX', field: 'sxPx', type: 'measure' },

    { field: 'trade', type: 'measure' },
    { field: 'submitterID', type: 'measure' },
    { field: 'submitterDealID', type: 'measure', minWidth: 170 },

    { field: 'portfolio' },
    { field: 'dealType' },
    { headerName: 'Bid', field: 'bidFlag' }
];

var chartRef;

var gridOptions = {
    columnDefs: columnDefs,
    defaultColDef: {
        editable: true,
        sortable: true,
        flex: 1,
        minWidth: 150,
        filter: true,
        resizable: true
    },
    columnTypes: {
        measure: {
            chartDataType: 'series',
            cellClass: 'number',
            valueFormatter: numberCellFormatter,
            cellRenderer: 'agAnimateShowChangeCellRenderer'
        }
    },
    animateRows: true,
    enableCharts: true,
    suppressAggFuncInHeader: true,
    getRowNodeId: function(data) { return data.trade; },
    onFirstDataRendered: function(params) {
        var createRangeChartParams = {
            cellRange: {
                columns: ['product', 'current', 'previous', 'pl1', 'pl2', 'gainDx', 'sxPx']
            },
            chartType: 'groupedColumn',
            chartContainer: document.querySelector('#myChart'),
            suppressChartRanges: true,
            aggFunc: 'sum'
        };

        chartRef = params.api.createRangeChart(createRangeChartParams);
    },
    chartThemes: ['pastel-dark'],
    chartThemeOverrides: {
        common: {
            legend: {
                position: 'bottom',
            }
        },
        column: {
            axes: {
                number: {
                    label: {
                        formatter: yAxisLabelFormatter
                    }
                },
                category: {
                    label: {
                        rotation: 0,
                    },
                },
            },
            series: {
                tooltipRenderer: tooltipRenderer
            }
        },
        line: {
            series: {
                tooltipRenderer: tooltipRenderer
            }
        }

    },
    getChartToolbarItems: function() {
        return []; // hide toolbar items
    }
};

function createChart(type) {
    // destroy existing chart
    if (chartRef) {
        chartRef.destroyChart();
    }

    var params = {
        cellRange: {
            columns: ['product', 'current', 'previous', 'pl1', 'pl2', 'gainDx', 'sxPx']
        },
        chartContainer: document.querySelector('#myChart'),
        chartType: type,
        suppressChartRanges: true,
        aggFunc: 'sum'
    };

    chartRef = gridOptions.api.createRangeChart(params);
}

function numberCellFormatter(params) {
    return Math.floor(params.value).toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
}

function yAxisLabelFormatter(params) {
    var n = params.value;
    if (n < 1e3) return n;
    if (n >= 1e3 && n < 1e6) return +(n / 1e3).toFixed(1) + "K";
    if (n >= 1e6 && n < 1e9) return +(n / 1e6).toFixed(1) + "M";
    if (n >= 1e9 && n < 1e12) return +(n / 1e9).toFixed(1) + "B";
    if (n >= 1e12) return +(n / 1e12).toFixed(1) + "T";
}

function tooltipRenderer(params) {
    var value = '$' + params.datum[params.yKey].toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
    var title = params.title || params.yName;
    return '
' + title + ': ' + value + '
';
}

// after page is loaded, create the grid
document.addEventListener("DOMContentLoaded", function() {
    var eGridDiv = document.querySelector('#myGrid');
    new agGrid.Grid(eGridDiv, gridOptions);
});

var worker;
(function startWorker() {
    worker = new Worker(__basePath + 'dataUpdateWorker.js');
    worker.onmessage = function(e) {
        if (e.data.type === 'setRowData') {
            gridOptions.api.setRowData(e.data.records);
        }
        if (e.data.type === 'updateData') {
            gridOptions.api.applyTransactionAsync({ update: e.data.records });
        }
    };

    worker.postMessage('start');
})();

function onStartLoad() {
    worker.postMessage('start');
}

function onStopMessages() {
    worker.postMessage('stop');
}
<|endoftext|>


<commit_before>/**
 * Story
 *
 * @module      ::  Model
 * @description ::  This model represent user story on taskboard. User stories are attached to project and
 *                  sprint. Sprint relation is not required, if sprintId is 0 or null current user story is
 *                  in project backlog.
 */
module.exports = {
    attributes: {
        projectId: {
            type:       'integer',
            required:   true
        },
        sprintId: {
            type:       'integer'
        },
        title: {
            type:       'string',
            required:   true
        },
        description: {
            type:       'text',
            required:   true
        },
        estimate: {
            type:       'integer',
            required:   true
        },
        priority: {
            type:       'integer'
        },
        vfCase: {
            type:       'integer'
        }
    },

    // Lifecycle Callbacks
    beforeCreate: function(values, cb) {
        Story
            .findOne({
                projectId: values.projectId,
                sprintId: values.sprintId
            })
            .sort('priority DESC')
            .done(function(error, story) {
                var priority;

                if (error) {
                    cb(error)
                } else if (!story) {
                    priority = 0;
                } else {
                    priority = story.priority + 1;
                }

                values.priority = priority;

                cb();
            });
    }
};
<commit_msg>??? estimate for stories - model changes.<commit_after>/**
 * Story
 *
 * @module      ::  Model
 * @description ::  This model represent user story on taskboard. User stories are attached to project and
 *                  sprint. Sprint relation is not required, if sprintId is 0 or null current user story is
 *                  in project backlog.
 */
module.exports = {
    attributes: {
        projectId: {
            type:       'integer',
            required:   true
        },
        sprintId: {
            type:       'integer'
        },
        title: {
            type:       'string',
            required:   true
        },
        description: {
            type:       'text',
            required:   true
        },
        estimate: {
            type:       'integer',
            required:   true,
            defaultsTo: -1
        },
        priority: {
            type:       'integer'
        },
        vfCase: {
            type:       'integer'
        },

        estimateFormatted: function() {
            return (this.estimate == -1) ? '???' : this.estimate;
        }
    },

    // Lifecycle Callbacks
    beforeCreate: function(values, cb) {
        Story
            .findOne({
                projectId: values.projectId,
                sprintId: values.sprintId
            })
            .sort('priority DESC')
            .done(function(error, story) {
                var priority;

                if (error) {
                    cb(error)
                } else if (!story) {
                    priority = 0;
                } else {
                    priority = story.priority + 1;
                }

                values.priority = priority;

                cb();
            });
    }
};
<|endoftext|>


<commit_before>/* eslint-env mocha */

"use strict";

const request = require("supertest");
const assert = require("proclaim");
const vm = require("vm");

const host = require("../helpers").host;

describe("OPTIONS /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.options("/v3/polyfill.js")
			.expect(200)
			.expect("Allow", "OPTIONS, GET, HEAD");
	});
});

describe("POST /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 405 status", () => {
		return request(host)
			.post("/v3/polyfill.js")
			.expect(405);
	});
});

describe("DELETE /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 405 status", () => {
		return request(host)
			.delete("/v3/polyfill.js")
			.expect(405);
	});
});

describe("PURGE /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 401 status", () => {
		return request(host)
			.purge("/v3/polyfill.js")
			.expect(401);
	});
});

describe("HEAD /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.head("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.expect(200)
			.expect("Content-Type", "text/javascript; charset=utf-8")
			.expect("Access-Control-Allow-Origin", "*")
			.expect("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS")
			.expect("cache-control", "public, s-maxage=31536000, max-age=604800, stale-while-revalidate=604800, stale-if-error=604800")
			.expect("surrogate-key", "polyfill-service");
	});
});

describe("GET /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.expect(200)
			.expect("Content-Type", "text/javascript; charset=utf-8")
			.expect("Access-Control-Allow-Origin", "*")
			.expect("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS")
			.expect("cache-control", "public, s-maxage=31536000, max-age=604800, stale-while-revalidate=604800, stale-if-error=604800")
			.expect("surrogate-key", "polyfill-service")
			.then(response => {
				assert.isString(response.text);
				assert.doesNotThrow(() => new vm.Script(response.text));
				assert.notMatch(response.text, /\/\/#\ssourceMappingURL(.+)/);
			});
	});
});

describe("GET /v3/polyfill.js?features=carrot&strict", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.expect(200)
			.expect("Content-Type", "text/javascript; charset=utf-8")
			.expect("cache-control", "public, s-maxage=31536000, max-age=604800, stale-while-revalidate=604800, stale-if-error=604800")
			.expect("surrogate-key", "polyfill-service")
			.then(response => {
				assert.isString(response.text);
				assert.doesNotThrow(() => new vm.Script(response.text));
				assert.notMatch(response.text, /\/\/#\ssourceMappingURL(.+)/);
			});
	});
});

describe("GET /v3/polyfill.js?callback=AAA&callback=BBB", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.get("/v3/polyfill.js?callback=AAA&callback=BBB")
			.set("Fastly-Debug", "true")
			.expect(200)
			.expect("Content-Type", "text/javascript; charset=utf-8")
			.expect("Access-Control-Allow-Origin", "*")
			.expect("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS")
			.expect("cache-control", "public, s-maxage=31536000, max-age=604800, stale-while-revalidate=604800, stale-if-error=604800")
			.expect("surrogate-key", "polyfill-service")
			.then(response => {
				assert.isString(response.text);
				assert.doesNotThrow(() => new vm.Script(response.text));
				assert.notMatch(response.text, /\/\/#\ssourceMappingURL(.+)/);
			});
	});
});

describe("GET /v3/polyfill.js?version=hello-i-am-not-a-version", function() {
	this.timeout(30000);
	it("responds with a generic message", () => {
		return request(host)
			.get("/v3/polyfill.js?version=hello-i-am-not-a-version")
			.expect("Content-Type", "text/html; charset=utf-8")
			.then(response => {
				assert.deepEqual(response.text, 'requested version does not exist')
			});
	});
});

describe("encoding", function() {
	this.timeout(30000);
	it("responds with no compression if client does not accept compressed responses", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.set("Accept-Encoding", "identity")
			.expect("Vary", "User-Agent, Accept-Encoding")
			.then(response => {
				assert.equal(response.headers["content-encoding"], undefined);
			});
	});

	it("responds with gzip compression if client accepts gzip compressed responses", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.set("Accept-Encoding", "gzip")
			.expect("Vary", "User-Agent, Accept-Encoding")
			.expect("Content-Encoding", "gzip");
	});

	it("responds with gzip compression if client accepts gzip and deflate compressed responses", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.set("Accept-Encoding", "gzip, deflate")
			.expect("Vary", "User-Agent, Accept-Encoding")
			.expect("Content-Encoding", "gzip");
	});

	it("responds with brotli compression if client accepts brotli compressed responses", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.set("Accept-Encoding", "br")
			.expect("Vary", "User-Agent, Accept-Encoding")
			.expect("Content-Encoding", "br");
	});

	it("responds with brotli compression if client accepts brotli and gzip compressed responses", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.set("Accept-Encoding", "br, gzip")
			.expect("Vary", "User-Agent, Accept-Encoding")
			.expect("Content-Encoding", "br");
	});
});

describe("GET /v3/polyfill.js?features=all&ua=non-existent-ua&unknown=polyfill&flags=gated&rum=1", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.get("/v3/polyfill.js?features=all&ua=non-existent-ua&unknown=polyfill&flags=gated&rum=1")
			.set("Fastly-Debug", "true")
			.expect(200)
			.expect("Content-Type", "text/javascript; charset=utf-8")
			.expect("Access-Control-Allow-Origin", "*")
			.expect("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS")
			.expect("cache-control", "public, s-maxage=31536000, max-age=604800, stale-while-revalidate=604800, stale-if-error=604800")
			.expect("surrogate-key", "polyfill-service")
			.then(response => {
				assert.isString(response.text);
				// vm.Script will cause the event loop to become blocked whilst it parses the large response
				assert.doesNotThrow(() => new vm.Script(response.text));
				assert.notMatch(response.text, /\/\/#\ssourceMappingURL(.+)/);
			});
	});
});
<commit_msg>Update polyfill.test.js<commit_after>/* eslint-env mocha */

"use strict";

const request = require("supertest");
const assert = require("proclaim");
const vm = require("vm");

const host = require("../helpers").host;

describe("OPTIONS /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.options("/v3/polyfill.js")
			.expect(200)
			.expect("Allow", "OPTIONS, GET, HEAD");
	});
});

describe("POST /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 405 status", () => {
		return request(host)
			.post("/v3/polyfill.js")
			.expect(405);
	});
});

describe("DELETE /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 405 status", () => {
		return request(host)
			.delete("/v3/polyfill.js")
			.expect(405);
	});
});

describe("PURGE /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 401 status", () => {
		return request(host)
			.purge("/v3/polyfill.js")
			.expect(401);
	});
});

describe("HEAD /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.head("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.expect(200)
			.expect("Content-Type", "text/javascript; charset=utf-8")
			.expect("Access-Control-Allow-Origin", "*")
			.expect("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS")
			.expect("cache-control", "public, s-maxage=31536000, max-age=604800, stale-while-revalidate=604800, stale-if-error=604800")
			.expect("surrogate-key", "polyfill-service");
	});
});

describe("GET /v3/polyfill.js", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.expect(200)
			.expect("Content-Type", "text/javascript; charset=utf-8")
			.expect("Access-Control-Allow-Origin", "*")
			.expect("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS")
			.expect("cache-control", "public, s-maxage=31536000, max-age=604800, stale-while-revalidate=604800, stale-if-error=604800")
			.expect("surrogate-key", "polyfill-service")
			.then(response => {
				assert.isString(response.text);
				assert.doesNotThrow(() => new vm.Script(response.text));
				assert.notMatch(response.text, /\/\/#\ssourceMappingURL(.+)/);
			});
	});
});

describe("GET /v3/polyfill.js?features=carrot&strict", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.expect(200)
			.expect("Content-Type", "text/javascript; charset=utf-8")
			.expect("cache-control", "public, s-maxage=31536000, max-age=604800, stale-while-revalidate=604800, stale-if-error=604800")
			.expect("surrogate-key", "polyfill-service")
			.then(response => {
				assert.isString(response.text);
				assert.doesNotThrow(() => new vm.Script(response.text));
				assert.notMatch(response.text, /\/\/#\ssourceMappingURL(.+)/);
			});
	});
});

describe("GET /v3/polyfill.js?callback=AAA&callback=BBB", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.get("/v3/polyfill.js?callback=AAA&callback=BBB")
			.set("Fastly-Debug", "true")
			.expect(200)
			.expect("Content-Type", "text/javascript; charset=utf-8")
			.expect("Access-Control-Allow-Origin", "*")
			.expect("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS")
			.expect("cache-control", "public, s-maxage=31536000, max-age=604800, stale-while-revalidate=604800, stale-if-error=604800")
			.expect("surrogate-key", "polyfill-service")
			.then(response => {
				assert.isString(response.text);
				assert.doesNotThrow(() => new vm.Script(response.text));
				assert.notMatch(response.text, /\/\/#\ssourceMappingURL(.+)/);
			});
	});
});

describe("GET /v3/polyfill.js?version=hello-i-am-not-a-version", function() {
	this.timeout(30000);
	it("responds with a generic message", () => {
		if (!host.includes('origami-polyfill-service-dev.in.ft.com') && !host.includes('qa.polyfill.io')) {
			return request(host)
				.get("/v3/polyfill.js?version=hello-i-am-not-a-version")
				.expect("Content-Type", "text/html; charset=utf-8")
				.then(response => {
					assert.deepEqual(response.text, 'requested version does not exist')
				});
		}
	});
});

describe("encoding", function() {
	this.timeout(30000);
	it("responds with no compression if client does not accept compressed responses", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.set("Accept-Encoding", "identity")
			.expect("Vary", "User-Agent, Accept-Encoding")
			.then(response => {
				assert.equal(response.headers["content-encoding"], undefined);
			});
	});

	it("responds with gzip compression if client accepts gzip compressed responses", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.set("Accept-Encoding", "gzip")
			.expect("Vary", "User-Agent, Accept-Encoding")
			.expect("Content-Encoding", "gzip");
	});

	it("responds with gzip compression if client accepts gzip and deflate compressed responses", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.set("Accept-Encoding", "gzip, deflate")
			.expect("Vary", "User-Agent, Accept-Encoding")
			.expect("Content-Encoding", "gzip");
	});

	it("responds with brotli compression if client accepts brotli compressed responses", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.set("Accept-Encoding", "br")
			.expect("Vary", "User-Agent, Accept-Encoding")
			.expect("Content-Encoding", "br");
	});

	it("responds with brotli compression if client accepts brotli and gzip compressed responses", () => {
		return request(host)
			.get("/v3/polyfill.js")
			.set("Fastly-Debug", "true")
			.set("Accept-Encoding", "br, gzip")
			.expect("Vary", "User-Agent, Accept-Encoding")
			.expect("Content-Encoding", "br");
	});
});

describe("GET /v3/polyfill.js?features=all&ua=non-existent-ua&unknown=polyfill&flags=gated&rum=1", function() {
	this.timeout(30000);
	it("responds with a 200 status", () => {
		return request(host)
			.get("/v3/polyfill.js?features=all&ua=non-existent-ua&unknown=polyfill&flags=gated&rum=1")
			.set("Fastly-Debug", "true")
			.expect(200)
			.expect("Content-Type", "text/javascript; charset=utf-8")
			.expect("Access-Control-Allow-Origin", "*")
			.expect("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS")
			.expect("cache-control", "public, s-maxage=31536000, max-age=604800, stale-while-revalidate=604800, stale-if-error=604800")
			.expect("surrogate-key", "polyfill-service")
			.then(response => {
				assert.isString(response.text);
				// vm.Script will cause the event loop to become blocked whilst it parses the large response
				assert.doesNotThrow(() => new vm.Script(response.text));
				assert.notMatch(response.text, /\/\/#\ssourceMappingURL(.+)/);
			});
	});
});
<|endoftext|>


<commit_before>
function get_link_url() {
    //URL for the notes
    if ('lims_step' in window && lims_step !== null){
        note_url='/api/v1/workset_links/' + lims_step
    }else if ('flowcell_id_reference' in window && flowcell_id_reference!== null){
        note_url='/api/v1/flowcell_links/' + flowcell_id_reference;
    }else {
        note_url='/api/v1/links/' + project
    }
    return note_url;
}

function load_links() {
  link_url=get_link_url();
  var link_icon = {'Deviation':'exclamation-circle text-danger', 'Other':'file text-primary'};
  $("#existing_links").empty();
  $("#existing_links_projinfo").empty();
  $("#links_tab").hide();
  $.getJSON(link_url, function(data) {
    $("#links_tab").show();
    $.each(data, function(key, link) {
      var link_href = link['url'] === "" ? "" : (' href="' + link['url'] + '"');
      var date = key.replace(/-/g, '/');
      date = date.replace(/\.\d{6}/, '');
      date = new Date(date);
      $("#existing_links_projinfo").append('
'+
      '
'+
        ''+
        ''+link['title']+''
        +'	'+link['desc']+'  '+link['user']+''+
        ' - '+date.toDateString()+'
');
      $("#existing_links").append('
 \

 \

  \

 \
                                         \
                                         \

 \

 \

 \
                                          '+link['title']+' \
                                           \
                                          '+link['user']+' - '+date.toDateString()+' \
                                           \

 '+link['desc']+' \

 \

 \

 \

');
    });
  });
}

$("#link_form").submit(function(e) {
  e.preventDefault();
  var type = $('#new_link_type option:selected').val();
  var title = $('#new_link_title').val();
  var url = $('#new_link_url').val();
  var desc = $('#new_link_desc').val();
  link_url=get_link_url();

  if (title && type) {
    $.ajax({
      async: false,
      type: 'POST',
      url: link_url,
      dataType: 'json',
      data: {'type': type, 'title': title, 'url':url, 'desc':desc}
    }).done(function(){
      //Clear form fields
      $('#new_link_type, #new_link_title, #new_link_url, #new_link_desc').val("");
      load_links();
    }).fail(function( jqxhr, textStatus, error ) {
        var err = textStatus + ", " + error;
        console.log( "Couldn't insert link: " + err );
        alert( "Error - Couldn't insert link ..  Is there something weird about this project in the LIMS?" );
    });

  }
  else if(!$.browser.chrome) {
    //Non-chrome users might not get a useful html5 message
    alert('The link needs a title and a type needs to be selected');
  }
});
<commit_msg>Open link in new tab:Oops<commit_after>
function get_link_url() {
    //URL for the notes
    if ('lims_step' in window && lims_step !== null){
        note_url='/api/v1/workset_links/' + lims_step
    }else if ('flowcell_id_reference' in window && flowcell_id_reference!== null){
        note_url='/api/v1/flowcell_links/' + flowcell_id_reference;
    }else {
        note_url='/api/v1/links/' + project
    }
    return note_url;
}

function load_links() {
  link_url=get_link_url();
  var link_icon = {'Deviation':'exclamation-circle text-danger', 'Other':'file text-primary'};
  $("#existing_links").empty();
  $("#existing_links_projinfo").empty();
  $("#links_tab").hide();
  $.getJSON(link_url, function(data) {
    $("#links_tab").show();
    $.each(data, function(key, link) {
      var link_href = link['url'] === "" ? "" : (' href="' + link['url'] + '"');
      var date = key.replace(/-/g, '/');
      date = date.replace(/\.\d{6}/, '');
      date = new Date(date);
      $("#existing_links_projinfo").append('
'+
      '
'+
        ''+
        ''+link['title']+''
        +'	'+link['desc']+'  '+link['user']+''+
        ' - '+date.toDateString()+'
');
      $("#existing_links").append('
 \

 \

  \

 \
                                         \
                                         \

 \

 \

 \
                                          '+link['title']+' \
                                           \
                                          '+link['user']+' - '+date.toDateString()+' \
                                           \

 '+link['desc']+' \

 \

 \

 \

');
    });
  });
}

$("#link_form").submit(function(e) {
  e.preventDefault();
  var type = $('#new_link_type option:selected').val();
  var title = $('#new_link_title').val();
  var url = $('#new_link_url').val();
  var desc = $('#new_link_desc').val();
  link_url=get_link_url();

  if (title && type) {
    $.ajax({
      async: false,
      type: 'POST',
      url: link_url,
      dataType: 'json',
      data: {'type': type, 'title': title, 'url':url, 'desc':desc}
    }).done(function(){
      //Clear form fields
      $('#new_link_type, #new_link_title, #new_link_url, #new_link_desc').val("");
      load_links();
    }).fail(function( jqxhr, textStatus, error ) {
        var err = textStatus + ", " + error;
        console.log( "Couldn't insert link: " + err );
        alert( "Error - Couldn't insert link ..  Is there something weird about this project in the LIMS?" );
    });

  }
  else if(!$.browser.chrome) {
    //Non-chrome users might not get a useful html5 message
    alert('The link needs a title and a type needs to be selected');
  }
});
<|endoftext|>


<commit_before>import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
// import { DropdownButton, MenuItem, Row, Col } from 'react-bootstrap';
import { Row, Col } from '@folio/stripes-components/lib/LayoutGrid';
import MultiColumnList from '@folio/stripes-components/lib/MultiColumnList';
import Button from '@folio/stripes-components/lib/Button';
import { UncontrolledDropdown } from '@folio/stripes-components/lib/Dropdown';
import MenuItem from '@folio/stripes-components/lib/MenuItem';
import DropdownMenu from '@folio/stripes-components/lib/DropdownMenu';

import Label from '../Label';
import { formatDate, formatDateTime } from '../../util';
import css from './OpenLoans.css';

class OpenLoans extends React.Component {

  static propTypes = {
    stripes: PropTypes.shape({
      locale: PropTypes.string.isRequired,
    }).isRequired,
    history: PropTypes.object.isRequired,
    mutator: PropTypes.shape({
      loanId: PropTypes.shape({
        replace: PropTypes.func,
      }),
      loansHistory: PropTypes.shape({
        PUT: PropTypes.func.isRequired,
      }),
    }),
    onClickViewLoanActionsHistory: PropTypes.func.isRequired,
    loans: PropTypes.arrayOf(PropTypes.object).isRequired,
  };

  static manifest = Object.freeze({
    loanId: {},
    loansHistory: {
      fetch: false,
      type: 'okapi',
      PUT: {
        path: 'circulation/loans/%{loanId}',
      },
    },
  });

  constructor(props) {
    super(props);

    this.handleOptionsChange = this.handleOptionsChange.bind(this);
    this.onRowClick = this.onRowClick.bind(this);
    this.toggleAll = this.toggleAll.bind(this);
    this.renewAll = this.renewAll.bind(this);

    this.state = { checkedLoans: {}, allChecked: false };
  }

  onRowClick(e, row) {
    if (e.target.type !== 'button') {
      this.props.onClickViewLoanActionsHistory(e, row);
    }
  }

  getLoansFormatter() {
    const stripes = this.props.stripes;
    const checkedLoans = this.state.checkedLoans;
    const loanTitleFormatter = (loan, label) => (
      <a
        href={`/items/view/${loan.itemId}`}
        onClick={e => this.goToItem(e, loan.itemId)}
      >{label}
    );

    return {
      '  ': loan => (
        <input
          checked={!!(checkedLoans[loan.id])}
          onClick={e => this.toggleItem(e, loan)}
          type="checkbox"
        />
      ),
      title: loan => loanTitleFormatter(loan, _.get(loan, ['item', 'title'], '')),
      barcode: loan => loanTitleFormatter(loan, _.get(loan, ['item', 'barcode'], '')),
      itemStatus: loan => `${_.get(loan, ['item', 'status', 'name'], '')}`,
      loanDate: loan => formatDate(loan.loanDate, stripes.locale),
      dueDate: loan => (loan.dueDate ? formatDateTime(loan.dueDate, stripes.locale) : ''),
      renewals: loan => loan.renewalCount || 0,
      ' ': loan => this.renderActions(loan),
    };
  }

  toggleItem(e, loan) {
    e.stopPropagation();

    const id = loan.id;
    const loans = this.state.checkedLoans;
    const checkedLoans = (loans[id])
      ? _.omit(loans, id)
      : { ...loans, [id]: loan };
    const allChecked = _.size(checkedLoans) === this.props.loans.length;

    this.setState({ checkedLoans, allChecked });
  }

  toggleAll(e) {
    const loans = this.props.loans;
    const checkedLoans = (e.target.checked)
      ? loans.reduce((memo, loan) => (Object.assign(memo, { [loan.id]: loan })), {})
      : {};

    const allChecked = !this.state.allChecked;
    this.setState({ checkedLoans, allChecked });
  }

  goToItem(e, itemId) {
    this.props.history.push(`/items/view/${itemId}`);
    e.preventDefault();
  }

  renew(loan) {
    Object.assign(loan, {
      renewalCount: (loan.renewalCount || 0) + 1,
      loanDate: moment().format(),
      dueDate: moment(loan.dueDate).add(30, 'days').format(),
      action: 'renewed',
    });

    this.props.mutator.loanId.replace(loan.id);
    this.props.mutator.loansHistory.PUT(_.omit(loan, ['item', 'rowIndex']));
  }

  renewAll() {
    _.forIn(this.state.checkedLoans, loan => this.renew(loan));
    this.setState({ checkedLoans: {}, allChecked: false });
  }

  /**
   * change handler for the options-menu prevents the event from bubbling
   * up to the event handler attached to the row.
   */
  handleOptionsChange(key, e) {
    if (key.action && this[key.action]) {
      this[key.action](key.loan);
    }
  }

  renderActions(loan) {
    return (
      <UncontrolledDropdown
        onSelectItem={this.handleOptionsChange}
      >
        <Button data-role='toggle' buttonStyle='hover dropdownActive'>•••</Button>
        <DropdownMenu data-role='menu' overrideStyle={{padding: '6px 0'}}>
          <MenuItem itemMeta={{ loan, action: 'renew' }}>
            <Button buttonStyle='dropdownItem'>Renew</Button>
          </MenuItem>
        </DropdownMenu>
      </UncontrolledDropdown>
    );
  }

  renderSubHeader() {
    const checkedLoans = this.state.checkedLoans;

    return (
      <Row>
        <Col xs={2}>
          {this.props.loans.length} Records found
        </Col>
        <Col xs={10}>
          <Button id="renew-all" disabled={_.size(checkedLoans) === 0} onClick={this.renewAll}>Renew</Button>
        </Col>
      </Row>
    );
  }

  render() {
    const allChecked = this.state.allChecked;
    const visibleColumns = ['  ', 'title', 'itemStatus', 'barcode', 'loanDate', 'dueDate', 'renewals', ' '];
    const columnMapping = {
      '  ': (<input type="checkbox" checked={allChecked} name="check-all" onChange={this.toggleAll} />),
      loanDate: 'Loan Date',
      dueDate: 'Due Date',
      itemStatus: 'Item Status',
    };

    return (

        {this.props.loans.length > 0 && this.renderSubHeader()}
        <MultiColumnList
          id="list-loanshistory"
          fullWidth
          formatter={this.getLoansFormatter()}
          columnWidths={{ '  ': 28, barcode: 150, title: 350 }}
          visibleColumns={visibleColumns}
          columnMapping={columnMapping}
          columnOverflow={{ ' ': true }}
          onRowClick={this.onRowClick}
          contentData={this.props.loans}
          autosize
          virtualize
        />

    );
  }
}

export default OpenLoans;
<commit_msg>Init work on sorting<commit_after>import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { DropdownButton, MenuItem, Row, Col } from 'react-bootstrap';
import MultiColumnList from '@folio/stripes-components/lib/MultiColumnList';
import Button from '@folio/stripes-components/lib/Button';

import Label from '../Label';
import { formatDate, formatDateTime } from '../../util';
import css from './OpenLoans.css';

const orderMap = {
  title: loan => _.get(loan, ['item', 'title']),
  barcode: loan => _.get(loan, ['item', 'barcode'])
};

class OpenLoans extends React.Component {

  static propTypes = {
    stripes: PropTypes.shape({
      locale: PropTypes.string.isRequired,
    }).isRequired,
    history: PropTypes.object.isRequired,
    mutator: PropTypes.shape({
      loanId: PropTypes.shape({
        replace: PropTypes.func,
      }),
      loansHistory: PropTypes.shape({
        PUT: PropTypes.func.isRequired,
      }),
    }),
    onClickViewLoanActionsHistory: PropTypes.func.isRequired,
    loans: PropTypes.arrayOf(PropTypes.object).isRequired,
  };

  static manifest = Object.freeze({
    loanId: {},
    loansHistory: {
      fetch: false,
      type: 'okapi',
      PUT: {
        path: 'circulation/loans/%{loanId}',
      },
    },
  });

  constructor(props) {
    super(props);

    this.handleOptionsChange = this.handleOptionsChange.bind(this);
    this.onRowClick = this.onRowClick.bind(this);
    this.toggleAll = this.toggleAll.bind(this);
    this.renewAll = this.renewAll.bind(this);
    this.onSort = this.onSort.bind(this);

    this.state = {
      checkedLoans: {},
      allChecked: false,
      sortOrder: ['title' , 'barcode'],
      sortDirection: ['asc', 'asc']
    };
  }

  onRowClick(e, row) {
    if (e.target.type !== 'button') {
      this.props.onClickViewLoanActionsHistory(e, row);
    }
  }

  getLoansFormatter() {
    const stripes = this.props.stripes;
    const checkedLoans = this.state.checkedLoans;
    const loanTitleFormatter = (loan, label) => (
      <a
        href={`/items/view/${loan.itemId}`}
        onClick={e => this.goToItem(e, loan.itemId)}
      >{label}
    );

    return {
      '  ': loan => (
        <input
          checked={!!(checkedLoans[loan.id])}
          onClick={e => this.toggleItem(e, loan)}
          type="checkbox"
        />
      ),
      title: loan => loanTitleFormatter(loan, _.get(loan, ['item', 'title'], '')),
      barcode: loan => loanTitleFormatter(loan, _.get(loan, ['item', 'barcode'], '')),
      itemStatus: loan => `${_.get(loan, ['item', 'status', 'name'], '')}`,
      loanDate: loan => formatDate(loan.loanDate, stripes.locale),
      dueDate: loan => (loan.dueDate ? formatDateTime(loan.dueDate, stripes.locale) : ''),
      renewals: loan => loan.renewalCount || 0,
      ' ': loan => this.renderActions(loan),
    };
  }

  toggleItem(e, loan) {
    e.stopPropagation();

    const id = loan.id;
    const loans = this.state.checkedLoans;
    const checkedLoans = (loans[id])
      ? _.omit(loans, id)
      : { ...loans, [id]: loan };
    const allChecked = _.size(checkedLoans) === this.props.loans.length;
    this.setState({ ...this.state, checkedLoans, allChecked });
  }

  toggleAll(e) {
    const loans = this.props.loans;
    const checkedLoans = (e.target.checked)
      ? loans.reduce((memo, loan) => (Object.assign(memo, { [loan.id]: loan })), {})
      : {};

    const allChecked = !this.state.allChecked;
    this.setState({ ...this.state, checkedLoans, allChecked });
  }

  goToItem(e, itemId) {
    this.props.history.push(`/items/view/${itemId}`);
    e.preventDefault();
  }

  renew(loan) {
    Object.assign(loan, {
      renewalCount: (loan.renewalCount || 0) + 1,
      loanDate: moment().format(),
      dueDate: moment(loan.dueDate).add(30, 'days').format(),
      action: 'renewed',
    });

    this.props.mutator.loanId.replace(loan.id);
    this.props.mutator.loansHistory.PUT(_.omit(loan, ['item', 'rowIndex']));
  }

  onSort(e, meta) {
    const sortOrder = [meta.alias, this.state.sortOrder[0]];
    const direction = (this.state.sortDirection[0] == 'desc') ? 'asc' : 'desc';
    const sortDirection = [direction, direction];

    this.setState({ ...this.state, sortOrder, sortDirection });
  }

  renewAll() {
    _.forIn(this.state.checkedLoans, loan => this.renew(loan));
    this.setState({ checkedLoans: {}, allChecked: false });
  }

  /**
   * change handler for the options-menu prevents the event from bubbling
   * up to the event handler attached to the row.
   */
  handleOptionsChange(key, e) {
    e.preventDefault();
    e.stopPropagation();

    if (key.action && this[key.action]) {
      this[key.action](key.loan);
    }
  }

  renderActions(loan) {
    return (
      <DropdownButton
        title="•••"
        id={`bg-nested-dropdown-${loan.id}`}
        noCaret
        pullRight
        onSelect={this.handleOptionsChange}
      >
        <MenuItem eventKey={{ loan, action: 'renew' }}>Renew</MenuItem>
      </DropdownButton>
    );
  }

  renderSubHeader() {
    const checkedLoans = this.state.checkedLoans;

    return (
      <Row>
        <Col xs={2}>
          {this.props.loans.length} Records found
        </Col>
        <Col xs={10}>
          <Button id="renew-all" disabled={_.size(checkedLoans) === 0} onClick={this.renewAll}>Renew</Button>
        </Col>
      </Row>
    );
  }

  render() {
    const allChecked = this.state.allChecked;
    const visibleColumns = ['  ', 'title', 'itemStatus', 'barcode', 'loanDate', 'dueDate', 'renewals', ' '];
    const columnMapping = {
      '  ': (<input type="checkbox" checked={allChecked} name="check-all" onChange={this.toggleAll} />),
      loanDate: 'Loan Date',
      dueDate: 'Due Date',
      itemStatus: 'Item Status',
    };

    const { sortOrder, sortDirection } = this.state;
    const loans = _.orderBy(this.props.loans,
      [orderMap[sortOrder[0]], orderMap[sortOrder[1]]],
      sortDirection);

    return (

        {this.props.loans.length > 0 && this.renderSubHeader()}
        <MultiColumnList
          id="list-loanshistory"
          fullWidth
          formatter={this.getLoansFormatter()}
          columnWidths={{ '  ': 20, barcode: 150, title: 350 }}
          visibleColumns={visibleColumns}
          columnMapping={columnMapping}
          columnOverflow={{ ' ': true }}
          onHeaderClick={this.onSort}
          onRowClick={this.onRowClick}
          sortOrder={sortOrder[0]}
          sortDirection={`${sortDirection[0]}ending`}
          contentData={loans}
          autosize
          virtualize
        />

    );
  }
}

export default OpenLoans;
<|endoftext|>


<commit_before>/**
 * Selection basic usage
 */

import React from 'react';

export default () => (

    Hello.

);
<commit_msg>Added basic example<commit_after>/**
 * Selection basic usage
 */

import React from 'react';
import Selection from '@folio/stripes-components/lib/Selection';

// the dataOptions prop takes an array of objects with 'label' and 'value' keys
const countriesOptions = [
  { value: 'AU', label: 'Australia' },
  { value: 'CN', label: 'China' },
  { value: 'DK', label: 'Denmark' },
  { value: 'MX', label: 'Mexico' },
  { value: 'SE', label: 'Sweden' },
  { value: 'US', label: 'United States' },
  { value: 'UK', label: 'United Kingdom' },
  // ...obviously there are more....
];

export default () => (

    <Selection
      name="SelectionCountry"
      label="Country"
      id="countrySelect"
      placeholder="Select country"
      dataOptions={countriesOptions}
    />

);
<|endoftext|>


<commit_before>var js_at = require('../../ast').js;
var path = require('path');

module.exports = function(flow){
  var queue = flow.files.queue;
  var fconsole = flow.console;
  var templateModule = flow.tmpl.module;

  if (!templateModule)
  {
    fconsole.log('basis.template is not found');
    fconsole.log('Skiped.');
    return;
  }

  /*console.log(flow.js.globalScope.resolve(['dot', ['dot', ['name', 'basis'], 'template'], 'define']));
  console.log(flow.js.globalScope.resolve(['dot', ['name', 'basis'], 'template']))
  process.exit();*/

  //
  // process tmpl resources
  //

  var implicitDefineSeed = 1;
  var implicitMap = {};
  var implicitDefine = {
    base: {}
  };

  // Temporary solution
  // TODO: rework it
  (function(){
    for (var key in flow.files.preprocess)
      flow.files.preprocess[key].forEach(function(preprocessor){
        if (typeof preprocessor.init == 'function')
          preprocessor.init(flow, basis);
      });
  })();

  fconsole.start('Check templates and implicit define');
  flow.js.resources.forEach(function(token){
    var file = token.resourceRef;
    if (file.type == 'template')
    {
      var code = js_at.translate(token);
      if (!token.themeDefined)
      {
        var templateGet = js_at.parse('basis.template.get', 1);
        var id;

        if (!implicitMap[file.relpath])
        {
          id = '#' + (implicitDefineSeed++).toString(36);
          implicitMap[file.relpath] = id;
          var resToken = token.slice();
          resToken.ref_ = token.ref_;
          resToken.refPath_ = token.refPath_;
          resToken.resourceRef = token.resourceRef;
          flow.tmpl.themeResources.base[id] = resToken;
          implicitDefine.base[id] = token.resourceRef;
        }
        else
        {
          id = implicitMap[file.relpath];
        }

        token.ref_ = flow.js.globalScope.resolve(templateGet);
        token.refPath_ = 'basis.template.get';
        token[1] = templateGet;
        token[2] = [['string', id]];
        //console.log(token);
        //token.splice(0, token.length, ['call', templateGet, [['string', 'xx']]]);
        fconsole.log(code, '->', js_at.translate(token));
      }
      else
      {
        fconsole.log(code, 'already in theme define');
      }
    }
  });
  fconsole.endl();

  //addImplicitDefine(flow, 'base', implicitBase);

  //
  // process themes
  //

  // collect keys
  var defineKeys = {};
  var themeList = [];
  for (var themeName in flow.tmpl.themes)
  {
    themeList.push(themeName);
    for (var key in flow.tmpl.themeResources[themeName])
      defineKeys[key] = true;
  }

  fconsole.start('Apply template defines');
  for (var themeName in flow.tmpl.themes)
  {
    fconsole.start('theme `' + themeName + '`');
    for (var key in flow.tmpl.themeResources[themeName])
    {
      var resource = flow.tmpl.themeResources[themeName][key];
      if (resource.resourceRef)
      {
        fconsole.log(key, '->', 'basis.resource(\'' + path.relative(flow.options.base, resource.resourceRef.filename) + '\')');
        // replace here because on windows it starts with `C:` or like so,
        // basis.js 1.2+ warn on it
        basis.template.theme(themeName)
          .define(key, basis.resource(resource.resourceRef.filename.replace(/^.*?([\/\\])/, '$1')));
      }
      else
      {
        flow.warn({
          message: 'template source is not a basis.js resource: path `' + key + '` in theme `' + themeName + '`'
        });
      }
    }
    fconsole.endl();
  }
  fconsole.endl();

  //
  // process templates
  //

  fconsole.start('Make template declarations');
  var baseDecl = {};
  for (var themeName in flow.tmpl.themes)
  {
    fconsole.start('theme `' + themeName + '`');
    basis.template.setTheme(themeName);

    if (!implicitDefine[themeName])
      implicitDefine[themeName] = {};

    for (var key in defineKeys)
    {
      var resource = flow.tmpl.themeResources[themeName][key];
      var file = resource && resource.resourceRef;
      var source = basis.template.get(key);
      var decl = basis.template.makeDeclaration(source.get(), path.dirname(source.url) + '/', {
        optimizeSize: flow.options.jsBuildMode
      }, source.url, source);
      var cmpStr = JSON.stringify(decl.tokens);

      fconsole.start(key + (file ? ': basis.resource("' + file.relpath + '")' : ''));
      if (themeName == 'base')
      {
        // save all base templates in map
        baseDecl[key] = {
          str: cmpStr,
          decl: decl
        };

        // copy warnings to flow
        if (decl.warns)
        {
          decl.warns.forEach(function(warn){
            flow.warn({
              file: file && file.relpath,
              message: warn
            });
          });
        }
      }
      else
      {
        if (resource)
        {
          // copy warnings to flow
          if (decl.warns)
          {
            decl.warns.forEach(function(warn){
              flow.warn({
                file: file && file.relpath,
                message: warn
              });
            });
          }
        }
        else
        {
          // theme has no it's own template source for that path
          // but template may contains inclusion, that can changes theme by themes
          if (cmpStr != baseDecl[key].str)
          {
            // template result has difference with base template -> some inclusion depends on theme
            // create fake file for result, and mark it to store in resource map
            var genericFilename = 'genericTemplate' + (implicitDefineSeed++) + '.tmpl';
            file = flow.files.add({
              jsRefCount: 1,
              generated: true,
              type: 'template',
              isResource: true
            });

            // set filename aside, to prevent file manager to read file with that name
            // filename requires for jsRef generation, and actualy it's a hack
            // TODO: solve the problem
            file.filename = genericFilename;
            file.filename = file.jsRef ? null : null;

            // add to implicit map
            implicitDefine[themeName][key] = file;

            fconsole.log('[i] add implicit define', genericFilename);
          }
          else
          {
            // declaration the same, just mask all template resource as required in current theme too
            var resources = baseDecl[key].decl.resources;
            if (resources.length)
            {
              for (var j = 0, resourceFilename; resourceFilename = decl.resources[j]; j++)
              {
                var resFile = basis.resource(resourceFilename).buildFile;
                if (resFile && resFile.themes)
                  resFile.themes.add(themeName);
              }
            }
          }
        }
      }

      if (file)
      {
        // if file exists, store declaration and link it with resources
        file.decl = decl;
        file.ast = decl.tokens;

        if (decl.resources.length)
        {
          for (var j = 0, resourceFilename; resourceFilename = decl.resources[j]; j++)
          {
            var resource = basis.resource(resourceFilename);
            var resFile = flow.files.add(
              resource.virtual  // treat virtual resources as inline
                ? {
                    type: 'style',  // are there possible other kind of resources?
                    inline: true,
                    baseURI: resource().baseURI,
                    content: resource().cssText,
                    themes: []
                  }
                : {
                    filename: resourceFilename, // resource filename already resolved, and should be absolute
                    themes: []
                  }
            );

            // set filename for virtual resources to add them to file-graph
            if (resource.virtual)
              resFile.filename = resource().url.replace(/\?.*$/, '');

            resource.buildFile = resFile;

            // if file has no themes property, that means css file used by other sources
            if (resFile.themes)
              resFile.themes.add(themeName);
            else
              resFile.noThemes = true;

            file.link(resFile, decl.resources);
            resFile.isResource = true;
          }
        }
      }
      fconsole.endl();
    }
    fconsole.endl();
  }
  fconsole.endl();

  // inject implicit
  for (var themeName in flow.tmpl.themes)
    addImplicitDefine(flow, themeName, implicitDefine[themeName]);
};

module.exports.handlerName = '[tmpl] Extract';

function addImplicitDefine(flow, themeName, map){
  var object = ['object', []];
  var files = [];

  for (var key in map)
  {
    var file = map[key];
    var token = ['call', ['dot', ['name', 'basis'], 'resource'], [['string', file.jsRef]]];

    token.ref_ = flow.js.globalScope.resolve(token[1]);
    token.refPath_ = 'basis.resource';
    token.resourceRef = file;

    object[1].push([key, token]);
    files.push(file);
  }

  if (object[1].length)
  {
    var injectCode = js_at.parse('getTheme().define()')[1];

    injectCode[0][1][1][1][2] = [['string', themeName]];
    injectCode[0][1][2][0] = object;

    js_at.append(flow.tmpl.module.ast, ['stat', injectCode]);

    Array.prototype.push.apply(flow.tmpl.module.resources, files);
  }
}
<commit_msg>extract: fix template comparison (isolate issue)<commit_after>var js_at = require('../../ast').js;
var path = require('path');

module.exports = function(flow){
  var queue = flow.files.queue;
  var fconsole = flow.console;
  var templateModule = flow.tmpl.module;

  if (!templateModule)
  {
    fconsole.log('basis.template is not found');
    fconsole.log('Skiped.');
    return;
  }

  /*console.log(flow.js.globalScope.resolve(['dot', ['dot', ['name', 'basis'], 'template'], 'define']));
  console.log(flow.js.globalScope.resolve(['dot', ['name', 'basis'], 'template']))
  process.exit();*/

  //
  // process tmpl resources
  //

  var implicitDefineSeed = 1;
  var implicitMap = {};
  var implicitDefine = {
    base: {}
  };

  // Temporary solution
  // TODO: rework it
  (function(){
    for (var key in flow.files.preprocess)
      flow.files.preprocess[key].forEach(function(preprocessor){
        if (typeof preprocessor.init == 'function')
          preprocessor.init(flow, basis);
      });
  })();

  fconsole.start('Check templates and implicit define');
  flow.js.resources.forEach(function(token){
    var file = token.resourceRef;
    if (file.type == 'template')
    {
      var code = js_at.translate(token);
      if (!token.themeDefined)
      {
        var templateGet = js_at.parse('basis.template.get', 1);
        var id;

        if (!implicitMap[file.relpath])
        {
          id = '#' + (implicitDefineSeed++).toString(36);
          implicitMap[file.relpath] = id;
          var resToken = token.slice();
          resToken.ref_ = token.ref_;
          resToken.refPath_ = token.refPath_;
          resToken.resourceRef = token.resourceRef;
          flow.tmpl.themeResources.base[id] = resToken;
          implicitDefine.base[id] = token.resourceRef;
        }
        else
        {
          id = implicitMap[file.relpath];
        }

        token.ref_ = flow.js.globalScope.resolve(templateGet);
        token.refPath_ = 'basis.template.get';
        token[1] = templateGet;
        token[2] = [['string', id]];
        //console.log(token);
        //token.splice(0, token.length, ['call', templateGet, [['string', 'xx']]]);
        fconsole.log(code, '->', js_at.translate(token));
      }
      else
      {
        fconsole.log(code, 'already in theme define');
      }
    }
  });
  fconsole.endl();

  //addImplicitDefine(flow, 'base', implicitBase);

  //
  // process themes
  //

  // collect keys
  var defineKeys = {};
  var themeList = [];
  for (var themeName in flow.tmpl.themes)
  {
    themeList.push(themeName);
    for (var key in flow.tmpl.themeResources[themeName])
      defineKeys[key] = true;
  }

  fconsole.start('Apply template defines');
  for (var themeName in flow.tmpl.themes)
  {
    fconsole.start('theme `' + themeName + '`');
    for (var key in flow.tmpl.themeResources[themeName])
    {
      var resource = flow.tmpl.themeResources[themeName][key];
      if (resource.resourceRef)
      {
        fconsole.log(key, '->', 'basis.resource(\'' + path.relative(flow.options.base, resource.resourceRef.filename) + '\')');
        // replace here because on windows it starts with `C:` or like so,
        // basis.js 1.2+ warn on it
        basis.template.theme(themeName)
          .define(key, basis.resource(resource.resourceRef.filename.replace(/^.*?([\/\\])/, '$1')));
      }
      else
      {
        flow.warn({
          message: 'template source is not a basis.js resource: path `' + key + '` in theme `' + themeName + '`'
        });
      }
    }
    fconsole.endl();
  }
  fconsole.endl();

  //
  // process templates
  //

  fconsole.start('Make template declarations');
  var baseDecl = {};
  for (var themeName in flow.tmpl.themes)
  {
    fconsole.start('theme `' + themeName + '`');
    basis.template.setTheme(themeName);

    if (!implicitDefine[themeName])
      implicitDefine[themeName] = {};

    for (var key in defineKeys)
    {
      var resource = flow.tmpl.themeResources[themeName][key];
      var file = resource && resource.resourceRef;
      var source = basis.template.get(key);
      var decl = basis.template.makeDeclaration(source.get(), path.dirname(source.url) + '/', {
        optimizeSize: flow.options.jsBuildMode
      }, source.url, source);
      var hash = [source.get()]
        .concat(decl.deps.map(function(dep){
          return dep.url || d;
        }))
        .join('\x00');

      fconsole.start(key + (file ? ': basis.resource("' + file.relpath + '")' : ''));
      if (themeName == 'base')
      {
        // save all base templates in map
        baseDecl[key] = {
          hash: hash,
          decl: decl
        };

        // copy warnings to flow
        if (decl.warns)
        {
          decl.warns.forEach(function(warn){
            flow.warn({
              file: file && file.relpath,
              message: warn
            });
          });
        }
      }
      else
      {
        if (resource)
        {
          // copy warnings to flow
          if (decl.warns)
          {
            decl.warns.forEach(function(warn){
              flow.warn({
                file: file && file.relpath,
                message: warn
              });
            });
          }
        }
        else
        {
          // theme has no it's own template source for that path
          // but template may contains inclusion, that can changes theme by themes
          if (hash != baseDecl[key].hash)
          {
            // template result has difference with base template -> some inclusion depends on theme
            // create fake file for result, and mark it to store in resource map
            var genericFilename = 'genericTemplate' + (implicitDefineSeed++) + '.tmpl';
            file = flow.files.add({
              jsRefCount: 1,
              generated: true,
              type: 'template',
              isResource: true
            });

            // set filename aside, to prevent file manager to read file with that name
            // filename requires for jsRef generation, and actualy it's a hack
            // TODO: solve the problem
            file.filename = genericFilename;
            file.filename = file.jsRef ? null : null;

            // add to implicit map
            implicitDefine[themeName][key] = file;

            fconsole.log('[i] add implicit define', genericFilename);
          }
          else
          {
            // declaration the same, just mask all template resource as required in current theme too
            var resources = baseDecl[key].decl.resources;
            if (resources.length)
            {
              for (var j = 0, resourceFilename; resourceFilename = resources[j]; j++)
              {
                var resFile = basis.resource(resourceFilename).buildFile;
                if (resFile && resFile.themes)
                  resFile.themes.add(themeName);
              }
            }
          }
        }
      }

      if (file)
      {
        // if file exists, store declaration and link it with resources
        file.decl = decl;
        file.ast = decl.tokens;

        if (decl.resources.length)
        {
          for (var j = 0, resourceFilename; resourceFilename = decl.resources[j]; j++)
          {
            var resource = basis.resource(resourceFilename);
            var resFile = flow.files.add(
              resource.virtual  // treat virtual resources as inline
                ? {
                    type: 'style',  // are there possible other kind of resources?
                    inline: true,
                    baseURI: resource().baseURI,
                    content: resource().cssText,
                    themes: []
                  }
                : {
                    filename: resourceFilename, // resource filename already resolved, and should be absolute
                    themes: []
                  }
            );

            // set filename for virtual resources to add them to file-graph
            if (resource.virtual)
              resFile.filename = resource().url.replace(/\?.*$/, '');

            resource.buildFile = resFile;

            // if file has no themes property, that means css file used by other sources
            if (resFile.themes)
              resFile.themes.add(themeName);
            else
              resFile.noThemes = true;

            file.link(resFile, decl.resources);
            resFile.isResource = true;
          }
        }
      }
      fconsole.endl();
    }
    fconsole.endl();
  }
  fconsole.endl();

  // inject implicit
  for (var themeName in flow.tmpl.themes)
    addImplicitDefine(flow, themeName, implicitDefine[themeName]);
};

module.exports.handlerName = '[tmpl] Extract';

function addImplicitDefine(flow, themeName, map){
  var object = ['object', []];
  var files = [];

  for (var key in map)
  {
    var file = map[key];
    var token = ['call', ['dot', ['name', 'basis'], 'resource'], [['string', file.jsRef]]];

    token.ref_ = flow.js.globalScope.resolve(token[1]);
    token.refPath_ = 'basis.resource';
    token.resourceRef = file;

    object[1].push([key, token]);
    files.push(file);
  }

  if (object[1].length)
  {
    var injectCode = js_at.parse('getTheme().define()')[1];

    injectCode[0][1][1][1][2] = [['string', themeName]];
    injectCode[0][1][2][0] = object;

    js_at.append(flow.tmpl.module.ast, ['stat', injectCode]);

    Array.prototype.push.apply(flow.tmpl.module.resources, files);
  }
}
<|endoftext|>


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

function parentPath(x) { return x.match(/(\/[\w_-]+)+\/[\w_-]+$/)[1]; }

var _ = require('underscore');
var util = require('util');
var assert = require('assert');

function SchoolsDB (db, config) {
	this.db = db;
	this.cfg = config;
}
util.inherits(SchoolsDB, require('./objects.js').DBSubsystemBase);

function adminlistContainsUser(admins, user) {
	return _.chain(admins).filter(function(a) { return a.status == 'admin' && a.adminid == user.id; }).value().length == 0;
}

function _reqschooladm (f, soft, scdb) {
	var soft = soft || false;

	return function(query, user, access, cb) {
		(parseInt(query.schoolid) == query.schoolid ? function(cont) { cont(); } : _.bind(function(cont) {
			(this && this.query ? this.query : scdb.query)('SELECT id FROM schools WHERE ? IN (id, name, path)', [query.schoolid], function(res) {
				if (res.length == 0)
					query.schoolid = null;
				else
					query.schoolid = res[0].id;

				cont();
			});
		}, this))(_.bind(function() {
			var forward = _.bind(function() { return _.bind(f, this)(query, user, access, cb); }, this);
			if (access.has('schooldb') || (soft && !query.schoolid))
				return forward();

			var lsa = null;
			if (this && this.loadSchoolAdmins) lsa = _.bind(this.loadSchoolAdmins, this);
			if (scdb && scdb.loadSchoolAdmins) lsa = _.bind(scdb.loadSchoolAdmins, scdb);

			assert.ok(lsa);

			lsa(query.schoolid, function(adminlist) {
				if (adminlistContainsUser(adminlist, user))
					cb('permission-denied');
				else
					forward();
			});
		}, this));
	};
}

// only internal
SchoolsDB.prototype.loadSchoolAdmins = function(schoolid, cb) {
	this.query('SELECT sa.schoolid AS schoolid, sa.uid AS adminid, sa.status AS status, users.name AS adminname ' +
		'FROM schools AS c ' +
		'JOIN schools AS p ON c.path LIKE CONCAT(p.path, "%") OR p.id = c.id ' +
		'JOIN schooladmins AS sa ON sa.schoolid = p.id ' +
		'JOIN users ON users.id = sa.uid ' +
		'WHERE c.id = ?', [schoolid], cb);
};

SchoolsDB.prototype.loadSchoolInfo = function(lookfor, user, access, cb) {
	this.query('SELECT schools.id, schools.name, schools.path, descpage, eventid, type, targetid, time, srcuser, json, url AS banner FROM schools ' +
		'LEFT JOIN events ON events.targetid = schools.id AND events.type = "school-create" ' +
		'LEFT JOIN httpresources ON httpresources.groupassoc = schools.id AND httpresources.role = "schools.banner" ' +
		'WHERE ? IN (schools.id, schools.path, schools.name) ' +
		'LIMIT 1', [lookfor], function(res) {
		if (res.length == 0)
			return cb('get-school-info-notfound');

		var s = res[0];
		s.parentPath = null;

		assert.ok(s.eventid);

		this.loadSchoolAdmins(s.id, function(admins) {
			s.admins = admins;

			this.query('SELECT * FROM schools AS c WHERE c.path LIKE ?', [s.path + '/%'], function(subschools) {
			this.query('SELECT c.*,u.name AS username,u.id AS uid, url AS profilepic, trustedhtml ' +
				'FROM ecomments AS c '+
				'LEFT JOIN users AS u ON c.commenter = u.id ' +
				'LEFT JOIN httpresources ON httpresources.user = c.commenter AND httpresources.role = "profile.image" '+
				'WHERE c.eventid = ?',
				[s.eventid],
				function(comments) {
				s.comments = comments;
				s.subschools = subschools;

				this.query('SELECT oh.stocktextid AS stockid, oh.stockname, ' +
					'SUM(ABS(money)) AS moneysum, ' +
					'SUM(ABS(money) / (UNIX_TIMESTAMP() - buytime)) AS wsum ' +
					'FROM orderhistory AS oh ' +
					'JOIN schoolmembers AS sm ON sm.uid = oh.userid AND sm.jointime < oh.buytime AND sm.schoolid = ? ' +
					'GROUP BY stocktextid ORDER BY wsum DESC LIMIT 10', [s.id], function(popular) {
					if (s.path.replace(/[^\/]/g, '').length != 1) { // need higher-level
						s.parentPath = parentPath(s.path);
						this.loadSchoolInfo(s.parentPath, user, access, function(code, result) {
							assert.equal(code, 'get-school-info-success');

							s.parentSchool = result;

							cb('get-school-info-success', s);
						});
					} else {
						cb('get-school-info-success', s);
					}
				});
			});
			});
		});
	});
};

SchoolsDB.prototype.getSchoolInfo = function(query, user, access, cb) {
	this.loadSchoolInfo(query.lookfor, user, access, cb);
};

SchoolsDB.prototype.changeDescription = _reqschooladm(function(query, user, access, cb) {
	this.query('UPDATE schools SET descpage = ? WHERE id = ?', [query.descpage, query.schoolid], function() {
		cb('school-change-description-success');
	});
});

SchoolsDB.prototype.changeMemberStatus = _reqschooladm(function(query, user, access, cb) {
	this.query('UPDATE schoolmembers SET pending = 0 WHERE schoolid = ? AND uid = ?', [query.schoolid, query.uid], function() {
		if (query.newstatus == 'member') {
			this.query('DELETE FROM schooladmins WHERE uid = ? AND schoolid = ?', [query.uid, query.schoolid], function() {
				cb('school-change-member-status-success');
			});
		} else {
			this.query('REPLACE INTO schooladmins (schoolid, uid, status) VALUES(?, ?, ?)', [query.schoolid, query.uid, query.status], function() {
				cb('school-change-member-status-success');
			});
		}
	});
});

SchoolsDB.prototype.deleteComment = _reqschooladm(function(query, user, access, cb) {
	this.query('SELECT c.commentid AS cid FROM ecomments AS c ' +
		'JOIN events AS e ON e.eventid = c.eventid ' +
		'WHERE c.commentid = ? AND e.targetid = ? AND e.type = "school-create"',
		[query.commentid, query.schoolid], function(res) {
		if (res.length == 0)
			return cb('permission-denied');

		assert.ok(res.length == 1 && res[0].cid == query.commentid);

		this.query('UPDATE ecomments SET comment = ?, trustedhtml = 1 WHERE commentid = ?',
			['Dieser Kommentar wurde durch die Gruppenadministratoren gelöscht.', query.commentid], function() {
			cb('school-delete-comment-success');
		});
	});
});

SchoolsDB.prototype.kickUser = _reqschooladm(function(query, user, access, cb) {
	this.query('DELETE FROM schoolmembers WHERE uid = ? AND schoolid = ?',
		[query.uid, query.schoolid], function() {
		this.query('DELETE FROM schooladmins WHERE uid = ? AND schoolid = ?',
			[query.uid, query.schoolid], function() {
			cb('school-kick-user-success');
		});
	});
});

SchoolsDB.prototype.createSchool = function(query, user, access, cb_) {
	if (!query.schoolpath)
		query.schoolpath = '/' + query.schoolname.replace(/[^\w_-]/g, '');

	this.locked(['userdb'], cb_, function(cb) {
		this.query('SELECT COUNT(*) AS c FROM schools WHERE path = ?', [query.schoolpath], function(r) {
			assert.equal(r.length, 1);
			if (r[0].c == 1 || !query.schoolname.trim() ||
				!/^(\/[\w_-]+)+$/.test(query.schoolpath)) {
				return cb('create-school-already-exists');
			}

			var createCB = _.bind(function() {
				this.query('INSERT INTO schools (name,path) VALUES(?,?)', [query.schoolname,query.schoolpath], function(res) {
					this.feed({'type': 'school-create', 'targetid': res.insertId, 'srcuser': user.id});

					cb('create-school-success');
				});
			}, this);

			if (query.schoolpath.replace(/[^\/]/g, '').length == 1)
				createCB();
			else this.query('SELECT COUNT(*) AS c FROM schools WHERE path = ?', [parentPath(query.schoolpath)], function(r) {
				assert.equal(r.length, 1);
				if (r[0].c != 1)
					return cb('create-school-missing-parent');

				createCB();
			});
		});
	});
};

SchoolsDB.prototype.listSchools = function(query, user, access, cb) {
	var where = '';
	var params = [];
	if (query.parentPath) {
		where = 'WHERE path LIKE ? OR path = ? ';
		params = params.concat([query.parentPath + '/%', query.parentPath]);
	}

	this.query('SELECT schools.id, schools.name, COUNT(sm.uid) AS usercount, schools.path FROM schools ' +
		'LEFT JOIN schoolmembers AS sm ON sm.schoolid=schools.id AND NOT pending ' +
		where +
		'GROUP BY schools.id', params, cb);
}

SchoolsDB.prototype.publishBanner = function(query, user, access, FileStorageDB, cb) {
	query.__groupassoc__ = query.schoolid;
	query.role = 'schools.banner';

	_reqschooladm(_.bind(FileStorageDB.publish, FileStorageDB), false, this)(query, user, access, cb);
};

SchoolsDB.prototype.createInviteLink = function(query, user, access, UserDB, cb) {
	_reqschooladm(_.bind(UserDB.createInviteLink, UserDB), true, this)(query, user, access, cb);
};

exports.SchoolsDB = SchoolsDB;

})();
<commit_msg><commit_after>(function () { "use strict";

function parentPath(x) { return x.match(/(\/[\w_-]+)+\/[\w_-]+$/)[1]; }

var _ = require('underscore');
var util = require('util');
var assert = require('assert');

function SchoolsDB (db, config) {
	this.db = db;
	this.cfg = config;
}
util.inherits(SchoolsDB, require('./objects.js').DBSubsystemBase);

function adminlistContainsUser(admins, user) {
	return _.chain(admins).filter(function(a) { return a.status == 'admin' && a.adminid == user.id; }).value().length == 0;
}

function _reqschooladm (f, soft, scdb) {
	var soft = soft || false;

	return function(query, user, access, cb) {
		(parseInt(query.schoolid) == query.schoolid ? function(cont) { cont(); } : _.bind(function(cont) {
			var dbquery = null;
			if (this && this.query) dbquery = _.bind(this.query, this);
			if (scdb && scdb.query) dbquery = _.bind(scdb.query, scdb);

			assert.ok(dbquery);

			dbquery('SELECT id FROM schools WHERE ? IN (id, name, path)', [query.schoolid], function(res) {
				if (res.length == 0)
					query.schoolid = null;
				else
					query.schoolid = res[0].id;

				cont();
			});
		}, this))(_.bind(function() {
			var forward = _.bind(function() { return _.bind(f, this)(query, user, access, cb); }, this);
			if (access.has('schooldb') || (soft && !query.schoolid))
				return forward();

			var lsa = null;
			if (this && this.loadSchoolAdmins) lsa = _.bind(this.loadSchoolAdmins, this);
			if (scdb && scdb.loadSchoolAdmins) lsa = _.bind(scdb.loadSchoolAdmins, scdb);

			assert.ok(lsa);

			lsa(query.schoolid, function(adminlist) {
				if (adminlistContainsUser(adminlist, user))
					cb('permission-denied');
				else
					forward();
			});
		}, this));
	};
}

// only internal
SchoolsDB.prototype.loadSchoolAdmins = function(schoolid, cb) {
	this.query('SELECT sa.schoolid AS schoolid, sa.uid AS adminid, sa.status AS status, users.name AS adminname ' +
		'FROM schools AS c ' +
		'JOIN schools AS p ON c.path LIKE CONCAT(p.path, "%") OR p.id = c.id ' +
		'JOIN schooladmins AS sa ON sa.schoolid = p.id ' +
		'JOIN users ON users.id = sa.uid ' +
		'WHERE c.id = ?', [schoolid], cb);
};

SchoolsDB.prototype.loadSchoolInfo = function(lookfor, user, access, cb) {
	this.query('SELECT schools.id, schools.name, schools.path, descpage, eventid, type, targetid, time, srcuser, json, url AS banner FROM schools ' +
		'LEFT JOIN events ON events.targetid = schools.id AND events.type = "school-create" ' +
		'LEFT JOIN httpresources ON httpresources.groupassoc = schools.id AND httpresources.role = "schools.banner" ' +
		'WHERE ? IN (schools.id, schools.path, schools.name) ' +
		'LIMIT 1', [lookfor], function(res) {
		if (res.length == 0)
			return cb('get-school-info-notfound');

		var s = res[0];
		s.parentPath = null;

		assert.ok(s.eventid);

		this.loadSchoolAdmins(s.id, function(admins) {
			s.admins = admins;

			this.query('SELECT * FROM schools AS c WHERE c.path LIKE ?', [s.path + '/%'], function(subschools) {
			this.query('SELECT c.*,u.name AS username,u.id AS uid, url AS profilepic, trustedhtml ' +
				'FROM ecomments AS c '+
				'LEFT JOIN users AS u ON c.commenter = u.id ' +
				'LEFT JOIN httpresources ON httpresources.user = c.commenter AND httpresources.role = "profile.image" '+
				'WHERE c.eventid = ?',
				[s.eventid],
				function(comments) {
				s.comments = comments;
				s.subschools = subschools;

				this.query('SELECT oh.stocktextid AS stockid, oh.stockname, ' +
					'SUM(ABS(money)) AS moneysum, ' +
					'SUM(ABS(money) / (UNIX_TIMESTAMP() - buytime)) AS wsum ' +
					'FROM orderhistory AS oh ' +
					'JOIN schoolmembers AS sm ON sm.uid = oh.userid AND sm.jointime < oh.buytime AND sm.schoolid = ? ' +
					'GROUP BY stocktextid ORDER BY wsum DESC LIMIT 10', [s.id], function(popular) {
					if (s.path.replace(/[^\/]/g, '').length != 1) { // need higher-level
						s.parentPath = parentPath(s.path);
						this.loadSchoolInfo(s.parentPath, user, access, function(code, result) {
							assert.equal(code, 'get-school-info-success');

							s.parentSchool = result;

							cb('get-school-info-success', s);
						});
					} else {
						cb('get-school-info-success', s);
					}
				});
			});
			});
		});
	});
};

SchoolsDB.prototype.getSchoolInfo = function(query, user, access, cb) {
	this.loadSchoolInfo(query.lookfor, user, access, cb);
};

SchoolsDB.prototype.changeDescription = _reqschooladm(function(query, user, access, cb) {
	this.query('UPDATE schools SET descpage = ? WHERE id = ?', [query.descpage, query.schoolid], function() {
		cb('school-change-description-success');
	});
});

SchoolsDB.prototype.changeMemberStatus = _reqschooladm(function(query, user, access, cb) {
	this.query('UPDATE schoolmembers SET pending = 0 WHERE schoolid = ? AND uid = ?', [query.schoolid, query.uid], function() {
		if (query.newstatus == 'member') {
			this.query('DELETE FROM schooladmins WHERE uid = ? AND schoolid = ?', [query.uid, query.schoolid], function() {
				cb('school-change-member-status-success');
			});
		} else {
			this.query('REPLACE INTO schooladmins (schoolid, uid, status) VALUES(?, ?, ?)', [query.schoolid, query.uid, query.status], function() {
				cb('school-change-member-status-success');
			});
		}
	});
});

SchoolsDB.prototype.deleteComment = _reqschooladm(function(query, user, access, cb) {
	this.query('SELECT c.commentid AS cid FROM ecomments AS c ' +
		'JOIN events AS e ON e.eventid = c.eventid ' +
		'WHERE c.commentid = ? AND e.targetid = ? AND e.type = "school-create"',
		[query.commentid, query.schoolid], function(res) {
		if (res.length == 0)
			return cb('permission-denied');

		assert.ok(res.length == 1 && res[0].cid == query.commentid);

		this.query('UPDATE ecomments SET comment = ?, trustedhtml = 1 WHERE commentid = ?',
			['Dieser Kommentar wurde durch die Gruppenadministratoren gelöscht.', query.commentid], function() {
			cb('school-delete-comment-success');
		});
	});
});

SchoolsDB.prototype.kickUser = _reqschooladm(function(query, user, access, cb) {
	this.query('DELETE FROM schoolmembers WHERE uid = ? AND schoolid = ?',
		[query.uid, query.schoolid], function() {
		this.query('DELETE FROM schooladmins WHERE uid = ? AND schoolid = ?',
			[query.uid, query.schoolid], function() {
			cb('school-kick-user-success');
		});
	});
});

SchoolsDB.prototype.createSchool = function(query, user, access, cb_) {
	if (!query.schoolpath)
		query.schoolpath = '/' + query.schoolname.replace(/[^\w_-]/g, '');

	this.locked(['userdb'], cb_, function(cb) {
		this.query('SELECT COUNT(*) AS c FROM schools WHERE path = ?', [query.schoolpath], function(r) {
			assert.equal(r.length, 1);
			if (r[0].c == 1 || !query.schoolname.trim() ||
				!/^(\/[\w_-]+)+$/.test(query.schoolpath)) {
				return cb('create-school-already-exists');
			}

			var createCB = _.bind(function() {
				this.query('INSERT INTO schools (name,path) VALUES(?,?)', [query.schoolname,query.schoolpath], function(res) {
					this.feed({'type': 'school-create', 'targetid': res.insertId, 'srcuser': user.id});

					cb('create-school-success');
				});
			}, this);

			if (query.schoolpath.replace(/[^\/]/g, '').length == 1)
				createCB();
			else this.query('SELECT COUNT(*) AS c FROM schools WHERE path = ?', [parentPath(query.schoolpath)], function(r) {
				assert.equal(r.length, 1);
				if (r[0].c != 1)
					return cb('create-school-missing-parent');

				createCB();
			});
		});
	});
};

SchoolsDB.prototype.listSchools = function(query, user, access, cb) {
	var where = '';
	var params = [];
	if (query.parentPath) {
		where = 'WHERE path LIKE ? OR path = ? ';
		params = params.concat([query.parentPath + '/%', query.parentPath]);
	}

	this.query('SELECT schools.id, schools.name, COUNT(sm.uid) AS usercount, schools.path FROM schools ' +
		'LEFT JOIN schoolmembers AS sm ON sm.schoolid=schools.id AND NOT pending ' +
		where +
		'GROUP BY schools.id', params, cb);
}

SchoolsDB.prototype.publishBanner = function(query, user, access, FileStorageDB, cb) {
	query.__groupassoc__ = query.schoolid;
	query.role = 'schools.banner';

	_reqschooladm(_.bind(FileStorageDB.publish, FileStorageDB), false, this)(query, user, access, cb);
};

SchoolsDB.prototype.createInviteLink = function(query, user, access, UserDB, cb) {
	_reqschooladm(_.bind(UserDB.createInviteLink, UserDB), true, this)(query, user, access, cb);
};

exports.SchoolsDB = SchoolsDB;

})();
<|endoftext|>


<commit_before><commit_msg>Test custom 'key' property<commit_after><|endoftext|>


<commit_before>/*
Copyright (c) 2011, Chris Umbel

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

var util = require('util'),
    SingularPluralInflector = require('./singular_plural_inflector'),
    FormSet = require('./form_set');

function attach() {
    var inflector = this;

    String.prototype.singularizePresentVerb = function() {
        return inflector.singularize(this);
    }

    String.prototype.pluralizePresentVerb = function() {
        return inflector.pluralize(this);
    }
}

var VerbInflector = function() {
    this.ambiguous = [
        'will'
    ];

    this.attach = attach;

    this.customPluralForms = [];
    this.customSingularForms = [];
    this.singularForms = new FormSet();
    this.pluralForms = new FormSet();

    this.addIrregular("am", "are");
    this.addIrregular("is", "are");
    this.addIrregular("was", "were");

    this.singularForms.regularForms.push([/ed$/i, 'ed']);
    this.singularForms.regularForms.push([/ss$/i, 'sses']);
    this.singularForms.regularForms.push([/x$/i, 'xes']);
    this.singularForms.regularForms.push([/(h|z|o)$/i, '$1es']);
    this.singularForms.regularForms.push([/$zz/i, 'zzes']);
    this.singularForms.regularForms.push([/([^a|e|i|o|u])y$/i, '$1ies']);
    this.singularForms.regularForms.push([/$/i, 's']);

    this.pluralForms.regularForms.push([/sses$/i, 'ss']);
    this.pluralForms.regularForms.push([/xes$/i, 'x']);
    this.pluralForms.regularForms.push([/([cs])hes$/i, '$1h']);
    this.pluralForms.regularForms.push([/zzes$/i, 'zz']);
    this.pluralForms.regularForms.push([/([^h|z|o|i])es$/i, '$1e']);
    this.pluralForms.regularForms.push([/ies$/i, 'y']);//flies->fly
    this.pluralForms.regularForms.push([/e?s$/i, '']);
};

util.inherits(VerbInflector, SingularPluralInflector);

module.exports = VerbInflector;
<commit_msg>added "has" and "have" to irregular inflections<commit_after>/*
Copyright (c) 2011, Chris Umbel

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

var util = require('util'),
    SingularPluralInflector = require('./singular_plural_inflector'),
    FormSet = require('./form_set');

function attach() {
    var inflector = this;

    String.prototype.singularizePresentVerb = function() {
        return inflector.singularize(this);
    }

    String.prototype.pluralizePresentVerb = function() {
        return inflector.pluralize(this);
    }
}

var VerbInflector = function() {
    this.ambiguous = [
        'will'
    ];

    this.attach = attach;

    this.customPluralForms = [];
    this.customSingularForms = [];
    this.singularForms = new FormSet();
    this.pluralForms = new FormSet();

    this.addIrregular("am", "are");
    this.addIrregular("is", "are");
    this.addIrregular("was", "were");
    this.addIrregular("has", "have");

    this.singularForms.regularForms.push([/ed$/i, 'ed']);
    this.singularForms.regularForms.push([/ss$/i, 'sses']);
    this.singularForms.regularForms.push([/x$/i, 'xes']);
    this.singularForms.regularForms.push([/(h|z|o)$/i, '$1es']);
    this.singularForms.regularForms.push([/$zz/i, 'zzes']);
    this.singularForms.regularForms.push([/([^a|e|i|o|u])y$/i, '$1ies']);
    this.singularForms.regularForms.push([/$/i, 's']);

    this.pluralForms.regularForms.push([/sses$/i, 'ss']);
    this.pluralForms.regularForms.push([/xes$/i, 'x']);
    this.pluralForms.regularForms.push([/([cs])hes$/i, '$1h']);
    this.pluralForms.regularForms.push([/zzes$/i, 'zz']);
    this.pluralForms.regularForms.push([/([^h|z|o|i])es$/i, '$1e']);
    this.pluralForms.regularForms.push([/ies$/i, 'y']);//flies->fly
    this.pluralForms.regularForms.push([/e?s$/i, '']);
};

util.inherits(VerbInflector, SingularPluralInflector);

module.exports = VerbInflector;
<|endoftext|>