<commit_before>'use strict';

var   byline     = require("byline"),
      fs         = require("fs"),
      Polygon    = require (__dirname + "/elements/polygon"),
      Circle     = require (__dirname + "/elements/circle"),
      Text       = require (__dirname + "/elements/text"),
      Point      = require (__dirname + "/elements/point");

var Parser = {};

/**
 * Transform a dxf file into an array where keys are the names of dxf sections
 * @param {String}     dxfPath     "path/of/dxf/file"
 * @param {Function}   callback    Callback function
 */
Parser.toArray = function (dxfPath, callback) {

    var stream         = byline(fs.createReadStream(dxfPath, { encoding: 'utf8' })),
        sectionNameTab = ['HEADER', 'CLASSES', 'TABLES', 'BLOCKS', 'ENTITIES', 'OBJECTS', 'THUMBNAILIMAGE'],
        sectionCur     = '',
        dxfTab         = [],
        section        = false,
        endsec         = false;

    stream.on('data', function (line) {
        if (sectionNameTab.indexOf(line) != -1) {
            sectionCur = line.toLowerCase();
            section = true;
            dxfTab[sectionCur] = [];
            endsec = false;
        }
        else if (section === true) {
            if (line === 'ENDSEC') {
                endsec = true;
                section = false;
                sectionCur = '';
            }
            else if (endsec === false) {
                dxfTab[sectionCur].push(line);
            }
        }
    });
    stream.on('error', function (error) {
        callback(error);
    });
    stream.on('end', function () {
        callback(null, dxfTab);
    });
};

/**
 * Get the polygons in the DXF file
 * @param   {Array}   sectionTab      Polygons are in the entities section
 * @returns {Array}   polygons        An array of polygons
 */
Parser.getPolygons = function (sectionTab) {

    var polygons    = [],
        polygon     = undefined,
        sectionTab  = sectionTab.entities,
        polygonBool = false;

    sectionTab.forEach(function (line, li) {
        if (line === 'LWPOLYLINE') {
            polygonBool = true;
            polygon     = new Polygon();
        }
        else if (polygonBool === true && line === '  8') polygon.setLayer(sectionTab[li + 1].replace(/ {1,}/g,""));
        else if (polygonBool === true && line === ' 90') polygon.setNumberPoints(parseInt(sectionTab[li + 1]));
        else if (polygonBool === true && line === ' 10') {
            var point = new Point();
            point.setX(parseFloat(sectionTab[li + 1]));
            point.setY(parseFloat(sectionTab[li + 3]));
            if (sectionTab[li + 4] === ' 42') point.setBulge(parseFloat(sectionTab[li + 5]));
            polygon.addPoint(point);
        }
        else if (
            polygonBool === true &&
            polygon.numberPoints !== 0 &&
            polygon.layer !== 'defaultLayer' &&
            polygon.points.length === polygon.numberPoints
        ){
            polygonBool = false;
            if(!polygons[polygon.layer]) polygons[polygon.layer] = [polygon];
            else polygons[polygon.layer].push(polygon);
        }
    });
    return polygons;
};

/**
 * Get the circles in the DXF file
 * @param   {Array}     sectionTab          Circles are in the entities section
 * @param   {object}    options             Function options
 * @param   {boolean}   options.toPolygon   true if you want to transforrm directly the circles into polygons
 * @param   {int}       options.nbSides     you can choose the number of sides of polygons from circle (default 20 sides)
 * @returns {Array}     circles             An array of circles
 */
Parser.getCircles = function (sectionTab, options){

    var circles             = [],
        polygonFromCircle   = undefined,
        circle              = undefined,
        sectionTab          = sectionTab.entities,
        toPolygon           = options !== undefined ? options.toPolygon : false,
        nbSides             = options !== undefined ? options.nbSides : 20,
        circleBool          = false;

    sectionTab.forEach(function (line, li){
        if(line === 'CIRCLE'){
            circleBool = true;
            circle = new Circle();
        }
        else if(circleBool === true && line === '  8') circle.setLayer(sectionTab[li+1].replace(/ {1,}/g,""));
        else if(circleBool === true && line === ' 10') {
            var point = new Point();
            point.setX(parseFloat(sectionTab[li + 1]));
            point.setY(parseFloat(sectionTab[li + 3]));
            circle.setPoint(point);
        }
        else if(circleBool === true && line === ' 40') circle.setRayon(sectionTab[li+1]);
        else if(
            circleBool === true &&
            circle.layer !== 'defaultLayer' &&
            circle.point !== undefined &&
            circle.rayon !== 0
        ){
            circleBool = false;
            if (toPolygon){
                polygonFromCircle = circle.toPolygon(nbSides);
                if(!circles[polygonFromCircle.layer]) circles[polygonFromCircle.layer] = [polygonFromCircle];
                else circles[polygonFromCircle.layer].push(polygonFromCircle);
            }
            else{
                if(!circles[circle.layer]) circles[circle.layer] = [circle];
                else circles[circle.layer].push(circle);
            }
        }
    });
    return circles;
};

/**
 * Get the texts in the DXF file
 * @param   {Array}    sectionTab         The texts are in the entities section
 * @returns {Array}    texts              An array of texts
 */
Parser.getTexts = function (sectionTab){

    var texts         = [],
        text          = undefined,
        sectionTab    = sectionTab.entities,
        textBool      = false;

    sectionTab.forEach(function (line, li){
        if(line == 'TEXT' || line == 'MTEXT'){
            textBool = true;
            text = new Text();
        }
        else if(textBool == true && line == '  8') text.setLayer(sectionTab[li+1].replace(/ {1,}/g,""));
        else if(textBool == true && line == ' 10') {
            var point = new Point();
            point.setX(parseFloat(sectionTab[li + 1]));
            point.setY(parseFloat(sectionTab[li + 3]));
            text.setPoint(point);
        }
        else if(textBool == true && line == '  1') {
            text.setContents(sectionTab[li+1]);
            text.contentsParse();
        }
        else if(
            textBool == true &&
            text.layer != 'defaultLayer' &&
            text.contents != 'defaultContents' &&
            text.point != undefined

        ){
            textBool = false;
            if(text.contents !== ''){
                if(!texts[text.layer]) texts[text.layer] = [text];
                else texts[text.layer].push(text);
            } texts.push(text);
        }
    });
    return texts;
};

/**
 * Get Parameters : the origin point of the view, its center point and its rotate angle
 * @param   {Array}    sectionTab     The parameters are in the tables section
 * @returns {Object}   params         originPoint, viewCenterPoint, rotateAngle
 */
Parser.getParameters = function (sectionTab) {

    var params     = {originPoint: undefined, viewCenterPoint: undefined, rotateAngle: undefined},
        sectionTab = sectionTab.tables,
        vPort      = false;

    sectionTab.forEach(function (line, li) {
        if (line == 'AcDbViewportTableRecord') vPort = true;

        else if (vPort == true && line == ' 12'){
            var viewCenterPoint = new Point();
            viewCenterPoint.setX(parseFloat(sectionTab[li + 1]));
            viewCenterPoint.setY(parseFloat(sectionTab[li + 3]));
            params.viewCenterPoint = viewCenterPoint;
        }
        else if (vPort == true && line == ' 13'){
            var originPoint = new Point();
            originPoint.setX(parseFloat(sectionTab[li + 1]));
            originPoint.setY(parseFloat(sectionTab[li + 3]));
            params.originPoint = originPoint;
        }
        else if (vPort == true && line == ' 51') params.rotateAngle = parseFloat(sectionTab[li + 1]);
        else if (
            vPort == true && params.rotateAngle != undefined &&
            params.originPoint != undefined &&
            params.viewCenterPoint != undefined)
        {
            vPort = false;
        }
    });
    return params;
};

/**
 * Get the layers of the appropriate entities in the DXF file
 * @param    {Array}    sectionTab     The specific layers are in the entities section
 * @param    {Array}    tabEnt         You can put "text","polygon" or "circle" in the tabEnt (all by default)
 * @returns  {Array}    layers
 */
Parser.getLayersByEntities = function (sectionTab, tabEnt){

    var layers     = [],
        tabTmp     = tabEnt || 'all',
        ent        = false;

    if(tabTmp === 'all') layers = Parser.getAllLayers(sectionTab);
    else{
        var sectionTab = sectionTab.entities,
            tabEnt     = [];

        if (tabTmp.indexOf("text") !== -1) tabEnt.push("MTEXT", "TEXT");
        if (tabTmp.indexOf("polygon") !== -1) tabEnt.push("LWPOLYLINE");
        if (tabTmp.indexOf("circle") !== -1) tabEnt.push("CIRCLE");

        sectionTab.forEach(function (line, li){
            if(tabEnt.indexOf(line) !== -1) ent = true;
            else if(ent === true && line == '  8'){
                if(layers.indexOf(sectionTab[li+1].replace(/ {1,}/g,"")) === -1) layers.push(sectionTab[li+1].replace(/ {1,}/g,""));
            }
        });
    }
    return layers;
};

/**
 * Get all layers of the DXF file
 * @param   {Array}    sectionTab     All layers are in the tables section
 * @returns {Array}    layer
 */
Parser.getAllLayers = function (sectionTab){

    var layers       = [],
        sectionTab   = sectionTab.tables,
        getLayer     = false;

    sectionTab.forEach(function (line, li){
        if(line === "AcDbLayerTableRecord") getLayer = true;
        else if(getLayer === true && line === '  2'){
            if(layers.indexOf(sectionTab[li+1].replace(/ {1,}/g,"")) === -1){
                layers.push(sectionTab[li+1].replace(/ {1,}/g,""));
                getLayer = false;
            }
        }
    });
    return layers;
};

/**
 * Established a mapping between polygons and texts
 * @param    {object}    polygons
 * @param    {object}    texts
 * @returns  {Array}     mapping
 */
Parser.makeMappings = function (polygons, texts){

    var mappings = [];

    texts.forEach(function (text){
        polygons.forEach(function (polygon, p){
            if(polygon.points !== undefined){
                if(text.point.isInside(polygon.points)){
                    if(!mappings[p]) mappings[p] = {polygon : polygon, texts : [text]};
                    else mappings[p].texts.push(text);
                }
            }
        });
    });
    return mappings;
};

/**
 * Get the polygons of the DXF file which does not have text
 * @param   {Array}  polygons
 * @param   {Array}  texts
 * @returns {Array}  polygonWithoutText
 */
Parser.getPolygonsWithoutText = function (polygons, texts){

    var polygonWithoutText = [];

    polygons.forEach(function(polygon){

        var textInPolygon = [];

        texts.forEach(function (text, t){
            if(polygon.points !== undefined){
                if(text.point.isInside(polygon.points)) textInPolygon.push(t);
            }
        });
        if (textInPolygon.length === 0) polygonWithoutText.push(polygon);
    });
    return polygonWithoutText;
};

/**
 * Get the Texts of the DXF file which does not have polygon
 * @param   {Array}  texts
 * @param   {Array}  polygons
 * @returns {Array}  textsWithoutPolygon
 */
Parser.getTextsWithoutPolygon = function (texts, polygons){

    var textsWithoutPolygon = [];

    texts.forEach(function(text){

        var polygonWithText = [];

        polygons.forEach(function (polygon, p){
            if(polygon.points !== undefined){
                if(text.point.isInside(polygon.points)) polygonWithText.push(p);
            }
        });
        if (polygonWithText.length === 0) textsWithoutPolygon.push(text);
    });
    return textsWithoutPolygon;
};

/**
 * Return the min x,z  and the max x,y of the polygon's array
 * @param   {Array}     polygons
 * @returns {Object}    {min: point, max: point}
 */
Parser.getDimensions = function (polygons){

    var minPoint = new Point(),
        maxPoint = new Point();

    polygons.forEach(function (polygon, p) {
        polygon.points.forEach(function (tpoint, i) {
            minPoint.x = tpoint.x < minPoint.x ? tpoint.x : (i === 0 && p === 0 ? tpoint.x : minPoint.x);
            minPoint.y = tpoint.y < minPoint.y ? tpoint.y : (i === 0 && p === 0 ? tpoint.y : minPoint.y);
            maxPoint.x = tpoint.x > maxPoint.x ? tpoint.x : (i === 0 && p === 0 ? tpoint.x : maxPoint.x);
            maxPoint.y = tpoint.y > maxPoint.y ? tpoint.y : (i === 0 && p === 0 ? tpoint.y : maxPoint.y);
        });
    });
    return {
        'min': minPoint,
        'max': maxPoint
    }
};

module.exports = Parser;
<commit_msg>New polygons, texts, circle returns<commit_after>'use strict';

var   byline     = require("byline"),
      fs         = require("fs"),
      Polygon    = require (__dirname + "/elements/polygon"),
      Circle     = require (__dirname + "/elements/circle"),
      Text       = require (__dirname + "/elements/text"),
      Point      = require (__dirname + "/elements/point");

var Parser = {};

/**
 * Transform a dxf file into an array where keys are the names of dxf sections
 * @param {String}     dxfPath     "path/of/dxf/file"
 * @param {Function}   callback    Callback function
 */
Parser.toArray = function (dxfPath, callback) {

    var stream         = byline(fs.createReadStream(dxfPath, { encoding: 'utf8' })),
        sectionNameTab = ['HEADER', 'CLASSES', 'TABLES', 'BLOCKS', 'ENTITIES', 'OBJECTS', 'THUMBNAILIMAGE'],
        sectionCur     = '',
        dxfTab         = [],
        section        = false,
        endsec         = false;

    stream.on('data', function (line) {
        if (sectionNameTab.indexOf(line) != -1) {
            sectionCur = line.toLowerCase();
            section = true;
            dxfTab[sectionCur] = [];
            endsec = false;
        }
        else if (section === true) {
            if (line === 'ENDSEC') {
                endsec = true;
                section = false;
                sectionCur = '';
            }
            else if (endsec === false) {
                dxfTab[sectionCur].push(line);
            }
        }
    });
    stream.on('error', function (error) {
        callback(error);
    });
    stream.on('end', function () {
        callback(null, dxfTab);
    });
};

/**
 * Get the polygons in the DXF file
 * @param   {Array}   sectionTab      Polygons are in the entities section
 * @returns {Array}   polygons        An array of polygons
 */
Parser.getPolygons = function (sectionTab) {

    var polygons    = [],
        polygon     = undefined,
        sectionTab  = sectionTab.entities,
        polygonBool = false;

    sectionTab.forEach(function (line, li) {
        if (line === 'LWPOLYLINE') {
            polygonBool = true;
            polygon     = new Polygon();
        }
        else if (polygonBool === true && line === '  8') polygon.setLayer(sectionTab[li + 1].replace(/ {1,}/g,""));
        else if (polygonBool === true && line === ' 90') polygon.setNumberPoints(parseInt(sectionTab[li + 1]));
        else if (polygonBool === true && line === ' 10') {
            var point = new Point();
            point.setX(parseFloat(sectionTab[li + 1]));
            point.setY(parseFloat(sectionTab[li + 3]));
            if (sectionTab[li + 4] === ' 42') point.setBulge(parseFloat(sectionTab[li + 5]));
            polygon.addPoint(point);
        }
        else if (
            polygonBool === true &&
            polygon.numberPoints !== 0 &&
            polygon.layer !== 'defaultLayer' &&
            polygon.points.length === polygon.numberPoints
        ){
            polygonBool = false;
            if (!polygons[polygon.layer]) polygons[polygon.layer] = [polygon];
            else polygons[polygon.layer].push(polygon);
        }
    });
    return polygons;
};

/**
 * Get the circles in the DXF file
 * @param   {Array}     sectionTab          Circles are in the entities section
 * @param   {object}    options             Function options
 * @param   {boolean}   options.toPolygon   true if you want to transforrm directly the circles into polygons
 * @param   {int}       options.nbSides     you can choose the number of sides of polygons from circle (default 20 sides)
 * @returns {Array}     circles             An array of circles
 */
Parser.getCircles = function (sectionTab, options){

    var circles             = [],
        polygonFromCircle   = undefined,
        circle              = undefined,
        sectionTab          = sectionTab.entities,
        toPolygon           = options !== undefined ? options.toPolygon : false,
        nbSides             = options !== undefined ? options.nbSides : 20,
        circleBool          = false;

    sectionTab.forEach(function (line, li){
        if(line === 'CIRCLE'){
            circleBool = true;
            circle = new Circle();
        }
        else if(circleBool === true && line === '  8') circle.setLayer(sectionTab[li+1].replace(/ {1,}/g,""));
        else if(circleBool === true && line === ' 10') {
            var point = new Point();
            point.setX(parseFloat(sectionTab[li + 1]));
            point.setY(parseFloat(sectionTab[li + 3]));
            circle.setPoint(point);
        }
        else if(circleBool === true && line === ' 40') circle.setRayon(sectionTab[li+1]);
        else if(
            circleBool === true &&
            circle.layer !== 'defaultLayer' &&
            circle.point !== undefined &&
            circle.rayon !== 0
        ){
            circleBool = false;
            if (toPolygon){
                polygonFromCircle = circle.toPolygon(nbSides);
                if(!circles[polygonFromCircle.layer]) circles[polygonFromCircle.layer] = [polygonFromCircle];
                else circles[polygonFromCircle.layer].push(polygonFromCircle);
            }
            else{
                if(!circles[circle.layer]) circles[circle.layer] = [circle];
                else circles[circle.layer].push(circle);
            }
        }
    });
    return circles;
};

/**
 * Get the texts in the DXF file
 * @param   {Array}    sectionTab         The texts are in the entities section
 * @returns {Array}    texts              An array of texts
 */
Parser.getTexts = function (sectionTab){

    var texts         = [],
        text          = undefined,
        sectionTab    = sectionTab.entities,
        textBool      = false;

    sectionTab.forEach(function (line, li){
        if(line == 'TEXT' || line == 'MTEXT'){
            textBool = true;
            text = new Text();
        }
        else if(textBool == true && line == '  8') text.setLayer(sectionTab[li+1].replace(/ {1,}/g,""));
        else if(textBool == true && line == ' 10') {
            var point = new Point();
            point.setX(parseFloat(sectionTab[li + 1]));
            point.setY(parseFloat(sectionTab[li + 3]));
            text.setPoint(point);
        }
        else if(textBool == true && line == '  1') {
            text.setContents(sectionTab[li+1]);
            text.contentsParse();
        }
        else if(
            textBool == true &&
            text.layer != 'defaultLayer' &&
            text.contents != 'defaultContents' &&
            text.point != undefined

        ){
            textBool = false;
            if(text.contents !== ''){
                if(!texts[text.layer]) texts[text.layer] = [text];
                else texts[text.layer].push(text);
            } texts.push(text);
        }
    });
    return texts;
};

/**
 * Get Parameters : the origin point of the view, its center point and its rotate angle
 * @param   {Array}    sectionTab     The parameters are in the tables section
 * @returns {Object}   params         originPoint, viewCenterPoint, rotateAngle
 */
Parser.getParameters = function (sectionTab) {

    var params     = {originPoint: undefined, viewCenterPoint: undefined, rotateAngle: undefined},
        sectionTab = sectionTab.tables,
        vPort      = false;

    sectionTab.forEach(function (line, li) {
        if (line == 'AcDbViewportTableRecord') vPort = true;

        else if (vPort == true && line == ' 12'){
            var viewCenterPoint = new Point();
            viewCenterPoint.setX(parseFloat(sectionTab[li + 1]));
            viewCenterPoint.setY(parseFloat(sectionTab[li + 3]));
            params.viewCenterPoint = viewCenterPoint;
        }
        else if (vPort == true && line == ' 13'){
            var originPoint = new Point();
            originPoint.setX(parseFloat(sectionTab[li + 1]));
            originPoint.setY(parseFloat(sectionTab[li + 3]));
            params.originPoint = originPoint;
        }
        else if (vPort == true && line == ' 51') params.rotateAngle = parseFloat(sectionTab[li + 1]);
        else if (
            vPort == true && params.rotateAngle != undefined &&
            params.originPoint != undefined &&
            params.viewCenterPoint != undefined)
        {
            vPort = false;
        }
    });
    return params;
};

/**
 * Get the layers of the appropriate entities in the DXF file
 * @param    {Array}    sectionTab     The specific layers are in the entities section
 * @param    {Array}    tabEnt         You can put "text","polygon" or "circle" in the tabEnt (all by default)
 * @returns  {Array}    layers
 */
Parser.getLayersByEntities = function (sectionTab, tabEnt){

    var layers     = [],
        tabTmp     = tabEnt || 'all',
        ent        = false;

    if(tabTmp === 'all') layers = Parser.getAllLayers(sectionTab);
    else{
        var sectionTab = sectionTab.entities,
            tabEnt     = [];

        if (tabTmp.indexOf("text") !== -1) tabEnt.push("MTEXT", "TEXT");
        if (tabTmp.indexOf("polygon") !== -1) tabEnt.push("LWPOLYLINE");
        if (tabTmp.indexOf("circle") !== -1) tabEnt.push("CIRCLE");

        sectionTab.forEach(function (line, li){
            if(tabEnt.indexOf(line) !== -1) ent = true;
            else if(ent === true && line == '  8'){
                if(layers.indexOf(sectionTab[li+1].replace(/ {1,}/g,"")) === -1) layers.push(sectionTab[li+1].replace(/ {1,}/g,""));
            }
        });
    }
    return layers;
};

/**
 * Get all layers of the DXF file
 * @param   {Array}    sectionTab     All layers are in the tables section
 * @returns {Array}    layer
 */
Parser.getAllLayers = function (sectionTab){

    var layers       = [],
        sectionTab   = sectionTab.tables,
        getLayer     = false;

    sectionTab.forEach(function (line, li){
        if(line === "AcDbLayerTableRecord") getLayer = true;
        else if(getLayer === true && line === '  2'){
            if(layers.indexOf(sectionTab[li+1].replace(/ {1,}/g,"")) === -1){
                layers.push(sectionTab[li+1].replace(/ {1,}/g,""));
                getLayer = false;
            }
        }
    });
    return layers;
};

/**
 * Established a mapping between polygons and texts
 * @param    {object}    polygons
 * @param    {object}    texts
 * @returns  {Array}     mapping
 */
Parser.makeMappings = function (polygons, texts){

    var mappings = [];

    texts.forEach(function (text){
        polygons.forEach(function (polygon, p){
            if(polygon.points !== undefined){
                if(text.point.isInside(polygon.points)){
                    if(!mappings[p]) mappings[p] = {polygon : polygon, texts : [text]};
                    else mappings[p].texts.push(text);
                }
            }
        });
    });
    return mappings;
};

/**
 * Get the polygons of the DXF file which does not have text
 * @param   {Array}  polygons
 * @param   {Array}  texts
 * @returns {Array}  polygonWithoutText
 */
Parser.getPolygonsWithoutText = function (polygons, texts){

    var polygonWithoutText = [];

    polygons.forEach(function(polygon){

        var textInPolygon = [];

        texts.forEach(function (text, t){
            if(polygon.points !== undefined){
                if(text.point.isInside(polygon.points)) textInPolygon.push(t);
            }
        });
        if (textInPolygon.length === 0) polygonWithoutText.push(polygon);
    });
    return polygonWithoutText;
};

/**
 * Get the Texts of the DXF file which does not have polygon
 * @param   {Array}  texts
 * @param   {Array}  polygons
 * @returns {Array}  textsWithoutPolygon
 */
Parser.getTextsWithoutPolygon = function (texts, polygons){

    var textsWithoutPolygon = [];

    texts.forEach(function(text){

        var polygonWithText = [];

        polygons.forEach(function (polygon, p){
            if(polygon.points !== undefined){
                if(text.point.isInside(polygon.points)) polygonWithText.push(p);
            }
        });
        if (polygonWithText.length === 0) textsWithoutPolygon.push(text);
    });
    return textsWithoutPolygon;
};

/**
 * Return the min x,z  and the max x,y of the polygon's array
 * @param   {Array}     polygons
 * @returns {Object}    {min: point, max: point}
 */
Parser.getDimensions = function (polygons){

    var minPoint = new Point(),
        maxPoint = new Point();

    polygons.forEach(function (polygon, p) {
        polygon.points.forEach(function (tpoint, i) {
            minPoint.x = tpoint.x < minPoint.x ? tpoint.x : (i === 0 && p === 0 ? tpoint.x : minPoint.x);
            minPoint.y = tpoint.y < minPoint.y ? tpoint.y : (i === 0 && p === 0 ? tpoint.y : minPoint.y);
            maxPoint.x = tpoint.x > maxPoint.x ? tpoint.x : (i === 0 && p === 0 ? tpoint.x : maxPoint.x);
            maxPoint.y = tpoint.y > maxPoint.y ? tpoint.y : (i === 0 && p === 0 ? tpoint.y : maxPoint.y);
        });
    });
    return {
        'min': minPoint,
        'max': maxPoint
    }
};

module.exports = Parser;
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// Flock - JavaScript Key-Value Cache
//
// In-memory key-value store. Stores entire tree structure in memory.
// Use for complex lookups, as memcache on top of a persistence layer,
// or as an in-memory DHT node.
//
// https://github.com/wwidd/flock
////////////////////////////////////////////////////////////////////////////////
var	exports,

flock = function () {
	var RE_PATH_VALIDATOR = /^(\.{3})*([^\.,]+(\.{1,3}|,))*[^\.,]+$/,
			RE_PATH_SKIPPER = /\.{2,}/,

	// - root: root object for datastore
	flock = function (root) {
		// creating default root object
		if (typeof root === 'undefined') {
			root = {};
		}

		var self = {
			//////////////////////////////
			// Getters, setters

			root: function () {
				return root;
			},

			//////////////////////////////
			// Control

			// retrieves node at the given path
			// - path: string or array representing path
			// returns node on path
			// raises TypeError on invalid path
			get: function (path) {
				var tpath = typeof path === 'object' ? path : flock.resolve(path),
						tmp = root,
						i;

				// walking nodes along path
				for (i = 0; i < tpath.length; i++) {
					tmp = tmp[tpath[i]];
				}

				// returning value on end node
				return tmp;
			},

			// sets node at the given path
			// - path: array representing path
			// - value: value to be set at path
			// returns the node on the input path
			set: function (path, value) {
				var tpath = typeof path === 'object' ? path : flock.resolve(path),
						last = tpath.length - 1,
						key, node, child,
						i;

				if (!tpath.length) {
					throw "flock.set: empty path '" + path + "'";
				}

				// walking nodes along path
				for (i = 0, node = root; i < last; i++) {
					key = tpath[i];
					child = node[key];
					if (typeof child === 'undefined') {
						// creating node
						child = node[key] = {};
					}
					node = child;
				}

				// setting value on end node
				node[tpath[i]] = value;

				return node;
			},

			// removes a datastore element from the given path
			// - path: array representing element path
			// returns
			// - true: elem was present and deleted
			// - false: elem was not present
			unset: function (path) {
				var tpath = typeof path === 'object' ? path.concat([]) : flock.resolve(path),
						key = tpath.pop(),
						parent = self.get(tpath);

				// deleting node when exists
				if (parent && parent.hasOwnProperty(key)) {
					delete parent[key];
					return true;
				} else {
					return false;
				}
			},

			// collects end nodes specified by a wildcard path
			// - path: path to end nodes, may contain wildcards "*"
			// - options:
			//	 - limit: max number of entries to retrieve
			//	 - loopback: traverses loopbacks
			//	 - mode: flock.lookup or flock.array
			multiget: function (path, options) {
				options = options || {};

				var tpath = typeof path === 'object' ? path.concat([]) : flock.resolve(path),
						limit = options.limit || 0,
						loopback = options.loopback || false,
						result = options.mode === flock.lookup ? {} : [],
						stack = options.loopback ? null : [];

				// default case
				if (!tpath.length) {
					return root;
				}

				// collects end nodes
				// must be class level, creating a function on each call
				// to .multiget() may impact performance significantly
				// - obj: node data
				// - i: current position in path
				// - depth: current depth in tree
				(function walk(obj, i, depth) {
					var key, j;

					// detecting loopback
					if (!loopback) {
						for (j = 0; j < depth; j++) {
							if (obj === stack[j]) {
								return;
							}
						}
						// putting current object on the stack
						stack[depth] = obj;
					}

					// processes one node
					// - key: key in object to proceed to
					// returns flag whether to terminate traversal
					function node(key) {
						var last = tpath.length - 1;
						if (i < last) {
							walk(obj[key], i + 1, depth + 1);
						} else {
							if (result instanceof Array) {
								result.push(obj[key]);
							} else {
								result[key] = obj[key];
							}
							if (--limit === 0) {
								return true;
							}
						}
						return false;
					}

					// processing next key in path
					key = tpath[i];
					if (key === '*') {
						// processing wildcard node
						for (key in obj) {
							if (obj.hasOwnProperty(key)) {
								if (node(key)) {
									return;
								}
							}
						}
					} else if ((key === '' || key === '.' || key === null) && typeof obj === 'object') {
						// processing skipper node
						// must be object type as strings have indexes, too
						for (key in obj) {
							if (obj.hasOwnProperty(key)) {
								if (key === tpath[i + 1]) {
									// current key matches next key in path
									// re-walking current object but leving skipper key
									walk(obj, i + 1, depth);
								} else {
									// current key doesn't match next key in path
									// walking next level, but staying on skipper key
									walk(obj[key], i, depth + 1);
								}
							}
						}
					} else if (key instanceof Array) {
						// processing list of nodes
						for (j = 0; j < key.length; j++) {
							if (node(key[j])) {
								return;
							}
						}
					} else {
						// processing single node
						key = tpath[i];
						if (!obj.hasOwnProperty(key) || node(key)) {
							return;
						}
					}
				}(root, 0, 0));

				return result;
			}
		};

		return self;
	};

	//////////////////////////////
	// Static variables

	flock.array = 0;
	flock.lookup = 1;

	//////////////////////////////
	// Static methods

	// returns an array representation of the passed string
	// path format is: keys separated with dots
	// wildcards:
	// - '*': for one level
	// - '.' or '': until the adjacent key in path matches
	// example: 'contacts.smith.*.ancestors...name'
	// 					will get the names of all ancestor names for contacts w/ last name 'smith'
	flock.resolve = function (path) {
		// processing path
		if (typeof path === 'string') {
			// validating path
			if (path.length && !RE_PATH_VALIDATOR.test(path)) {
				throw "flock.resolve: invalid path";
			}

			var keys,
					i, key;

			// splitting along dots
			keys = path.length ? path.replace(RE_PATH_SKIPPER, function (match, offset) {
				return offset ? '..' : '.';
			}).split('.') : [];

			// splitting along commas to form multiple choice keys
			for (i = 0; i < keys.length; i++) {
				key = keys[i];
				if (key.indexOf(',') > -1) {
					keys[i] = key.split(',');
				}
			}

			return keys;
		} else {
			throw "flock.resolve: invalid argument";
		}
	};

	return flock;
}();

(exports || {}).flock = flock;

<commit_msg>option 'undef' added to multiget()<commit_after>////////////////////////////////////////////////////////////////////////////////
// Flock - JavaScript Key-Value Cache
//
// In-memory key-value store. Stores entire tree structure in memory.
// Use for complex lookups, as memcache on top of a persistence layer,
// or as an in-memory DHT node.
//
// https://github.com/wwidd/flock
////////////////////////////////////////////////////////////////////////////////
var	exports,

flock = function () {
	var RE_PATH_VALIDATOR = /^(\.{3})*([^\.,]+(\.{1,3}|,))*[^\.,]+$/,
			RE_PATH_SKIPPER = /\.{2,}/,

	// - root: root object for datastore
	flock = function (root) {
		// creating default root object
		if (typeof root === 'undefined') {
			root = {};
		}

		var self = {
			//////////////////////////////
			// Getters, setters

			root: function () {
				return root;
			},

			//////////////////////////////
			// Control

			// retrieves node at the given path
			// - path: string or array representing path
			// returns node on path
			// raises TypeError on invalid path
			get: function (path) {
				var tpath = typeof path === 'object' ? path : flock.resolve(path),
						tmp = root,
						i;

				// walking nodes along path
				for (i = 0; i < tpath.length; i++) {
					tmp = tmp[tpath[i]];
				}

				// returning value on end node
				return tmp;
			},

			// sets node at the given path
			// - path: array representing path
			// - value: value to be set at path
			// returns the node on the input path
			set: function (path, value) {
				var tpath = typeof path === 'object' ? path : flock.resolve(path),
						last = tpath.length - 1,
						key, node, child,
						i;

				if (!tpath.length) {
					throw "flock.set: empty path '" + path + "'";
				}

				// walking nodes along path
				for (i = 0, node = root; i < last; i++) {
					key = tpath[i];
					child = node[key];
					if (typeof child === 'undefined') {
						// creating node
						child = node[key] = {};
					}
					node = child;
				}

				// setting value on end node
				node[tpath[i]] = value;

				return node;
			},

			// removes a datastore element from the given path
			// - path: array representing element path
			// returns
			// - true: elem was present and deleted
			// - false: elem was not present
			unset: function (path) {
				var tpath = typeof path === 'object' ? path.concat([]) : flock.resolve(path),
						key = tpath.pop(),
						parent = self.get(tpath);

				// deleting node when exists
				if (parent && parent.hasOwnProperty(key)) {
					delete parent[key];
					return true;
				} else {
					return false;
				}
			},

			// collects end nodes specified by a wildcard path
			// - path: path to end nodes, may contain wildcards "*"
			// - options:
			//	 - limit: max number of entries to retrieve, default: unlimited
			//	 - mode: type of return value is Object or Array (flock.lookup/flock.array), default: flock.array
			//	 - loopback: whether to traverse loopbacks, default: false
			//	 - undef: whether to collect undefined entries, default: false
			multiget: function (path, options) {
				options = options || {};

				var tpath = typeof path === 'object' ? path.concat([]) : flock.resolve(path),
						last = tpath.length - 1,
						limit = options.limit || 0,
						loopback = options.loopback || false,
						result = options.mode === flock.lookup ? {} : [],
						stack = options.loopback ? null : [];

				// default case
				if (!tpath.length) {
					return root;
				}

				// collects end nodes
				// must be class level, creating a function on each call
				// to .multiget() may impact performance significantly
				// - obj: node data
				// - i: current position in path
				// - depth: current depth in tree
				(function walk(obj, i, depth) {
					var key, j;

					// detecting loopback
					if (!loopback) {
						for (j = 0; j < depth; j++) {
							if (obj === stack[j]) {
								return;
							}
						}
						// putting current object on the stack
						stack[depth] = obj;
					}

					// processes one node
					// - key: key in object to proceed to
					// returns flag whether to terminate traversal
					function node(key) {
						var value = obj[key];
						if (i < last) {
							walk(value, i + 1, depth + 1);
						} else if (options.undef || typeof value !== 'undefined') {
							if (result instanceof Array) {
								result.push(value);
							} else {
								result[key] = value;
							}
							if (--limit === 0) {
								return true;
							}
						}
						return false;
					}

					// processing next key in path
					key = tpath[i];
					if (key === '*') {
						// processing wildcard node
						for (key in obj) {
							if (obj.hasOwnProperty(key)) {
								if (node(key)) {
									return;
								}
							}
						}
					} else if ((key === '' || key === '.' || key === null) && typeof obj === 'object') {
						// processing skipper node
						// must be object type as strings have indexes, too
						for (key in obj) {
							if (obj.hasOwnProperty(key)) {
								if (key === tpath[i + 1]) {
									// current key matches next key in path
									// re-walking current object but leving skipper key
									walk(obj, i + 1, depth);
								} else {
									// current key doesn't match next key in path
									// walking next level, but staying on skipper key
									walk(obj[key], i, depth + 1);
								}
							}
						}
					} else if (key instanceof Array) {
						// processing list of nodes
						for (j = 0; j < key.length; j++) {
							if (node(key[j])) {
								return;
							}
						}
					} else {
						// processing single node
						key = tpath[i];
						if (!obj.hasOwnProperty(key) || node(key)) {
							return;
						}
					}
				}(root, 0, 0));

				return result;
			}
		};

		return self;
	};

	//////////////////////////////
	// Static variables

	flock.array = 0;
	flock.lookup = 1;

	//////////////////////////////
	// Static methods

	// returns an array representation of the passed string
	// path format is: keys separated with dots
	// wildcards:
	// - '*': for one level
	// - '.' or '': until the adjacent key in path matches
	// example: 'contacts.smith.*.ancestors...name'
	// 					will get the names of all ancestor names for contacts w/ last name 'smith'
	flock.resolve = function (path) {
		// processing path
		if (typeof path === 'string') {
			// validating path
			if (path.length && !RE_PATH_VALIDATOR.test(path)) {
				throw "flock.resolve: invalid path";
			}

			var keys,
					i, key;

			// splitting along dots
			keys = path.length ? path.replace(RE_PATH_SKIPPER, function (match, offset) {
				return offset ? '..' : '.';
			}).split('.') : [];

			// splitting along commas to form multiple choice keys
			for (i = 0; i < keys.length; i++) {
				key = keys[i];
				if (key.indexOf(',') > -1) {
					keys[i] = key.split(',');
				}
			}

			return keys;
		} else {
			throw "flock.resolve: invalid argument";
		}
	};

	return flock;
}();

(exports || {}).flock = flock;

<|endoftext|>


<commit_before>/**
 * BPM for Discord
 * (c) 2015-2016 ByzantineFailure
 *
 * Core BPM code.  This code's requires rely on a lot of Makefile magic.
 * The dependencies are built by the BPM core build process then moved here
 * before the webpack occurs.
 **/

var utils = require('../utils'),
    initOptions = require('../settings/index.js');

var cssMap = {};
cssMap['/emote-classes.css'] = require('raw-loader!./emote-classes.css');
cssMap['/gif-animotes.css'] = require('raw-loader!./gif-animotes.css');
cssMap['/bootstrap.css'] = require('raw-loader!./bootstrap.css');
cssMap['/bpmotes.css'] = require('raw-loader!./bpmotes.css');
cssMap['/combiners-nsfw.css'] = require('raw-loader!./combiners-nsfw.css');
cssMap['/extracss-pure.css'] = require('raw-loader!./extracss-pure.css');
cssMap['/extracss-webkit.css'] = require('raw-loader!./extracss-webkit.css');

function getChatInputTextarea() {
    var sendbox = document.querySelector('.channel-text-area-default > [class*=inner] > textarea');
    if(sendbox.length === 0) {
        return null;
    }
    else {
        return sendbox ;
    }
}

window.addEventListener('bpm_backend_message', function(event) {
    var message = event.data;
    switch(message.method) {
        case 'insert_css':
            var css = cssMap[message.file];
            var node = document.createElement('style');
            node.type = 'text/css';
            node.appendChild(document.createTextNode(css));
            document.head.appendChild(node);
            break;
        //We may consider perhaps making this an option instead.
        case 'insert_emote':
            var chatbox = getChatInputTextarea();
            if(!chatbox) {
                console.log('Cannot add search emote "' + message.emote + '", chat textarea does not exist');
                return;
            }
            chatbox.value = chatbox.value + "[](" + message.emote + ")";
            break;
        case 'init_options':
            var target = message.target;
            initOptions(target);
            break;
        default:
            throw new Error('BPM: unrecognized discord message: ' + message.method, message);
            break;
    }
});

//Require BPM content in last
require('./bpm-resources.js');
require('./pref-setup.js');
require('./background.js');
require('./betterponymotes.js');

<commit_msg>Delete 'value' on textbox before emote insertion (#92)<commit_after>/**
 * BPM for Discord
 * (c) 2015-2016 ByzantineFailure
 *
 * Core BPM code.  This code's requires rely on a lot of Makefile magic.
 * The dependencies are built by the BPM core build process then moved here
 * before the webpack occurs.
 **/

var utils = require('../utils'),
    initOptions = require('../settings/index.js');

var cssMap = {};
cssMap['/emote-classes.css'] = require('raw-loader!./emote-classes.css');
cssMap['/gif-animotes.css'] = require('raw-loader!./gif-animotes.css');
cssMap['/bootstrap.css'] = require('raw-loader!./bootstrap.css');
cssMap['/bpmotes.css'] = require('raw-loader!./bpmotes.css');
cssMap['/combiners-nsfw.css'] = require('raw-loader!./combiners-nsfw.css');
cssMap['/extracss-pure.css'] = require('raw-loader!./extracss-pure.css');
cssMap['/extracss-webkit.css'] = require('raw-loader!./extracss-webkit.css');

function getChatInputTextarea() {
    var sendbox = document.querySelector('.channel-text-area-default > [class*=inner] > textarea');
    if(sendbox.length === 0) {
        return null;
    }
    else {
        return sendbox ;
    }
}

// Shennanigans to get around React's BS.  See the below link for details
// https://github.com/ByzantineFailure/BPM-for-Discord/issues/92
function updateTextarea(node, value) {
    // So, react actually puts its own `setter` for `value` on any node that's
    // part of an input for a react component.  In order to actually get state
    // changes triggered, we have to delete this setter and re-set our value.
    // We are comfortable w/ this because React doesn't make this happen in
    // safari or if a getter/setter already exists anyway.
    delete node.value;
    node.value = value;
    // Trigger an event which bubbles up to React's global event handler so we can
    // make sure we update Discord's internal State.
    const event = new Event('change', { target: node, bubbles: true, cancelable: false });
    node.dispatchEvent(event);
}

window.addEventListener('bpm_backend_message', function(event) {
    var message = event.data;
    switch(message.method) {
        case 'insert_css':
            var css = cssMap[message.file];
            var node = document.createElement('style');
            node.type = 'text/css';
            node.appendChild(document.createTextNode(css));
            document.head.appendChild(node);
            break;
        //We may consider perhaps making this an option instead.
        case 'insert_emote':
            var chatbox = getChatInputTextarea();
            if(!chatbox) {
                console.log('Cannot add search emote "' + message.emote + '", chat textarea does not exist');
                return;
            }
            var newValue = chatbox.value + "[](" + message.emote + ")";
            updateTextarea(chatbox, newValue);
            break;
        case 'init_options':
            var target = message.target;
            initOptions(target);
            break;
        default:
            throw new Error('BPM: unrecognized discord message: ' + message.method, message);
            break;
    }
});

//Require BPM content in last
require('./bpm-resources.js');
require('./pref-setup.js');
require('./background.js');
require('./betterponymotes.js');

<|endoftext|>


<commit_before>const React = require("react");
const { DOM: dom, PropTypes } = React;
const ImPropTypes = require("react-immutable-proptypes");
const { connect } = require("react-redux");
const { bindActionCreators } = require("redux");
const {
  getSelectedSource,
  getSourceTabs,
  getFileSearchState
} = require("../selectors");
const { getFilename, getRawSourceURL, isPretty } = require("../utils/source");
const { isEnabled } = require("devtools-config");
const classnames = require("classnames");
const actions = require("../actions");
const CloseButton = require("./CloseButton");
const PaneToggleButton = require("./PaneToggleButton");
const Svg = require("./utils/Svg");
const Dropdown = React.createFactory(require("./Dropdown"));
const { showMenu, buildMenu } = require("../utils/menu");
const { debounce } = require("lodash");
const { getURL } = require("../utils/sources-tree.js");
require("./SourceTabs.css");
require("./Dropdown.css");

/*
 * Finds the hidden tabs by comparing the tabs' top offset.
 * hidden tabs will have a great top offset.
 *
 * @param sourceTabs Immutable.list
 * @param sourceTabEls HTMLCollection
 *
 * @returns Immutable.list
 */
function getHiddenTabs(sourceTabs, sourceTabEls) {
  sourceTabEls = [].slice.call(sourceTabEls);
  function getTopOffset() {
    const topOffsets = sourceTabEls.map(t => t.getBoundingClientRect().top);
    return Math.min(...topOffsets);
  }

  const tabTopOffset = getTopOffset();
  return sourceTabs.filter((tab, index) => {
    return sourceTabEls[index].getBoundingClientRect().top > tabTopOffset;
  });
}

/**
 * Clipboard function taken from
 * https://dxr.mozilla.org/mozilla-central/source/devtools/shared/platform/content/clipboard.js
 */
function copyToTheClipboard(string) {
  let doCopy = function(e) {
    e.clipboardData.setData("text/plain", string);
    e.preventDefault();
  };

  document.addEventListener("copy", doCopy);
  document.execCommand("copy", false, null);
  document.removeEventListener("copy", doCopy);
}

const SourceTabs = React.createClass({
  propTypes: {
    sourceTabs: ImPropTypes.list,
    selectedSource: ImPropTypes.map,
    selectSource: PropTypes.func.isRequired,
    closeTab: PropTypes.func.isRequired,
    closeTabs: PropTypes.func.isRequired,
    toggleFileSearch: PropTypes.func.isRequired,
    togglePane: PropTypes.func.isRequired,
    showSource: PropTypes.func.isRequired,
    horizontal: PropTypes.bool.isRequired,
    startPanelCollapsed: PropTypes.bool.isRequired,
    endPanelCollapsed: PropTypes.bool.isRequired,
  },

  displayName: "SourceTabs",

  getInitialState() {
    return {
      dropdownShown: false,
      hiddenSourceTabs: null
    };
  },

  componentDidUpdate(prevProps) {
    if (!(prevProps === this.props)) {
      this.updateHiddenSourceTabs();
    }
  },

  componentDidMount() {
    this.updateHiddenSourceTabs();
    window.addEventListener("resize", this.onResize, false);
  },

  componentDidDismount() {
    window.removeEventListener("resize", this.onResize);
  },

  onTabContextMenu(event, tab) {
    event.preventDefault();
    this.showContextMenu(event, tab);
  },

  showContextMenu(e, tab) {
    const { closeTab, closeTabs, sourceTabs, showSource } = this.props;

    const closeTabLabel = L10N.getStr("sourceTabs.closeTab");
    const closeOtherTabsLabel = L10N.getStr("sourceTabs.closeOtherTabs");
    const closeTabsToRightLabel = L10N.getStr("sourceTabs.closeTabsToRight");
    const closeAllTabsLabel = L10N.getStr("sourceTabs.closeAllTabs");

    const tabs = sourceTabs.map(t => t.get("id"));
    const sourceTab = sourceTabs.find(t => t.get("id") == tab);

    const closeTabMenuItem = {
      id: "node-menu-close-tab",
      label: closeTabLabel,
      accesskey: "C",
      disabled: false,
      click: () => closeTab(tab)
    };

    const closeOtherTabsMenuItem = {
      id: "node-menu-close-other-tabs",
      label: closeOtherTabsLabel,
      accesskey: "O",
      disabled: false,
      click: () => closeTabs(tabs.filter(t => t !== tab))
    };

    const closeTabsToRightMenuItem = {
      id: "node-menu-close-tabs-to-right",
      label: closeTabsToRightLabel,
      accesskey: "R",
      disabled: false,
      click: () => {
        const tabIndex = tabs.findIndex(t => t == tab);
        closeTabs(tabs.filter((t, i) => i > tabIndex));
      }
    };

    const closeAllTabsMenuItem = {
      id: "node-menu-close-all-tabs",
      label: closeAllTabsLabel,
      accesskey: "A",
      disabled: false,
      click: () => closeTabs(tabs)
    };

    const showSourceMenuItem = {
      id: "node-menu-show-source",
      label: "Show source",
      accesskey: "s",
      disabled: false,
      click: () => showSource(tab)
    };

    const copySourceUrl = {
      id: "node-menu-close-tabs-to-right",
      label: "Copy Link Address",
      accesskey: "X",
      disabled: false,
      click: () => copyToTheClipboard(sourceTab.get("url"))
    };

    const items = [
      { item: closeTabMenuItem },
      { item: closeOtherTabsMenuItem, hidden: () => tabs.size === 1 },
      { item: closeTabsToRightMenuItem, hidden: () =>
         tabs.some((t, i) => t === tab && (tabs.size - 1) === i) },
      { item: closeAllTabsMenuItem },
    ];

    if (isEnabled("copySource")) {
      items.push({ item: { type: "separator" }});
      items.push({ item: copySourceUrl });
    }

    if (isEnabled("showSource")) {
      items.push({ item: showSourceMenuItem });
    }

    showMenu(e, buildMenu(items));
  },

  onResize: debounce(function() {
    this.updateHiddenSourceTabs();
  }),

  /*
   * Updates the hiddenSourceTabs state, by
   * finding the source tabs who have wrapped and are not on the top row.
   */
  updateHiddenSourceTabs() {
    if (!this.refs.sourceTabs) {
      return;
    }

    const sourceTabs = this.props.sourceTabs;
    const sourceTabEls = this.refs.sourceTabs.children;
    const hiddenSourceTabs = getHiddenTabs(sourceTabs, sourceTabEls);

    this.setState({ hiddenSourceTabs });
  },

  toggleSourcesDropdown(e) {
    this.setState({
      dropdownShown: !this.state.dropdownShown,
    });
  },

  renderDropdownSource(source) {
    const { selectSource } = this.props;
    const filename = getFilename(source.toJS());

    return dom.li({
      key: source.get("id"),
      onClick: () => {
        // const tabIndex = getLastVisibleTabIndex(sourceTabs, sourceTabEls);
        const tabIndex = 0;
        selectSource(source.get("id"), { tabIndex });
      }
    }, filename);
  },

  renderTabs() {
    const sourceTabs = this.props.sourceTabs;
    return dom.div(
      { className: "source-tabs", ref: "sourceTabs" },
      sourceTabs.map(this.renderTab)
    );
  },

  renderTab(source) {
    const { selectedSource, selectSource, closeTab } = this.props;
    const filename = getRawSourceURL(getURL(source.get("url")).filename);
    const active = source.get("id") == selectedSource.get("id");
    const isPrettyCode = isPretty({ url: source.get("url") });

    function onClickClose(ev) {
      ev.stopPropagation();
      closeTab(source.get("id"));
    }

    return dom.div(
      {
        className: classnames("source-tab", {
          active,
          pretty: isPretty({ url: source.get("url") })
        }),
        key: source.get("id"),
        onClick: () => selectSource(source.get("id")),
        onContextMenu: (e) => this.onTabContextMenu(e, source.get("id")),
        title: getRawSourceURL(source.get("url"))
      },
      isPrettyCode ? Svg("prettyPrint") : null,
      dom.div({ className: "filename" }, filename),
      CloseButton({ handleClick: onClickClose }));
  },

  renderNewButton() {
    return dom.div({
      className: "new-tab-btn",
      onClick: () => this.props.toggleFileSearch(true)
    }, Svg("plus"));
  },

  renderDropdown() {
    const hiddenSourceTabs = this.state.hiddenSourceTabs;
    if (!hiddenSourceTabs || hiddenSourceTabs.size == 0) {
      return dom.div({});
    }

    return Dropdown({
      panel: dom.ul(
        {},
        this.state.hiddenSourceTabs.map(this.renderDropdownSource)
      )
    });
  },

  renderStartPanelToggleButton() {
    return PaneToggleButton({
      position: "start",
      collapsed: !this.props.startPanelCollapsed,
      handleClick: this.props.togglePane,
    });
  },

  renderEndPanelToggleButton() {
    if (!this.props.horizontal) {
      return;
    }

    return PaneToggleButton({
      position: "end",
      collapsed: !this.props.endPanelCollapsed,
      handleClick: this.props.togglePane,
      horizontal: this.props.horizontal
    });
  },

  render() {
    return dom.div({ className: "source-header" },
      this.renderStartPanelToggleButton(),
      this.renderTabs(),
      this.renderNewButton(),
      this.renderDropdown(),
      this.renderEndPanelToggleButton()
    );
  }
});

module.exports = connect(
  state => ({
    selectedSource: getSelectedSource(state),
    sourceTabs: getSourceTabs(state),
    searchOn: getFileSearchState(state)
  }),
  dispatch => bindActionCreators(actions, dispatch)
)(SourceTabs);
<commit_msg>Add a pretty print context menu option to the source tabs.<commit_after>const React = require("react");
const { DOM: dom, PropTypes } = React;
const ImPropTypes = require("react-immutable-proptypes");
const { connect } = require("react-redux");
const { bindActionCreators } = require("redux");
const {
  getSelectedSource,
  getSourceTabs,
  getFileSearchState
} = require("../selectors");
const { getFilename, getRawSourceURL, isPretty } = require("../utils/source");
const { isEnabled } = require("devtools-config");
const classnames = require("classnames");
const actions = require("../actions");
const CloseButton = require("./CloseButton");
const PaneToggleButton = require("./PaneToggleButton");
const Svg = require("./utils/Svg");
const Dropdown = React.createFactory(require("./Dropdown"));
const { showMenu, buildMenu } = require("../utils/menu");
const { debounce } = require("lodash");
const { getURL } = require("../utils/sources-tree.js");
require("./SourceTabs.css");
require("./Dropdown.css");

/*
 * Finds the hidden tabs by comparing the tabs' top offset.
 * hidden tabs will have a great top offset.
 *
 * @param sourceTabs Immutable.list
 * @param sourceTabEls HTMLCollection
 *
 * @returns Immutable.list
 */
function getHiddenTabs(sourceTabs, sourceTabEls) {
  sourceTabEls = [].slice.call(sourceTabEls);
  function getTopOffset() {
    const topOffsets = sourceTabEls.map(t => t.getBoundingClientRect().top);
    return Math.min(...topOffsets);
  }

  const tabTopOffset = getTopOffset();
  return sourceTabs.filter((tab, index) => {
    return sourceTabEls[index].getBoundingClientRect().top > tabTopOffset;
  });
}

/**
 * Clipboard function taken from
 * https://dxr.mozilla.org/mozilla-central/source/devtools/shared/platform/content/clipboard.js
 */
function copyToTheClipboard(string) {
  let doCopy = function(e) {
    e.clipboardData.setData("text/plain", string);
    e.preventDefault();
  };

  document.addEventListener("copy", doCopy);
  document.execCommand("copy", false, null);
  document.removeEventListener("copy", doCopy);
}

const SourceTabs = React.createClass({
  propTypes: {
    sourceTabs: ImPropTypes.list,
    selectedSource: ImPropTypes.map,
    selectSource: PropTypes.func.isRequired,
    closeTab: PropTypes.func.isRequired,
    closeTabs: PropTypes.func.isRequired,
    toggleFileSearch: PropTypes.func.isRequired,
    togglePrettyPrint: PropTypes.func.isRequired,
    togglePane: PropTypes.func.isRequired,
    showSource: PropTypes.func.isRequired,
    horizontal: PropTypes.bool.isRequired,
    startPanelCollapsed: PropTypes.bool.isRequired,
    endPanelCollapsed: PropTypes.bool.isRequired,
  },

  displayName: "SourceTabs",

  getInitialState() {
    return {
      dropdownShown: false,
      hiddenSourceTabs: null
    };
  },

  componentDidUpdate(prevProps) {
    if (!(prevProps === this.props)) {
      this.updateHiddenSourceTabs();
    }
  },

  componentDidMount() {
    this.updateHiddenSourceTabs();
    window.addEventListener("resize", this.onResize, false);
  },

  componentDidDismount() {
    window.removeEventListener("resize", this.onResize);
  },

  onTabContextMenu(event, tab) {
    event.preventDefault();
    this.showContextMenu(event, tab);
  },

  showContextMenu(e, tab) {
    const {
      closeTab,
      closeTabs,
      sourceTabs,
      showSource,
      togglePrettyPrint } = this.props;

    const closeTabLabel = L10N.getStr("sourceTabs.closeTab");
    const closeOtherTabsLabel = L10N.getStr("sourceTabs.closeOtherTabs");
    const closeTabsToRightLabel = L10N.getStr("sourceTabs.closeTabsToRight");
    const closeAllTabsLabel = L10N.getStr("sourceTabs.closeAllTabs");

    const tabs = sourceTabs.map(t => t.get("id"));
    const sourceTab = sourceTabs.find(t => t.get("id") == tab);

    const closeTabMenuItem = {
      id: "node-menu-close-tab",
      label: closeTabLabel,
      accesskey: "C",
      disabled: false,
      click: () => closeTab(tab)
    };

    const closeOtherTabsMenuItem = {
      id: "node-menu-close-other-tabs",
      label: closeOtherTabsLabel,
      accesskey: "O",
      disabled: false,
      click: () => closeTabs(tabs.filter(t => t !== tab))
    };

    const closeTabsToRightMenuItem = {
      id: "node-menu-close-tabs-to-right",
      label: closeTabsToRightLabel,
      accesskey: "R",
      disabled: false,
      click: () => {
        const tabIndex = tabs.findIndex(t => t == tab);
        closeTabs(tabs.filter((t, i) => i > tabIndex));
      }
    };

    const closeAllTabsMenuItem = {
      id: "node-menu-close-all-tabs",
      label: closeAllTabsLabel,
      accesskey: "A",
      disabled: false,
      click: () => closeTabs(tabs)
    };

    const showSourceMenuItem = {
      id: "node-menu-show-source",
      label: "Show source",
      accesskey: "s",
      disabled: false,
      click: () => showSource(tab)
    };

    const copySourceUrl = {
      id: "node-menu-close-tabs-to-right",
      label: "Copy Link Address",
      accesskey: "X",
      disabled: false,
      click: () => copyToTheClipboard(sourceTab.get("url"))
    };

    const prettyPrint = {
      id: "node-menu-pretty-print",
      label: "Pretty Print Source",
      accesskey: "Z",
      disabled: false,
      click: () => togglePrettyPrint(sourceTab.get("id"))
    };

    const items = [
      { item: closeTabMenuItem },
      { item: closeOtherTabsMenuItem, hidden: () => tabs.size === 1 },
      { item: closeTabsToRightMenuItem, hidden: () =>
         tabs.some((t, i) => t === tab && (tabs.size - 1) === i) },
      { item: closeAllTabsMenuItem },
    ];

    if (isEnabled("copySource")) {
      items.push({ item: { type: "separator" }});
      items.push({ item: copySourceUrl });
    }

    if (isEnabled("showSource")) {
      items.push({ item: showSourceMenuItem });
    }

    items.push({ item: prettyPrint });

    showMenu(e, buildMenu(items));
  },

  onResize: debounce(function() {
    this.updateHiddenSourceTabs();
  }),

  /*
   * Updates the hiddenSourceTabs state, by
   * finding the source tabs who have wrapped and are not on the top row.
   */
  updateHiddenSourceTabs() {
    if (!this.refs.sourceTabs) {
      return;
    }

    const sourceTabs = this.props.sourceTabs;
    const sourceTabEls = this.refs.sourceTabs.children;
    const hiddenSourceTabs = getHiddenTabs(sourceTabs, sourceTabEls);

    this.setState({ hiddenSourceTabs });
  },

  toggleSourcesDropdown(e) {
    this.setState({
      dropdownShown: !this.state.dropdownShown,
    });
  },

  renderDropdownSource(source) {
    const { selectSource } = this.props;
    const filename = getFilename(source.toJS());

    return dom.li({
      key: source.get("id"),
      onClick: () => {
        // const tabIndex = getLastVisibleTabIndex(sourceTabs, sourceTabEls);
        const tabIndex = 0;
        selectSource(source.get("id"), { tabIndex });
      }
    }, filename);
  },

  renderTabs() {
    const sourceTabs = this.props.sourceTabs;
    return dom.div(
      { className: "source-tabs", ref: "sourceTabs" },
      sourceTabs.map(this.renderTab)
    );
  },

  renderTab(source) {
    const { selectedSource, selectSource, closeTab } = this.props;
    const filename = getRawSourceURL(getURL(source.get("url")).filename);
    const active = source.get("id") == selectedSource.get("id");
    const isPrettyCode = isPretty({ url: source.get("url") });

    function onClickClose(ev) {
      ev.stopPropagation();
      closeTab(source.get("id"));
    }

    return dom.div(
      {
        className: classnames("source-tab", {
          active,
          pretty: isPretty({ url: source.get("url") })
        }),
        key: source.get("id"),
        onClick: () => selectSource(source.get("id")),
        onContextMenu: (e) => this.onTabContextMenu(e, source.get("id")),
        title: getRawSourceURL(source.get("url"))
      },
      isPrettyCode ? Svg("prettyPrint") : null,
      dom.div({ className: "filename" }, filename),
      CloseButton({ handleClick: onClickClose }));
  },

  renderNewButton() {
    return dom.div({
      className: "new-tab-btn",
      onClick: () => this.props.toggleFileSearch(true)
    }, Svg("plus"));
  },

  renderDropdown() {
    const hiddenSourceTabs = this.state.hiddenSourceTabs;
    if (!hiddenSourceTabs || hiddenSourceTabs.size == 0) {
      return dom.div({});
    }

    return Dropdown({
      panel: dom.ul(
        {},
        this.state.hiddenSourceTabs.map(this.renderDropdownSource)
      )
    });
  },

  renderStartPanelToggleButton() {
    return PaneToggleButton({
      position: "start",
      collapsed: !this.props.startPanelCollapsed,
      handleClick: this.props.togglePane,
    });
  },

  renderEndPanelToggleButton() {
    if (!this.props.horizontal) {
      return;
    }

    return PaneToggleButton({
      position: "end",
      collapsed: !this.props.endPanelCollapsed,
      handleClick: this.props.togglePane,
      horizontal: this.props.horizontal
    });
  },

  render() {
    return dom.div({ className: "source-header" },
      this.renderStartPanelToggleButton(),
      this.renderTabs(),
      this.renderNewButton(),
      this.renderDropdown(),
      this.renderEndPanelToggleButton()
    );
  }
});

module.exports = connect(
  state => ({
    selectedSource: getSelectedSource(state),
    sourceTabs: getSourceTabs(state),
    searchOn: getFileSearchState(state)
  }),
  dispatch => bindActionCreators(actions, dispatch)
)(SourceTabs);
<|endoftext|>


<commit_before>var http = require('http');
var https = require('https');
var fs = require('fs');
var url = require('url');
var extend = require('extend');
var fileMgr = require('./file-mgr');
var logger = require('./logger');

var cache = {};
var listeners = [];
var newUrls;
var TIMEOUT = 10000;
var MAX_RULES_LEN = 1024 * 64;
var MAX_FILE_LEN = 1024 * 256;
var MAX_INTERVAL = 1000 * 30;
var MIN_INTERVAL = 1000 * 10;
var EEXCEED = 'EEXCEED';
var OPTIONS = { encoding: 'utf8' };
var queue = [];
var queueTimer;
var FILE_RE = /^(?:[a-z]:[\\/]|~?\/)/i;

function getInterval(time) {
  var len = Object.keys(cache).length || 1;
  var interval = Math.max(MIN_INTERVAL, Math.ceil(MAX_INTERVAL / len));
  var minTime = interval - (time > 0 ? time : 0);
  return Math.max(minTime, 1000);
}

function triggerChange(data, body) {
  if (data) {
    body = body && body.trim() || '';
    if (data.body === body) {
      return;
    }
    data.body = body;
  }
  if (newUrls) {
    return;
  }
  newUrls = {};
  listeners.forEach(function(l) {
    l();
  });
  Object.keys(newUrls).forEach(function(url) {
    newUrls[url] = cache[url];
  });
  cache = newUrls;
  newUrls = null;
}

function parseOptions(options) {
  var fullUrl = options.url || options.uri || options;
  if (typeof fullUrl === 'string') {
    options = extend(options, url.parse(fullUrl));
  }
  var maxLength = options.maxLength;
  if (!(maxLength > 0)) {
    options.maxLength = 0;
  }
  options.agent = false;
  options.rejectUnauthorized = false;
  return options;
}

function request(options, callback) {
  options = parseOptions(options);
  var isHttps = options.protocol === 'https:';
  var httpModule = isHttps ? https : http;
  var done, timer, res;
  var body = '';
  var callbackHandler = function(err) {
    clearTimeout(timer);
    err && client.abort();
    if (!done) {
      done = true;
      callback(err, body, res || '');
    }
  };
  var addTimeout = function() {
    clearTimeout(timer);
    timer = setTimeout(function() {
      callbackHandler(new Error('Timeout'));
    }, TIMEOUT);
  };
  addTimeout();
  var maxLength = options.maxLength;
  var client = httpModule.get(options, function(r) {
    res = r;
    res.on('error', callbackHandler);
    res.setEncoding('utf8');
    res.on('data', function(data) {
      body += data;
      addTimeout();
      if (maxLength && body.length > maxLength) {
        var err;
        if (!options.ignoreExceedError) {
          err = new Error('The response body exceeded length limit');
          err.code = EEXCEED;
        }
        callbackHandler(err);
      }
    });
    res.on('end', callbackHandler);
  });
  client.on('error', callbackHandler);
  client.end();
  return client;
}

exports.request = request;

function readFile(url, callback) {
  var data;
  var now = Date.now();
  var execCallback = function() {
    callback(url, Date.now() - now);
  };
  fs.stat(url, function(err, stat) {
    data = cache[url];
    if (!data) {
      return execCallback();
    }
    if (err) {
      if (err.code === 'ENOENT') {
        err = null;
      } else {
        logger.error(url, err.message);
      }
      triggerChange(data);
      data.mtime = null;
      return execCallback();
    }
    if (!stat.isFile()) {
      triggerChange(data);
      data.mtime = null;
      return execCallback();
    }
    var time = stat.mtime.getTime();
    if (time === data.mtime) {
      return execCallback();
    }

    var stream = fs.createReadStream(fileMgr.convertSlash(url), OPTIONS);
    var done;
    var body = '';
    var listener = function(err) {
      if (done) {
        return;
      }
      execCallback();
      if (err && err.code !== 'ENOENT') {
        return;
      }
      done = true;
      data.mtime = time;
      stream.close();
      triggerChange(data, body);
    };
    stream.on('data', function(text) {
      if (done) {
        return;
      }
      body += text;
      if (body.length > MAX_FILE_LEN) {
        listener();
      }
    });
    stream.on('error', listener);
    stream.on('end', listener);
  });
}

function addQueue(url, consumeTime) {
  if (cache[url] && queue.indexOf(url) === -1) {
    queue.push(url);
  }
  var data;
  while(!queueTimer && !data) {
    url = queue.shift();
    if (!url) {
      return;
    }
    data = cache[url];
    if (data) {
      queueTimer = setTimeout(function() {
        queueTimer = null;
        updateBody(url, addQueue);
      }, getInterval(consumeTime));
      return;
    }
  }
}

function updateBody(url, callback, init) {
  var data = cache[url];
  if (!data) {
    return callback && callback();
  }
  if (FILE_RE.test(url)) {
    return readFile(url, addQueue);
  }
  var now = Date.now();
  request({
    url: url,
    maxLength: MAX_RULES_LEN,
    ignoreExceedError: true
  }, function(err, body, res) {
    data = cache[url];
    callback && callback(url, Date.now() - now);
    if (!data) {
      return;
    }
    if (!err && res.statusCode !== 200) {
      err = new Error('Response ' + res.statusCode);
    }
    if (err) {
      logger.error(url, err.message);
      if (init) {
        updateBody(url);
        return;
      }
    }
    addQueue(url);
    if (err) {
      return;
    }
    triggerChange(data, body);
  });
  return true;
}

exports.addChangeListener = function(l) {
  listeners.push(l);
};

exports.add = function(url) {
  var data = cache[url];
  if (!data) {
    cache[url] = data = {body: ''};
    updateBody(url, null, true);
  }
  if (newUrls) {
    newUrls[url] = 1;
  }
  return data.body;
};

exports.clean = function() {
  if (!newUrls && Object.keys(cache).length) {
    triggerChange();
  }
};
<commit_msg>perf: the interval of update local rules<commit_after>var http = require('http');
var https = require('https');
var fs = require('fs');
var url = require('url');
var extend = require('extend');
var fileMgr = require('./file-mgr');
var logger = require('./logger');

var cache = {};
var listeners = [];
var newUrls;
var TIMEOUT = 10000;
var MAX_RULES_LEN = 1024 * 64;
var MAX_FILE_LEN = 1024 * 256;
var MAX_INTERVAL = 1000 * 30;
var MIN_INTERVAL = 1000 * 10;
var EEXCEED = 'EEXCEED';
var OPTIONS = { encoding: 'utf8' };
var queue = [];
var queueTimer;
var FILE_RE = /^(?:[a-z]:[\\/]|~?\/)/i;

function getInterval(time, isLocal) {
  var len = Object.keys(cache).length || 1;
  var interval = isLocal ? 5000 : Math.max(MIN_INTERVAL, Math.ceil(MAX_INTERVAL / len));
  var minTime = interval - (time > 0 ? time : 0);
  return Math.max(minTime, 1000);
}

function triggerChange(data, body) {
  if (data) {
    body = body && body.trim() || '';
    if (data.body === body) {
      return;
    }
    data.body = body;
  }
  if (newUrls) {
    return;
  }
  newUrls = {};
  listeners.forEach(function(l) {
    l();
  });
  Object.keys(newUrls).forEach(function(url) {
    newUrls[url] = cache[url];
  });
  cache = newUrls;
  newUrls = null;
}

function parseOptions(options) {
  var fullUrl = options.url || options.uri || options;
  if (typeof fullUrl === 'string') {
    options = extend(options, url.parse(fullUrl));
  }
  var maxLength = options.maxLength;
  if (!(maxLength > 0)) {
    options.maxLength = 0;
  }
  options.agent = false;
  options.rejectUnauthorized = false;
  return options;
}

function request(options, callback) {
  options = parseOptions(options);
  var isHttps = options.protocol === 'https:';
  var httpModule = isHttps ? https : http;
  var done, timer, res;
  var body = '';
  var callbackHandler = function(err) {
    clearTimeout(timer);
    err && client.abort();
    if (!done) {
      done = true;
      callback(err, body, res || '');
    }
  };
  var addTimeout = function() {
    clearTimeout(timer);
    timer = setTimeout(function() {
      callbackHandler(new Error('Timeout'));
    }, TIMEOUT);
  };
  addTimeout();
  var maxLength = options.maxLength;
  var client = httpModule.get(options, function(r) {
    res = r;
    res.on('error', callbackHandler);
    res.setEncoding('utf8');
    res.on('data', function(data) {
      body += data;
      addTimeout();
      if (maxLength && body.length > maxLength) {
        var err;
        if (!options.ignoreExceedError) {
          err = new Error('The response body exceeded length limit');
          err.code = EEXCEED;
        }
        callbackHandler(err);
      }
    });
    res.on('end', callbackHandler);
  });
  client.on('error', callbackHandler);
  client.end();
  return client;
}

exports.request = request;

function readFile(url, callback) {
  var data;
  var now = Date.now();
  var execCallback = function() {
    callback(url, Date.now() - now);
  };
  fs.stat(url, function(err, stat) {
    data = cache[url];
    if (!data) {
      return execCallback();
    }
    if (err) {
      if (err.code === 'ENOENT') {
        err = null;
      } else {
        logger.error(url, err.message);
      }
      triggerChange(data);
      data.mtime = null;
      return execCallback();
    }
    if (!stat.isFile()) {
      triggerChange(data);
      data.mtime = null;
      return execCallback();
    }
    var time = stat.mtime.getTime();
    if (time === data.mtime) {
      return execCallback();
    }

    var stream = fs.createReadStream(fileMgr.convertSlash(url), OPTIONS);
    var done;
    var body = '';
    var listener = function(err) {
      if (done) {
        return;
      }
      execCallback();
      if (err && err.code !== 'ENOENT') {
        return;
      }
      done = true;
      data.mtime = time;
      stream.close();
      triggerChange(data, body);
    };
    stream.on('data', function(text) {
      if (done) {
        return;
      }
      body += text;
      if (body.length > MAX_FILE_LEN) {
        listener();
      }
    });
    stream.on('error', listener);
    stream.on('end', listener);
  });
}

function addQueue(url, consumeTime) {
  if (cache[url] && queue.indexOf(url) === -1) {
    queue.push(url);
  }
  var data;
  while(!queueTimer && !data) {
    url = queue.shift();
    if (!url) {
      return;
    }
    data = cache[url];
    if (data) {
      queueTimer = setTimeout(function() {
        queueTimer = null;
        updateBody(url, addQueue);
      }, getInterval(consumeTime, data.isLocalUrl || data.isLocaPath));
      return;
    }
  }
}

function updateBody(url, callback, init) {
  var data = cache[url];
  if (!data) {
    return callback && callback();
  }
  if (data.isLocaPath) {
    return readFile(url, addQueue);
  }
  var now = Date.now();
  request({
    url: url,
    maxLength: MAX_RULES_LEN,
    ignoreExceedError: true
  }, function(err, body, res) {
    data = cache[url];
    callback && callback(url, Date.now() - now);
    if (!data) {
      return;
    }
    if (!err && res.statusCode !== 200) {
      err = new Error('Response ' + res.statusCode);
    }
    if (err) {
      logger.error(url, err.message);
      if (init) {
        updateBody(url);
        return;
      }
    }
    addQueue(url);
    if (err) {
      return;
    }
    triggerChange(data, body);
  });
  return true;
}

exports.addChangeListener = function(l) {
  listeners.push(l);
};

exports.add = function(url) {
  var data = cache[url];
  if (!data) {
    cache[url] = data = {
      body: '',
      isLocalUrl: url.indexOf('http://127.0.0.1') === 0,
      isLocaPath: FILE_RE.test(url)
    };
    updateBody(url, null, true);
  }
  if (newUrls) {
    newUrls[url] = 1;
  }
  return data.body;
};

exports.clean = function() {
  if (!newUrls && Object.keys(cache).length) {
    triggerChange();
  }
};
<|endoftext|>


<commit_before>tinyMCE.init({

		// General options
		mode : "exact",
		elements : ${idlist},
		theme : "advanced",
		skin : "o2k7",
		plugins : "brix,safari,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,inlinepopups",
		relative_urls : "false",

		// Theme options
		theme_advanced_buttons1 : "bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect,fontselect,fontsizeselect",
		theme_advanced_buttons2 : "pastetext,pasteword,|,search,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image",
		theme_advanced_buttons3 : "",
		theme_advanced_buttons4 : "",
		theme_advanced_toolbar_location : "top",
		theme_advanced_toolbar_align : "left",
		theme_advanced_statusbar_location : "bottom",
		theme_advanced_resizing : true,

		// Example content CSS (should be your site CSS)
		content_css : "css/content.css",

		// Drop lists for link/image/media/template dialogs
		template_external_list_url : "lists/template_list.js",
		external_link_list_url : "lists/link_list.js",
		external_image_list_url : "lists/image_list.js",
		media_external_list_url : "lists/media_list.js",

		// Replace values for the template plugin
		template_replace_values : {
			username : "Some User",
			staffid : "991234"
		}
	});<commit_msg>expose source button<commit_after>tinyMCE.init({

		// General options
		mode : "exact",
		elements : ${idlist},
		theme : "advanced",
		skin : "o2k7",
		plugins : "brix,safari,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,inlinepopups",
		relative_urls : "false",

		// Theme options
		theme_advanced_buttons1 : "bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect,fontselect,fontsizeselect",
		theme_advanced_buttons2 : "pastetext,pasteword,|,search,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,code",
		theme_advanced_buttons3 : "",
		theme_advanced_buttons4 : "",
		theme_advanced_toolbar_location : "top",
		theme_advanced_toolbar_align : "left",
		theme_advanced_statusbar_location : "bottom",
		theme_advanced_resizing : true,

		// Example content CSS (should be your site CSS)
		content_css : "css/content.css",

		// Drop lists for link/image/media/template dialogs
		template_external_list_url : "lists/template_list.js",
		external_link_list_url : "lists/link_list.js",
		external_image_list_url : "lists/image_list.js",
		media_external_list_url : "lists/media_list.js",

		// Replace values for the template plugin
		template_replace_values : {
			username : "Some User",
			staffid : "991234"
		}
	});<|endoftext|>


<commit_before>/**
 * Uploader
 * @flow
 */
import s3 from 'multer-s3'
import multer from 'multer'

const ALLOWED_MIMETYPES = ['image/jpeg', 'image/png']

function fileFilter (req, file, cb) {
  const hasAllowedMimetype = ALLOWED_MIMETYPES.includes(file.mimetype)
  const hasRequestId = !!req.requestId

  const isAllowed = hasAllowedMimetype && hasRequestId
  cb(null, isAllowed)
}

function filename (req, file, cb) {
  if (!req.requestId) {
    cb(new Error('Must have requestId'))
  } else {
    cb(null, `${req.requestId}/${file.fieldname}`)
  }
}

export default function createUploader (config: Object): Function {
  const uploader = multer({
    fileFilter,
    limit: {
      fileSize: 2e6, // 2MB
      fields: 10,
      files: 5
    },
    storage: s3({
      filename,
      bucket: 'bshed',
      dirname: 'uploads/images',
      ...config
    })
  })
  .fields([{
    name: 'image0',
    maxCount: 1
  }, {
    name: 'image1',
    maxCount: 1
  }, {
    name: 'image2',
    maxCount: 1
  }, {
    name: 'image3',
    maxCount: 1
  }, {
    name: 'image4',
    maxCount: 1
  }])

  return function performUpload (req, res) {
    return new Promise((resolve, reject) => {
      uploader(req, res, err =>
        err ? reject(err) : resolve()
      )
    })
  }
}
<commit_msg>Add extra images to uploader to account for new limit<commit_after>/**
 * Uploader
 * @flow
 */
import s3 from 'multer-s3'
import multer from 'multer'

const ALLOWED_MIMETYPES = ['image/jpeg', 'image/png']

function fileFilter (req, file, cb) {
  const hasAllowedMimetype = ALLOWED_MIMETYPES.includes(file.mimetype)
  const hasRequestId = !!req.requestId

  const isAllowed = hasAllowedMimetype && hasRequestId
  cb(null, isAllowed)
}

function filename (req, file, cb) {
  if (!req.requestId) {
    cb(new Error('Must have requestId'))
  } else {
    cb(null, `${req.requestId}/${file.fieldname}`)
  }
}

export default function createUploader (config: Object): Function {
  const uploader = multer({
    fileFilter,
    limit: {
      fileSize: 2e6, // 2MB
      fields: 10,
      files: 5
    },
    storage: s3({
      filename,
      bucket: 'bshed',
      dirname: 'uploads/images',
      ...config
    })
  })
  .fields([{
    name: 'image0',
    maxCount: 1
  }, {
    name: 'image1',
    maxCount: 1
  }, {
    name: 'image2',
    maxCount: 1
  }, {
    name: 'image3',
    maxCount: 1
  }, {
    name: 'image4',
    maxCount: 1
  }, {
    name: 'image5',
    maxCount: 1
  }, {
    name: 'image6',
    maxCount: 1
  }, {
    name: 'image7',
    maxCount: 1
  }])

  return function performUpload (req, res) {
    return new Promise((resolve, reject) => {
      uploader(req, res, err =>
        err ? reject(err) : resolve()
      )
    })
  }
}
<|endoftext|>


<commit_before>import Ember from 'ember';
import Beat from './beat';
import Sampler from './sampler';

/**
 * Provides classes that are capable of interacting with the Web Audio API's
 * AudioContext.
 *
 * @module Audio
 */

const {
  computed
} = Ember;

/**
 * An instance of this class has a single "sound" (comprised of one or multiple
 * audio sources) but provides methods to play that sound repeatedly, mixed with
 * "rests," in a rhythmic way. An instance of this class behaves very similarly
 * to a "lane" on a drum machine.
 *
 * @class BeatTrack
 * @extends Sampler
 *
 * @todo need a way to stop a BeatTrack once it's started
 * @todo verify that playingTime/duration stuff is still working/thought-out
 */
const BeatTrack = Sampler.extend({

  /**
   * Determines the number of beats in a BeatTrack instance.
   *
   * @property numBeats
   * @type {number}
   */
  numBeats: 4,

  /**
   * If specified, Determines length of time, in milliseconds, before isPlaying
   * and currentTimeIsPlaying are automatically switched back to false after
   * having been switched to true for each beat. 100ms is used by default.
   *
   * @property duration
   * @type {number}
   * @default 100
   */
  duration: 100,

  /**
   * Computed property. An array of Beat instances. The number of Beat instances
   * in the array is always the same as the `numBeats` property.
   *
   * @property beats
   * @type {array|Beat}
   */
  beats: computed('numBeats', 'duration', function() {
    const beats = [];
    const numBeats = this.get('numBeats');

    for (let i = 0; i < numBeats; i++) {
      const beat = Beat.create({
        duration: this.get('duration'),
        _parentPlayIn: this.playIn.bind(this),
        _parentPlay: this.play.bind(this)
      });

      beats.push(beat);
    }

    return beats;
  }),

  /**
   * Calls play on all Beat instances in the beats array.
   *
   * @method playBeats
   *
   * @param {number} bpm The tempo at which the beats should be played.
   *
   * @param noteType {number} The (rhythmic) length of each beat. Fractions
   * are suggested here so that it's easy to reason about. For example, for
   * eighth notes, pass in `1/8`.
   */
  playBeats(bpm, noteType) {
    this._callPlayMethodOnBeats('playIn', bpm, noteType);
  },

  /**
   * Calls play on `active` Beat instances in the beats array. Any beat that
   * is not marked active is effectively a "rest".
   *
   * @method playActiveBeats
   *
   * @param {number} bpm The tempo at which the beats and rests should be played.
   *
   * @param noteType {number} The (rhythmic) length of each beat/rest. Fractions
   * are suggested here so that it's easy to reason about. For example, for
   * eighth notes, pass in `1/8`.
   */
  playActiveBeats(bpm, noteType) {
    this._callPlayMethodOnBeats('ifActivePlayIn', bpm, noteType);
  },

  /**
   * The underlying method behind playBeats and playActiveBeats.
   *
   * @private
   * @method _callPlayMethodOnBeats
   *
   * @param {string} method The method that should be called on each beat.
   *
   * @param {number} bpm The tempo that should be used to calculate the length
   * of a beat/rest.
   *
   * @param noteType {number} The (rhythmic) length of each beat/rest that should
   * be used to calculate the length of a beat/rest in seconds.
   */
  _callPlayMethodOnBeats(method, bpm, noteType=1/4) {
    // http://bradthemad.org/guitar/tempo_explanation.php
    const duration = (240 * noteType) / bpm;
    this.get('beats').map((beat, idx) => beat[method](idx * duration));
  }
});

export default BeatTrack;
<commit_msg>remove addressed todo<commit_after>import Ember from 'ember';
import Beat from './beat';
import Sampler from './sampler';

/**
 * Provides classes that are capable of interacting with the Web Audio API's
 * AudioContext.
 *
 * @module Audio
 */

const {
  computed
} = Ember;

/**
 * An instance of this class has a single "sound" (comprised of one or multiple
 * audio sources) but provides methods to play that sound repeatedly, mixed with
 * "rests," in a rhythmic way. An instance of this class behaves very similarly
 * to a "lane" on a drum machine.
 *
 * @class BeatTrack
 * @extends Sampler
 *
 * @todo need a way to stop a BeatTrack once it's started
 */
const BeatTrack = Sampler.extend({

  /**
   * Determines the number of beats in a BeatTrack instance.
   *
   * @property numBeats
   * @type {number}
   */
  numBeats: 4,

  /**
   * If specified, Determines length of time, in milliseconds, before isPlaying
   * and currentTimeIsPlaying are automatically switched back to false after
   * having been switched to true for each beat. 100ms is used by default.
   *
   * @property duration
   * @type {number}
   * @default 100
   */
  duration: 100,

  /**
   * Computed property. An array of Beat instances. The number of Beat instances
   * in the array is always the same as the `numBeats` property.
   *
   * @property beats
   * @type {array|Beat}
   */
  beats: computed('numBeats', 'duration', function() {
    const beats = [];
    const numBeats = this.get('numBeats');

    for (let i = 0; i < numBeats; i++) {
      const beat = Beat.create({
        duration: this.get('duration'),
        _parentPlayIn: this.playIn.bind(this),
        _parentPlay: this.play.bind(this)
      });

      beats.push(beat);
    }

    return beats;
  }),

  /**
   * Calls play on all Beat instances in the beats array.
   *
   * @method playBeats
   *
   * @param {number} bpm The tempo at which the beats should be played.
   *
   * @param noteType {number} The (rhythmic) length of each beat. Fractions
   * are suggested here so that it's easy to reason about. For example, for
   * eighth notes, pass in `1/8`.
   */
  playBeats(bpm, noteType) {
    this._callPlayMethodOnBeats('playIn', bpm, noteType);
  },

  /**
   * Calls play on `active` Beat instances in the beats array. Any beat that
   * is not marked active is effectively a "rest".
   *
   * @method playActiveBeats
   *
   * @param {number} bpm The tempo at which the beats and rests should be played.
   *
   * @param noteType {number} The (rhythmic) length of each beat/rest. Fractions
   * are suggested here so that it's easy to reason about. For example, for
   * eighth notes, pass in `1/8`.
   */
  playActiveBeats(bpm, noteType) {
    this._callPlayMethodOnBeats('ifActivePlayIn', bpm, noteType);
  },

  /**
   * The underlying method behind playBeats and playActiveBeats.
   *
   * @private
   * @method _callPlayMethodOnBeats
   *
   * @param {string} method The method that should be called on each beat.
   *
   * @param {number} bpm The tempo that should be used to calculate the length
   * of a beat/rest.
   *
   * @param noteType {number} The (rhythmic) length of each beat/rest that should
   * be used to calculate the length of a beat/rest in seconds.
   */
  _callPlayMethodOnBeats(method, bpm, noteType=1/4) {
    // http://bradthemad.org/guitar/tempo_explanation.php
    const duration = (240 * noteType) / bpm;
    this.get('beats').map((beat, idx) => beat[method](idx * duration));
  }
});

export default BeatTrack;
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { connect } from 'react-redux'
import HouseholdSidebar from '../views/HouseholdSidebar'
import HouseholdHero from '../views/HouseholdHero'
import Household from '../views/Household'
import { Route, Switch } from 'react-router-dom'

class HouseholdsContainer extends Component {

  render() {
    return (

          <Route path='/households' render={() => <HouseholdSidebar url={this.props.match.url} households={this.props.households} />} />

          <Switch>
            <Route exact path="/households" component={HouseholdHero} />
            <Route path="/households/:id" render={(props) => <Household {...props} />} />
          </Switch>

    )
  }
}

export default connect((state) => {
  const clients = state.households.filter(h => h.client === true)
  return {
    households: clients
  }
}import { } from 'prettier';
)(HouseholdsContainer)<commit_msg>Clean up HouseholdsContainer<commit_after>import React, { Component } from 'react'
import { connect } from 'react-redux'
import HouseholdSidebar from '../views/HouseholdSidebar'
import HouseholdHero from '../views/HouseholdHero'
import Household from '../views/Household'
import { Route, Switch } from 'react-router-dom'

class HouseholdsContainer extends Component {

  render() {
    return (

          <Route path='/households' render={() => <HouseholdSidebar url={this.props.match.url} households={this.props.households} />} />

          <Switch>
            <Route exact path="/households" component={HouseholdHero} />
            <Route path="/households/:id" render={(props) => <Household {...props} />} />
          </Switch>

    )
  }
}

export default connect((state) => {
  const clients = state.households.filter(h => h.client === true)
  return {
    households: clients
  }
}
)(HouseholdsContainer)<|endoftext|>