<commit_before>// Copyright (c) 2017, Najjar Chedy
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
const _ = require('underscore'),
    logger = require('./logger').logger,
    utils = require('./utils');

const InstructionType_jmp = 0;
const InstructionType_conditionalJmpInst = 1;
const InstructionType_notRetInst = 2;
const InstructionType_retInst = 3;

// deal with typeinfo name for, typeinfo for, vtable for
const isFunctionName = x => x.text.trim().indexOf('.') !== 0 && !x.text.includes(' for ');

const gccX86 = {
    filterData: asmArr => {
        const jmpLabelRegex = /\.L\d+:/;
        const isCode = x => x && x.text && (x.source !== null || jmpLabelRegex.test(x.text) || isFunctionName(x));
        return _.chain(asmArr)
            .map(_.clone)
            .filter(isCode)
            .value();
    },
    isFunctionEnd: x => x[0] !== ' ' && x[0] !== '.' && x.indexOf(':') !== -1,

    isBasicBlockEnd: (inst, prevInst) => inst[0] === '.' || prevInst.includes(' ret'),

    getInstructionType: inst => {
        if (inst.includes('jmp')) return InstructionType_jmp;
        else if (inst.trim()[0] === 'j') return InstructionType_conditionalJmpInst;
        else if (!inst.includes(' ret')) return InstructionType_notRetInst;
        else return InstructionType_retInst;
    },

    extractNodeName: inst => inst.match(/\.L\d+/) + ':',

    isJmpInstruction: x => x.trim()[0] === 'j'
};

const clangX86 = {
    filterData: asmArr => {
        const jmpLabelRegex = /\.LBB\d+_\d+:/;
        const isCode = x => x && x.text && (x.source !== null || jmpLabelRegex.test(x.text) || isFunctionName(x));

        const removeComments = x => {
            const pos = x.text.indexOf('#');
            if (pos !== -1)
                x.text = utils.trimRight(x.text.substring(0, pos));
            return x;
        };

        return _.chain(asmArr)
            .map(_.clone)
            .filter(isCode)
            .map(removeComments)
            .value();
    },
    isFunctionEnd: x => x[0] !== ' ' && x[0] !== '.' && x.indexOf(':') !== -1,

    isBasicBlockEnd: (inst, prevInst) => inst[0] === '.' || prevInst.includes(' ret'),

    getInstructionType: function (inst) {
        if (inst.includes('jmp')) return InstructionType_jmp;
        else if (inst.trim()[0] === 'j') return InstructionType_conditionalJmpInst;
        else if (!inst.includes(' ret')) return InstructionType_notRetInst;
        else return InstructionType_retInst;
    },

    extractNodeName: inst => inst.match(/\.LBB\d+_\d+/) + ':',

    isJmpInstruction: x => x.trim()[0] === 'j'
};

function splitToFunctions(asmArr, isEnd) {
    if (asmArr.length === 0) return [];
    const result = [];
    let first = 1;
    const last = asmArr.length;
    const fnRange = {start: 0, end: null};
    while (first !== last) {
        if (isEnd(asmArr[first].text)) {
            fnRange.end = first;
            result.push(_.clone(fnRange));
            fnRange.start = first;
        }
        ++first;
    }

    fnRange.end = last;
    result.push(_.clone(fnRange));
    return result;
}

function splitToBasicBlocks(asmArr, range, isEnd, isJmp) {
    let first = range.start;
    const last = range.end;
    if (first === last) return [];
    const functionName = asmArr[first].text;
    ++first;

    let rangeBb = {nameId: functionName.substr(0, 50), start: first, end: null, actionPos: []};
    const result = [];

    const newRangeWith = function (oldRange, nameId, start) {
        return {nameId: nameId, start: start, actionPos: [], end: oldRange.end};
    };

    while (first < last) {
        const inst = asmArr[first].text;
        if (isEnd(inst, asmArr[first - 1] ? asmArr[first - 1].text : '')) {
            rangeBb.end = first;
            result.push(_.clone(rangeBb));
            //inst is expected to be .L*: where * in 1,2,...
            rangeBb = newRangeWith(rangeBb, inst, first + 1);
        }
        else if (isJmp(inst)) {
            rangeBb.actionPos.push(first);
        }
        ++first;
    }

    rangeBb.end = last;
    result.push(_.clone(rangeBb));
    return result;
}

function splitToCanonicalBasicBlock(basicBlock) {
    const actionPos = basicBlock.actionPos;
    let actPosSz = actionPos.length;
    if (actionPos[actPosSz - 1] + 1 === basicBlock.end) {
        --actPosSz;
    }

    if (actPosSz === 0)
        return [{
            nameId: basicBlock.nameId,
            start: basicBlock.start,
            end: basicBlock.end
        }];
    else if (actPosSz === 1)
        return [
            {nameId: basicBlock.nameId, start: basicBlock.start, end: actionPos[0] + 1},
            {nameId: basicBlock.nameId + '@' + (actionPos[0] + 1), start: actionPos[0] + 1, end: basicBlock.end}
        ];
    else {
        let first = 0;
        const last = actPosSz;
        const blockName = basicBlock.nameId;
        let tmp = {nameId: blockName, start: basicBlock.start, end: actionPos[first] + 1};
        const result = [];
        result.push(_.clone(tmp));
        while (first !== last - 1) {
            tmp.nameId = blockName + '@' + (actionPos[first] + 1);
            tmp.start = actionPos[first] + 1;
            ++first;
            tmp.end = actionPos[first] + 1;
            result.push(_.clone(tmp));
        }

        tmp = {nameId: blockName + '@' + (actionPos[first] + 1), start: actionPos[first] + 1, end: basicBlock.end};
        result.push(_.clone(tmp));

        return result;

    }
}

function concatInstructions(asmArr, first, last) {
    return _.chain(asmArr.slice(first, last))
        .map(x => x.text.substr(0, 50))
        .value()
        .join('\n');
}

function makeNodes(asms, arrOfCanonicalBasicBlock) {
    return _.map(arrOfCanonicalBasicBlock, e => {
        return {
            id: e.nameId,
            label: `${e.nameId}${e.nameId.indexOf(':') !== -1 ? '' : ':'}\n${concatInstructions(asms, e.start, e.end)}`,
            color: '#99ccff',
            shape: 'box'
        };
    });
}

function makeEdges(asmArr, arrOfCanonicalBasicBlock, rules) {
    const edge = {};
    const edges = [];

    const setEdge = function (edge, sourceNode, targetNode, color) {
        edge.from = sourceNode;
        edge.to = targetNode;
        edge.arrows = 'to';
        edge.color = color;
    };
    const isBasicBlockEnd = rules.isBasicBlockEnd;

    const hasName = function (asmArr, cbb) {
        const asm = asmArr[cbb.end];
        return asm ? isBasicBlockEnd(asm.text, '') : false;
    };

    const generateName = function (name, suffix) {
        const pos = name.indexOf('@');
        if (pos === -1)
            return name + '@' + suffix;

        return name.substring(0, pos + 1) + suffix;
    };
    /* note: x.end-1 possible values:
        jmp .L*, {jne,je,jg,...} .L*, ret/rep ret, call and any other instruction that doesn't change control flow
    */

    _.each(arrOfCanonicalBasicBlock, function (x) {
        let targetNode;
        const lastInst = asmArr[x.end - 1].text;
        switch (rules.getInstructionType(lastInst)) {
            case InstructionType_jmp: {
                //we have to deal only with jmp destination, jmp instruction are always taken.
                //edge from jump inst
                targetNode = rules.extractNodeName(lastInst);
                setEdge(edge, x.nameId, targetNode, 'blue');
                edges.push(_.clone(edge));
                break;
            }
            case InstructionType_conditionalJmpInst: {
                //deal with : branch taken, branch not taken
                targetNode = rules.extractNodeName(lastInst);
                setEdge(edge, x.nameId, targetNode, 'green');
                edges.push(_.clone(edge));
                targetNode = hasName(asmArr, x) ? asmArr[x.end].text : generateName(x.nameId, x.end);
                setEdge(edge, x.nameId, targetNode, 'red');
                edges.push(_.clone(edge));
                logger.debug(edge);
                break;
            }
            case InstructionType_notRetInst: {
                //precondition: lastInst is not last instruction in asmArr (but it is in canonical basic block)
                //note : asmArr[x.end] expected to be .L*:(name of a basic block)
                //       this .L*: has to be exactly after the last instruction in the current canonical basic block
                if (asmArr[x.end]) {
                    targetNode = asmArr[x.end].text;
                    setEdge(edge, x.nameId, targetNode, 'grey');
                    edges.push(_.clone(edge));
                }
                break;
            }
            case InstructionType_retInst:
                break;
        }
    });
    logger.debug(edges);
    return edges;
}

function isLLVMBased(compilerType, version) {
    return version.includes('clang') ||
        version.includes('LLVM') ||
        version.includes('rustc') ||
        compilerType === 'swift' ||
        compilerType === 'zig' ||
        compilerType === 'ispc';
}

function generateCfgStructure(compilerType, version, asmArr) {
    const rules = isLLVMBased(compilerType, version) ? clangX86 : gccX86;
    const code = rules.filterData(asmArr);
    const funcs = splitToFunctions(code, rules.isFunctionEnd);
    if (!funcs.length) {
        return funcs;
    }
    const result = {};
    _.each(funcs, _.bind(function (rng) {
        const basicBlocks = splitToBasicBlocks(code, rng, rules.isBasicBlockEnd, rules.isJmpInstruction);
        let arrOfCanonicalBasicBlock = [];
        _.each(basicBlocks, _.bind(function (elm) {
            const tmp = splitToCanonicalBasicBlock(elm);
            arrOfCanonicalBasicBlock = arrOfCanonicalBasicBlock.concat(tmp);
        }, this));

        result[code[rng.start].text] = {
            nodes: makeNodes(code, arrOfCanonicalBasicBlock),
            edges: makeEdges(code, arrOfCanonicalBasicBlock, rules)
        };
    }, this));

    return result;
}

module.exports.generateStructure = generateCfgStructure;

<commit_msg>Filter non function symbols based on section.<commit_after>// Copyright (c) 2017, Najjar Chedy
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
const _ = require('underscore'),
    logger = require('./logger').logger,
    utils = require('./utils');

const InstructionType_jmp = 0;
const InstructionType_conditionalJmpInst = 1;
const InstructionType_notRetInst = 2;
const InstructionType_retInst = 3;

// deal with typeinfo name for, typeinfo for, vtable for
const isFunctionName = x => x.text.trim().indexOf('.') !== 0;

function getAsmDirective(txt) {
    const pattern = /^\s*(\.[^L ]\S*)/;
    const match = txt.match(pattern);
    if (match !== null) {
        return match[1];
    }
    return null;
}

function filterTextSection(data) {
    var useCurrentSection = true;
    var result = [];
    for (var i in data) {
        var x = data[i];
        var directive = getAsmDirective(x.text);
        if (directive != null) {
            if (directive === ".text" || directive === ".data") {
                useCurrentSection = directive === ".text";
            } else if (directive === ".section") {
                // Only patttern match for now.
                // Extracting section name would require adjusting demangling code
                // as demangled name could contain various symbols including ','.
                useCurrentSection = /\.section\s*"?\.text/.test(x.text);
            } else if (useCurrentSection) {
                result.push(x);
            }
        } else if (useCurrentSection) {
            result.push(x);
        }
    }
    return result;
}

const gccX86 = {
    filterData: asmArr => {
        const jmpLabelRegex = /\.L\d+:/;
        const isCode = x => x && x.text && (x.source !== null || jmpLabelRegex.test(x.text) || isFunctionName(x));
        return _.chain(filterTextSection(asmArr))
            .map(_.clone)
            .filter(isCode)
            .value();
    },
    isFunctionEnd: x => x[0] !== ' ' && x[0] !== '.' && x.indexOf(':') !== -1,

    isBasicBlockEnd: (inst, prevInst) => inst[0] === '.' || prevInst.includes(' ret'),

    getInstructionType: inst => {
        if (inst.includes('jmp')) return InstructionType_jmp;
        else if (inst.trim()[0] === 'j') return InstructionType_conditionalJmpInst;
        else if (!inst.includes(' ret')) return InstructionType_notRetInst;
        else return InstructionType_retInst;
    },

    extractNodeName: inst => inst.match(/\.L\d+/) + ':',

    isJmpInstruction: x => x.trim()[0] === 'j'
};

const clangX86 = {
    filterData: asmArr => {
        const jmpLabelRegex = /\.LBB\d+_\d+:/;
        const isCode = x => x && x.text && (x.source !== null || jmpLabelRegex.test(x.text) || isFunctionName(x));

        const removeComments = x => {
            const pos = x.text.indexOf('#');
            if (pos !== -1)
                x.text = utils.trimRight(x.text.substring(0, pos));
            return x;
        };

        return _.chain(filterTextSection(asmArr))
            .map(_.clone)
            .filter(isCode)
            .map(removeComments)
            .value();
    },
    isFunctionEnd: x => x[0] !== ' ' && x[0] !== '.' && x.indexOf(':') !== -1,

    isBasicBlockEnd: (inst, prevInst) => inst[0] === '.' || prevInst.includes(' ret'),

    getInstructionType: function (inst) {
        if (inst.includes('jmp')) return InstructionType_jmp;
        else if (inst.trim()[0] === 'j') return InstructionType_conditionalJmpInst;
        else if (!inst.includes(' ret')) return InstructionType_notRetInst;
        else return InstructionType_retInst;
    },

    extractNodeName: inst => inst.match(/\.LBB\d+_\d+/) + ':',

    isJmpInstruction: x => x.trim()[0] === 'j'
};

function splitToFunctions(asmArr, isEnd) {
    if (asmArr.length === 0) return [];
    const result = [];
    let first = 1;
    const last = asmArr.length;
    const fnRange = {start: 0, end: null};
    while (first !== last) {
        if (isEnd(asmArr[first].text)) {
            fnRange.end = first;
            result.push(_.clone(fnRange));
            fnRange.start = first;
        }
        ++first;
    }

    fnRange.end = last;
    result.push(_.clone(fnRange));
    return result;
}

function splitToBasicBlocks(asmArr, range, isEnd, isJmp) {
    let first = range.start;
    const last = range.end;
    if (first === last) return [];
    const functionName = asmArr[first].text;
    ++first;

    let rangeBb = {nameId: functionName.substr(0, 50), start: first, end: null, actionPos: []};
    const result = [];

    const newRangeWith = function (oldRange, nameId, start) {
        return {nameId: nameId, start: start, actionPos: [], end: oldRange.end};
    };

    while (first < last) {
        const inst = asmArr[first].text;
        if (isEnd(inst, asmArr[first - 1] ? asmArr[first - 1].text : '')) {
            rangeBb.end = first;
            result.push(_.clone(rangeBb));
            //inst is expected to be .L*: where * in 1,2,...
            rangeBb = newRangeWith(rangeBb, inst, first + 1);
        }
        else if (isJmp(inst)) {
            rangeBb.actionPos.push(first);
        }
        ++first;
    }

    rangeBb.end = last;
    result.push(_.clone(rangeBb));
    return result;
}

function splitToCanonicalBasicBlock(basicBlock) {
    const actionPos = basicBlock.actionPos;
    let actPosSz = actionPos.length;
    if (actionPos[actPosSz - 1] + 1 === basicBlock.end) {
        --actPosSz;
    }

    if (actPosSz === 0)
        return [{
            nameId: basicBlock.nameId,
            start: basicBlock.start,
            end: basicBlock.end
        }];
    else if (actPosSz === 1)
        return [
            {nameId: basicBlock.nameId, start: basicBlock.start, end: actionPos[0] + 1},
            {nameId: basicBlock.nameId + '@' + (actionPos[0] + 1), start: actionPos[0] + 1, end: basicBlock.end}
        ];
    else {
        let first = 0;
        const last = actPosSz;
        const blockName = basicBlock.nameId;
        let tmp = {nameId: blockName, start: basicBlock.start, end: actionPos[first] + 1};
        const result = [];
        result.push(_.clone(tmp));
        while (first !== last - 1) {
            tmp.nameId = blockName + '@' + (actionPos[first] + 1);
            tmp.start = actionPos[first] + 1;
            ++first;
            tmp.end = actionPos[first] + 1;
            result.push(_.clone(tmp));
        }

        tmp = {nameId: blockName + '@' + (actionPos[first] + 1), start: actionPos[first] + 1, end: basicBlock.end};
        result.push(_.clone(tmp));

        return result;

    }
}

function concatInstructions(asmArr, first, last) {
    return _.chain(asmArr.slice(first, last))
        .map(x => x.text.substr(0, 50))
        .value()
        .join('\n');
}

function makeNodes(asms, arrOfCanonicalBasicBlock) {
    return _.map(arrOfCanonicalBasicBlock, e => {
        return {
            id: e.nameId,
            label: `${e.nameId}${e.nameId.indexOf(':') !== -1 ? '' : ':'}\n${concatInstructions(asms, e.start, e.end)}`,
            color: '#99ccff',
            shape: 'box'
        };
    });
}

function makeEdges(asmArr, arrOfCanonicalBasicBlock, rules) {
    const edge = {};
    const edges = [];

    const setEdge = function (edge, sourceNode, targetNode, color) {
        edge.from = sourceNode;
        edge.to = targetNode;
        edge.arrows = 'to';
        edge.color = color;
    };
    const isBasicBlockEnd = rules.isBasicBlockEnd;

    const hasName = function (asmArr, cbb) {
        const asm = asmArr[cbb.end];
        return asm ? isBasicBlockEnd(asm.text, '') : false;
    };

    const generateName = function (name, suffix) {
        const pos = name.indexOf('@');
        if (pos === -1)
            return name + '@' + suffix;

        return name.substring(0, pos + 1) + suffix;
    };
    /* note: x.end-1 possible values:
        jmp .L*, {jne,je,jg,...} .L*, ret/rep ret, call and any other instruction that doesn't change control flow
    */

    _.each(arrOfCanonicalBasicBlock, function (x) {
        let targetNode;
        const lastInst = asmArr[x.end - 1].text;
        switch (rules.getInstructionType(lastInst)) {
            case InstructionType_jmp: {
                //we have to deal only with jmp destination, jmp instruction are always taken.
                //edge from jump inst
                targetNode = rules.extractNodeName(lastInst);
                setEdge(edge, x.nameId, targetNode, 'blue');
                edges.push(_.clone(edge));
                break;
            }
            case InstructionType_conditionalJmpInst: {
                //deal with : branch taken, branch not taken
                targetNode = rules.extractNodeName(lastInst);
                setEdge(edge, x.nameId, targetNode, 'green');
                edges.push(_.clone(edge));
                targetNode = hasName(asmArr, x) ? asmArr[x.end].text : generateName(x.nameId, x.end);
                setEdge(edge, x.nameId, targetNode, 'red');
                edges.push(_.clone(edge));
                logger.debug(edge);
                break;
            }
            case InstructionType_notRetInst: {
                //precondition: lastInst is not last instruction in asmArr (but it is in canonical basic block)
                //note : asmArr[x.end] expected to be .L*:(name of a basic block)
                //       this .L*: has to be exactly after the last instruction in the current canonical basic block
                if (asmArr[x.end]) {
                    targetNode = asmArr[x.end].text;
                    setEdge(edge, x.nameId, targetNode, 'grey');
                    edges.push(_.clone(edge));
                }
                break;
            }
            case InstructionType_retInst:
                break;
        }
    });
    logger.debug(edges);
    return edges;
}

function isLLVMBased(compilerType, version) {
    return version.includes('clang') ||
        version.includes('LLVM') ||
        version.includes('rustc') ||
        compilerType === 'swift' ||
        compilerType === 'zig' ||
        compilerType === 'ispc';
}

function generateCfgStructure(compilerType, version, asmArr) {
    const rules = isLLVMBased(compilerType, version) ? clangX86 : gccX86;
    const code = rules.filterData(asmArr);
    const funcs = splitToFunctions(code, rules.isFunctionEnd);
    if (!funcs.length) {
        return funcs;
    }
    const result = {};
    _.each(funcs, _.bind(function (rng) {
        const basicBlocks = splitToBasicBlocks(code, rng, rules.isBasicBlockEnd, rules.isJmpInstruction);
        let arrOfCanonicalBasicBlock = [];
        _.each(basicBlocks, _.bind(function (elm) {
            const tmp = splitToCanonicalBasicBlock(elm);
            arrOfCanonicalBasicBlock = arrOfCanonicalBasicBlock.concat(tmp);
        }, this));

        result[code[rng.start].text] = {
            nodes: makeNodes(code, arrOfCanonicalBasicBlock),
            edges: makeEdges(code, arrOfCanonicalBasicBlock, rules)
        };
    }, this));

    return result;
}

module.exports.generateStructure = generateCfgStructure;

<|endoftext|>


<commit_before>/**
 * Cylinder-Head-Sector Address
 * @constructor
 * @param {Number} cylinder
 * @param {Number} head
 * @param {Number} sector
 * @return {CHS}
 */
function CHS( cylinder, head, sector ) {

  if( !(this instanceof CHS) )
    return new CHS( cylinder, head, sector )

  if( Buffer.isBuffer( cylinder ) )
    return CHS.fromBuffer( cylinder )

  if( cylinder != null && !Number.isFinite( cylinder ) )
    throw new TypeError( 'Cylinder must be a number' )

  if( head != null && !Number.isFinite( head ) )
    throw new TypeError( 'Head must be a number' )

  if( sector != null && !Number.isFinite( sector ) )
    throw new TypeError( 'Sector must be a number' )

  this.cylinder = cylinder != null ?
    ( cylinder | 0 ) : 1023

  this.head = head != null ?
    ( head | 0 ) : 254

  this.sector = sector != null ?
    ( sector | 0 ) : 63

}

/**
 * Create a CHS Address from a given buffer
 * @param  {Buffer} buffer
 * @return {CHS}
 */
CHS.fromBuffer = function( buffer ) {
  return new CHS().parse( buffer )
}

/**
 * Create a CHS Address from a Logical Block Address (LBA)
 * @param {Number} lba Logical Block Address
 * @param {Number} hpt Heads per Track
 * @param {Number} spt Sectors per Track
 * @return {CHS}
 */
CHS.fromLBA = function( lba, hpt, spt ) {
  return new CHS().setLBA( lba, hpt, spt )
}

/**
 * CHS prototype
 * @type {Object}
 */
CHS.prototype = {

  constructor: CHS,

  /**
   * Get/set values from/to a Buffer
   * @property {Buffer} buffer
   */
  set buffer( value ) { return this.parse( value ) },
  get buffer() { return this.toBuffer() },

  /**
   * Set CHS to a Logical Block Address (LBA)
   * @param {Number} lba Logical Block Address
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   * @return {CHS}
   */
  setLBA: function( lba, hpt, spt ) {
    this.cylinder = ( lba / ( spt * hpt )) | 0
    this.head     = ( lba / spt ) % hpt
    this.sector   = ( lba % spt ) + 1
    return this
  },

  /**
   * Get the Logical Block Address (LBA)
   * corresponding to the given disk geometry
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   * @return {Number} lba
   */
  getLBA: function( hpt, spt ) {
    return ( this.cylinder * hpt + this.head ) *
      spt + ( this.sector - 1 )
  },

  /**
   * @see #getLBA()
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   * @return {Number} lba
   */
  toLBA: function( hpt, spt ) {
    return this.getLBA( hpt, spt )
  },

  /**
   * Parse a given Buffer
   * @param  {Buffer} buffer
   * @return {CHS}
   */
  parse: function( buffer ) {

    if( !Buffer.isBuffer( buffer ) )
      throw new TypeError( 'Value must be a buffer' )

    this.head = buffer[0]

    // Sector in bits 5–0;
    // bits 7–6 are high bits of cylinder
    // 00111111b
    this.sector = buffer[1] & 0x3F

    // Bits 7-6 from sector & bits 7–0 of cylinder
    // 11000000b
    this.cylinder = ( ( buffer[1] & 0xC0 ) << 2 ) | buffer[2]

    return this

  },

  /**
   * Create a Buffer representation of the CHS Address
   * @return {Buffer}
   */
  toBuffer: function() {

    var cylinder = this.cylinder & 0xFF
    var head = this.head
    var sector = (( this.sector >> 2 ) & 0xC0) ^ this.sector

    return new Buffer([ head, sector, cylinder ])

  },

}

// Exports
module.exports = CHS
<commit_msg>Update lib/chs: Add `.clone()` and `.copy()`<commit_after>/**
 * Cylinder-Head-Sector Address
 * @constructor
 * @param {(Number|Buffer)} [cylinder=1023]
 * @param {Number} [head=254]
 * @param {Number} [sector=63]
 */
function CHS( cylinder, head, sector ) {

  if( !(this instanceof CHS) )
    return new CHS( cylinder, head, sector )

  if( Buffer.isBuffer( cylinder ) )
    return CHS.fromBuffer( cylinder )

  if( cylinder != null && !Number.isFinite( cylinder ) )
    throw new TypeError( 'Cylinder must be a number' )

  if( head != null && !Number.isFinite( head ) )
    throw new TypeError( 'Head must be a number' )

  if( sector != null && !Number.isFinite( sector ) )
    throw new TypeError( 'Sector must be a number' )

  /** @type {Number} Cylinder */
  this.cylinder = cylinder != null ?
    ( cylinder | 0 ) : 1023

  /** @type {Number} Head */
  this.head = head != null ?
    ( head | 0 ) : 254

  /** @type {Number} Sector */
  this.sector = sector != null ?
    ( sector | 0 ) : 63

}

/**
 * Create a CHS Address from a given buffer
 * @param  {Buffer} buffer
 * @return {CHS}
 */
CHS.fromBuffer = function( buffer ) {
  return new CHS().parse( buffer )
}

/**
 * Create a CHS Address from a Logical Block Address (LBA)
 * @param {Number} lba Logical Block Address
 * @param {Number} hpt Heads per Track
 * @param {Number} spt Sectors per Track
 * @return {CHS}
 */
CHS.fromLBA = function( lba, hpt, spt ) {
  return new CHS().setLBA( lba, hpt, spt )
}

/**
 * CHS prototype
 * @type {Object}
 * @ignore
 */
CHS.prototype = {

  constructor: CHS,

  /** @type {Buffer} Get/set values from/to a Buffer */
  set buffer( value ) { return this.parse( value ) },
  get buffer() { return this.toBuffer() },

  /**
   * Set CHS to a Logical Block Address (LBA)
   * @param {Number} lba Logical Block Address
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   * @return {CHS}
   */
  setLBA: function( lba, hpt, spt ) {
    this.cylinder = ( lba / ( spt * hpt )) | 0
    this.head     = ( lba / spt ) % hpt
    this.sector   = ( lba % spt ) + 1
    return this
  },

  /**
   * Get the Logical Block Address (LBA)
   * corresponding to the given disk geometry
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   * @return {Number} lba
   */
  getLBA: function( hpt, spt ) {
    return ( this.cylinder * hpt + this.head ) *
      spt + ( this.sector - 1 )
  },

  /**
   * @see #getLBA()
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   * @return {Number} lba
   */
  toLBA: function( hpt, spt ) {
    return this.getLBA( hpt, spt )
  },

  /**
   * Clone the CHS Address
   * @return {CHS}
   */
  clone: function() {
    return new CHS( this.cylinder, this.head, this.sector )
  },

  /**
   * Copy this address to a target address
   * @param {CHS} target
   * @return {CHS}
   */
  copy: function( target ) {

    target.cylinder = this.cylinder
    target.head = this.head
    target.sector = this.sector

    return target

  },

  /**
   * Parse a given Buffer
   * @param  {Buffer} buffer
   * @return {CHS}
   */
  parse: function( buffer ) {

    if( !Buffer.isBuffer( buffer ) )
      throw new TypeError( 'Value must be a buffer' )

    this.head = buffer[0]

    // Sector in bits 5–0;
    // bits 7–6 are high bits of cylinder
    // 00111111b
    this.sector = buffer[1] & 0x3F

    // Bits 7-6 from sector & bits 7–0 of cylinder
    // 11000000b
    this.cylinder = ( ( buffer[1] & 0xC0 ) << 2 ) | buffer[2]

    return this

  },

  /**
   * Create a Buffer representation of the CHS Address
   * @return {Buffer}
   */
  toBuffer: function() {

    var cylinder = this.cylinder & 0xFF
    var head = this.head
    var sector = (( this.sector >> 2 ) & 0xC0) ^ this.sector

    return new Buffer([ head, sector, cylinder ])

  },

}

// Exports
module.exports = CHS
<|endoftext|>


<commit_before>'use strict';

var findup = require('findup-sync');
var spawnSync = require('child_process').spawnSync;
var gruntPath = findup('node_modules/grunt-cli/bin/grunt', {cwd: __dirname});

process.title = 'grunth';

var harmonyFlags = [
    '--es-staging',
    '--harmony_scoping',
//    '--harmony_modules', // We have `require` and ES6 modules are still in flux
//    '--harmony_proxies', // `new Proxy({}, {})` throws
    '--harmony_generators',
    '--harmony_numeric_literals',
    '--harmony_strings',
    '--harmony_arrays',
//    '--harmony_arrow_functions', // No lexical `this`; (function (){return(()=>this)()}.bind(2))() === 2
];

module.exports = function cli(params) {
    spawnSync('node',
        harmonyFlags.concat([gruntPath]).concat(params),
        {
            cwd: process.cwd(),
            stdio: 'inherit',
        }
    );
};
<commit_msg>Log grunth-cli version on `grunth --version`<commit_after>'use strict';

var findup = require('findup-sync');
var spawnSync = require('child_process').spawnSync;
var gruntPath = findup('node_modules/grunt-cli/bin/grunt', {cwd: __dirname});

process.title = 'grunth';

var harmonyFlags = [
    '--es-staging',
    '--harmony_scoping',
//    '--harmony_modules', // We have `require` and ES6 modules are still in flux
//    '--harmony_proxies', // `new Proxy({}, {})` throws
    '--harmony_generators',
    '--harmony_numeric_literals',
    '--harmony_strings',
    '--harmony_arrays',
//    '--harmony_arrow_functions', // No lexical `this`; (function (){return(()=>this)()}.bind(2))() === 2
];

module.exports = function cli(params) {
    if (params.indexOf('--version') !== -1 || params.indexOf('-V') !== -1) {
        console.log('grunth-cli v' + require('../package.json').version);
    }
    spawnSync('node',
        harmonyFlags.concat([gruntPath]).concat(params),
        {
            cwd: process.cwd(),
            stdio: 'inherit',
        }
    );
};
<|endoftext|>


<commit_before>#!/usr/bin/env node
//
// Use in CLI:
//
//   Type "riot" for help.
//
// Use in Node:
//
//   var riot = require('riot/compiler')
//   riot.make({ from: 'foo', to: 'bar', compact: true })
//   riot.watch({ from: 'foo.tag', to: 'bar.js' })
//

var ph = require('path'),
  sh = require('shelljs'),
  chokidar = require('chokidar'),
  compiler = require('./compiler')

var methods = {

  help: function() {
    log([
      '',
      'Builds .tag files to .js',
      '',
      'Options:',
      '',
      '  -h, --help      You\'re reading it',
      '  -v, --version   Print Riot\'s version',
      '  -w, --watch     Watch for changes',
      '  -c, --compact   Minify </p> <p> to </p><p>',
      '  -t, --type      JavaScript pre-processor. Built-in support for: es6, coffeescript, typescript, livescript, none',
      '  -m, --modular   AMD and CommonJS',
      '  --template      HTML pre-processor. Built-in suupport for: jade',
      '  --whitespace    Preserve newlines and whitepace',
      '  --brackets      Change brackets used for expressions. Defaults to { }',
      '  --expr          Run expressions trough parser defined with --type',
      '  --ext           Change tag file extension. Defaults to .tag',
      '',
      'Build a single .tag file:',
      '',
      '  riot foo.tag           To a same named file (foo.js)',
      '  riot foo.tag bar.js    To a different named file (bar.js)',
      '  riot foo.tag bar       To a different dir (bar/foo.js)',
      '',
      'Build all .tag files in a directory:',
      '',
      '  riot foo/bar           To a same directory (foo/**/*.js)',
      '  riot foo/bar baz       To a different directory (baz/**/*.js)',
      '  riot foo/bar baz.js    To a single concatenated file (baz.js)',
      '',
      'Examples for options:',
      '',
      '  riot foo bar',
      '  riot --w foo bar',
      '  riot --watch foo bar',
      '  riot --compact foo bar',
      '  riot foo bar --compact',
      '  riot test.tag --type coffeescript --expr',
      ''
    ].join('\n'))
  },

  version: function() {
    log(require('../package.json').version)
  },

  make: function(opt) {
    init(opt)

    // Generate a list of input/output files

    var tmp = /\/[^.~][^~/]+$/ // skip temporary files (created by editors), e.g. /.name.tag, /~name.tag, /name~.tag
    function find(from) { return sh.find(from).filter(function(f) { return ext.test(f) && tmp.test(f) }) }
    function remap(from, to, base) { return from.map(function(from) {
      return ph.join(to, ph.relative(base, from).replace(ext, '.js'))
    }) }

    var from = opt.flow[0] == 'f' ? [opt.from] : find(opt.from),
      base = opt.flow[0] == 'f' ? ph.dirname(opt.from) : opt.from,
      to = opt.flow[1] == 'f' ? [opt.to] : remap(from, opt.to, base)

    // Create any necessary dirs

    var dirs = {}
    to.map(function(f) { dirs[ph.dirname(f)] = 0 })
    sh.mkdir('-p', Object.keys(dirs))

    // Process files

    function parse(from) { return compiler.compile(sh.cat(from).replace(/^\uFEFF/g, /* strips BOM */''), opt.compiler) }
    function toFile(from, to) {
      console.log(opt)

      from.map(function (path, i) {
        var out = '';

        if (opt.compiler.modular && i === 0) {
          out = "(function(tagger) {\n\
  if (typeof define === 'function' && define.amd) {\n\
    define(['riot'], function(riot) { tagger(riot); });\n\
  } else if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {\n\
    tagger(require('riot'));\n\
  } else {\n\
    tagger(window.riot);\n\
  }\n\
})(function(riot) {\n";
        }

        out += parse(path);

        if (opt.compiler.modular && i === from.length - 1) out += '\n});';

        return out;
      }).join('\n').to(to[0])
    }
    function toDir(from, to) { from.map(function(from, i) { parse(from).to(to[i]) }) }
    ;(opt.flow[1] == 'f' ? toFile : toDir)(from, to)

    // Print what's been done

    from.map(function(src, i) {
      log(toRelative(src) + ' -> ' + toRelative(to[i] || to[0]))
    })

  },

  watch: function(opt) {
    init(opt)

    methods.make(opt)

    var glob = opt.flow[0] == 'f' ? opt.from : ph.join(opt.from, '**/*.'+opt.ext)

    chokidar.watch(glob, { ignoreInitial: true })
      .on('ready', function() { log('Watching ' + toRelative(glob)) })
      .on('all', function(e, path) { methods.make(opt) })

  }
}

var ext

function init(opt) {

  // Run only once

  if (init.called) return
  init.called = true

  if (!opt.ext) opt.ext = 'tag'
  ext = RegExp('\\.' + opt.ext + '$')

  // If no target dir, default to source dir

  if (!opt.to) opt.to = ext.test(opt.from) ? ph.dirname(opt.from) : opt.from

  // Resolve to absolute paths

  opt.from = ph.resolve(opt.from)
  opt.to = ph.resolve(opt.to)

  // Throw if source path doesn't exist

  if (!sh.test('-e', opt.from)) err('Source path does not exist')

  // Determine the input/output types

  opt.flow = (ext.test(opt.from) ? 'f' : 'd') + (/\.js$/.test(opt.to) ? 'f' : 'd')
}

function cli() {

  // Get CLI arguments

  var args = require('minimist')(process.argv.slice(2), {
    boolean: ['watch', 'compact', 'help', 'version', 'whitespace', 'modular'],
    alias: { w: 'watch', c: 'compact', h: 'help', v: 'version', t: 'type', m: 'modular' }
  })

  // Translate args into options hash

  var opts = {
    compiler: {
      compact: args.compact,
      template: args.template,
      type: args.type,
      brackets: args.brackets,
      expr: args.expr,
      modular: args.modular
    },
    ext: args.ext,
    from: args._.shift(),
    to: args._.shift()
  }

  // Call matching method

  var method = Object.keys(methods).filter(function(v) { return args[v] })[0]
    || ( opts.from ? 'make' : 'help' )

  methods[method](opts)

}

function toRelative(path) {
  return path.replace(sh.pwd() + '/', '')
}

function log(msg) {
  if (!log.silent) console.log(msg)
}

function err(msg) {
  msg += '\n'
  if (!log.silent) log(msg) || process.exit(1)
  else throw msg
}

// Run from CLI or as Node module

if (module.parent) {
  module.exports = methods
  log.silent = true
} else cli()
<commit_msg>remove console debug<commit_after>#!/usr/bin/env node
//
// Use in CLI:
//
//   Type "riot" for help.
//
// Use in Node:
//
//   var riot = require('riot/compiler')
//   riot.make({ from: 'foo', to: 'bar', compact: true })
//   riot.watch({ from: 'foo.tag', to: 'bar.js' })
//

var ph = require('path'),
  sh = require('shelljs'),
  chokidar = require('chokidar'),
  compiler = require('./compiler')

var methods = {

  help: function() {
    log([
      '',
      'Builds .tag files to .js',
      '',
      'Options:',
      '',
      '  -h, --help      You\'re reading it',
      '  -v, --version   Print Riot\'s version',
      '  -w, --watch     Watch for changes',
      '  -c, --compact   Minify </p> <p> to </p><p>',
      '  -t, --type      JavaScript pre-processor. Built-in support for: es6, coffeescript, typescript, livescript, none',
      '  -m, --modular   AMD and CommonJS',
      '  --template      HTML pre-processor. Built-in suupport for: jade',
      '  --whitespace    Preserve newlines and whitepace',
      '  --brackets      Change brackets used for expressions. Defaults to { }',
      '  --expr          Run expressions trough parser defined with --type',
      '  --ext           Change tag file extension. Defaults to .tag',
      '',
      'Build a single .tag file:',
      '',
      '  riot foo.tag           To a same named file (foo.js)',
      '  riot foo.tag bar.js    To a different named file (bar.js)',
      '  riot foo.tag bar       To a different dir (bar/foo.js)',
      '',
      'Build all .tag files in a directory:',
      '',
      '  riot foo/bar           To a same directory (foo/**/*.js)',
      '  riot foo/bar baz       To a different directory (baz/**/*.js)',
      '  riot foo/bar baz.js    To a single concatenated file (baz.js)',
      '',
      'Examples for options:',
      '',
      '  riot foo bar',
      '  riot --w foo bar',
      '  riot --watch foo bar',
      '  riot --compact foo bar',
      '  riot foo bar --compact',
      '  riot test.tag --type coffeescript --expr',
      ''
    ].join('\n'))
  },

  version: function() {
    log(require('../package.json').version)
  },

  make: function(opt) {
    init(opt)

    // Generate a list of input/output files

    var tmp = /\/[^.~][^~/]+$/ // skip temporary files (created by editors), e.g. /.name.tag, /~name.tag, /name~.tag
    function find(from) { return sh.find(from).filter(function(f) { return ext.test(f) && tmp.test(f) }) }
    function remap(from, to, base) { return from.map(function(from) {
      return ph.join(to, ph.relative(base, from).replace(ext, '.js'))
    }) }

    var from = opt.flow[0] == 'f' ? [opt.from] : find(opt.from),
      base = opt.flow[0] == 'f' ? ph.dirname(opt.from) : opt.from,
      to = opt.flow[1] == 'f' ? [opt.to] : remap(from, opt.to, base)

    // Create any necessary dirs

    var dirs = {}
    to.map(function(f) { dirs[ph.dirname(f)] = 0 })
    sh.mkdir('-p', Object.keys(dirs))

    // Process files

    function parse(from) { return compiler.compile(sh.cat(from).replace(/^\uFEFF/g, /* strips BOM */''), opt.compiler) }
    function toFile(from, to) {
      from.map(function (path, i) {
        var out = '';

        if (opt.compiler.modular && i === 0) {
          out = "(function(tagger) {\n\
  if (typeof define === 'function' && define.amd) {\n\
    define(['riot'], function(riot) { tagger(riot); });\n\
  } else if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {\n\
    tagger(require('riot'));\n\
  } else {\n\
    tagger(window.riot);\n\
  }\n\
})(function(riot) {\n";
        }

        out += parse(path);

        if (opt.compiler.modular && i === from.length - 1) out += '\n});';

        return out;
      }).join('\n').to(to[0])
    }
    function toDir(from, to) { from.map(function(from, i) { parse(from).to(to[i]) }) }
    ;(opt.flow[1] == 'f' ? toFile : toDir)(from, to)

    // Print what's been done

    from.map(function(src, i) {
      log(toRelative(src) + ' -> ' + toRelative(to[i] || to[0]))
    })

  },

  watch: function(opt) {
    init(opt)

    methods.make(opt)

    var glob = opt.flow[0] == 'f' ? opt.from : ph.join(opt.from, '**/*.'+opt.ext)

    chokidar.watch(glob, { ignoreInitial: true })
      .on('ready', function() { log('Watching ' + toRelative(glob)) })
      .on('all', function(e, path) { methods.make(opt) })

  }
}

var ext

function init(opt) {

  // Run only once

  if (init.called) return
  init.called = true

  if (!opt.ext) opt.ext = 'tag'
  ext = RegExp('\\.' + opt.ext + '$')

  // If no target dir, default to source dir

  if (!opt.to) opt.to = ext.test(opt.from) ? ph.dirname(opt.from) : opt.from

  // Resolve to absolute paths

  opt.from = ph.resolve(opt.from)
  opt.to = ph.resolve(opt.to)

  // Throw if source path doesn't exist

  if (!sh.test('-e', opt.from)) err('Source path does not exist')

  // Determine the input/output types

  opt.flow = (ext.test(opt.from) ? 'f' : 'd') + (/\.js$/.test(opt.to) ? 'f' : 'd')
}

function cli() {

  // Get CLI arguments

  var args = require('minimist')(process.argv.slice(2), {
    boolean: ['watch', 'compact', 'help', 'version', 'whitespace', 'modular'],
    alias: { w: 'watch', c: 'compact', h: 'help', v: 'version', t: 'type', m: 'modular' }
  })

  // Translate args into options hash

  var opts = {
    compiler: {
      compact: args.compact,
      template: args.template,
      type: args.type,
      brackets: args.brackets,
      expr: args.expr,
      modular: args.modular
    },
    ext: args.ext,
    from: args._.shift(),
    to: args._.shift()
  }

  // Call matching method

  var method = Object.keys(methods).filter(function(v) { return args[v] })[0]
    || ( opts.from ? 'make' : 'help' )

  methods[method](opts)

}

function toRelative(path) {
  return path.replace(sh.pwd() + '/', '')
}

function log(msg) {
  if (!log.silent) console.log(msg)
}

function err(msg) {
  msg += '\n'
  if (!log.silent) log(msg) || process.exit(1)
  else throw msg
}

// Run from CLI or as Node module

if (module.parent) {
  module.exports = methods
  log.silent = true
} else cli()
<|endoftext|>


<commit_before>define([
    'jquery',
    'knockout',
    'underscore',
    'viewmodels/widget',
    'arches',
    'mapbox-gl',
    'mapbox-gl-draw',
    'map/mapbox-style',
    'bindings/fadeVisible',
    'bindings/mapbox-gl',
    'bindings/chosen',
    'bindings/ajax-chosen'
], function($, ko, _, WidgetViewModel, arches, mapboxgl, Draw, mapStyle) {
    /**
     * knockout components namespace used in arches
     * @external "ko.components"
     * @see http://knockoutjs.com/documentation/component-binding.html
     */

    /**
     * registers a geometry-widget component for use in forms
     * @function external:"ko.components".geometry-widget
     * @param {object} params
     * @param {boolean} params.value - the value being managed
     * @param {object} params.config -
     * @param {string} params.config.zoom - map zoom level
     * @param {string} params.config.centerX - map center longitude
     * @param {string} params.config.centerY - map center latitude
     * @param {string} params.config.geocoder - the text string id of the geocoder api (currently MapzenGeocoder or BingGeocoder).
     * @param {string} params.config.basemap - the layer name of the selected basemap to be shown in the map
     * @param {string} params.config.geometryTypes - the geometry types available for a user to edit
     * @param {string} params.config.pitch - the pitch of the map in degrees
     * @param {string} params.config.pitch - the bearing of the map in degrees with north at 0
     */
    return ko.components.register('geometry-widget', {
        viewModel: function(params) {
            var self = this;
            params.configKeys = ['zoom', 'centerX', 'centerY', 'geocoder', 'basemap', 'geometryTypes', 'pitch', 'bearing'];
            WidgetViewModel.apply(this, [params]);
            this.selectedBasemap = this.basemap;

            this.mapToolsExpanded = ko.observable(false);
            this.geocodeShimAdded = ko.observable(false);
            this.mapToolsExpanded.subscribe(function(expanded) {
                self.geocodeShimAdded(expanded);
            });

            this.geocoderOptions = ko.observableArray([{
                'id': 'BingGeocoder',
                'name': 'Bing'
            }, {
                'id': 'MapzenGeocoder',
                'name': 'Mapzen'
            }]);

            this.geocodeUrl = arches.urls.geocoder;
            this.geocodePoint = ko.observable();
            this.geocodeResponseOptions = ko.observable();
            this.mapControlPanels = {
                basemaps: ko.observable(false),
                overlays: ko.observable(true),
                maptools: ko.observable(true),
                legend: ko.observable(true)
            };

            this.addInitialLayers = function() {
                var initialLayers = [];
                var overlayLayers = _.sortBy(_.where(arches.mapLayers, {
                    isoverlay: true
                }), 'sortorder').reverse();

                arches.mapLayers.forEach(function(mapLayer) {
                    if (mapLayer.name === this.basemap()) {
                        _.each(mapLayer.layer_definitions, function(layer) {
                            initialLayers.push(layer);
                        });
                    }
                }, this);

                overlayLayers.forEach(function(overlayLayer) {
                    _.each(overlayLayer.layer_definitions, function(layer) {
                        initialLayers.push(layer);
                    });
                })

                initialLayers.push({
                    "id": "geocode-point",
                    "source": "geocode-point",
                    "type": "circle",
                    "paint": {
                        "circle-radius": 5,
                        "circle-color": "red"
                    }
                });

                return initialLayers;
            }

            this.editingToolIcons = {
                Point: 'ion-location',
                Line: 'ion-steam',
                Polygon: 'fa fa-pencil-square-o',
                Delete: 'ion-trash-a'
            }

            this.setupMap = function(map) {

                var self = this;
                var draw = Draw();
                this.map = map;
                this.draw = draw;
                this.map.addControl(draw);
                this.redrawGeocodeLayer = function() {
                    var cacheLayer = map.getLayer('geocode-point');
                    map.removeLayer('geocode-point');
                    map.addLayer(cacheLayer, 'gl-draw-active-line.hot');
                }

                this.selectEditingTool = function(val, e) {
                    switch (val) {
                        case 'Point':
                            draw.changeMode('draw_point');
                            break;
                        case 'Line':
                            draw.changeMode('draw_line_string');
                            break;
                        case 'Polygon':
                            draw.changeMode('draw_polygon');
                            break;
                        default:
                            draw.trash();
                    }
                }

                var overlays =
                    _.each(_.where(arches.mapLayers, {
                        isoverlay: true
                    }), function(overlay) {
                        _.extend(overlay, {
                            opacity: ko.observable(100),
                            color: _.filter(overlay.layer_definitions[0].paint, function(prop, key) {
                                if (key.includes('-color')) {
                                    return prop
                                };
                            })[0],
                            showingTools: ko.observable(false),
                            invisible: ko.observable(false),
                            toggleOverlayTools: function(e) {
                                this.showingTools(!this.showingTools());
                            },
                            toggleOverlayVisibility: function(e) {
                                this.opacity() > 0.0 ? this.opacity(0.0) : this.opacity(100.0);
                            },
                            updateOpacity: function(val) {
                                var shift = val > 0.0 ? 0.0001 : -0.0001;
                                var unsetMap = setTimeout(function(val) {
                                    map.setBearing(map.getBearing() + shift)
                                }, 200); //Layers do not always redraw when toggled
                                var refreshMap = setTimeout(function(val) {
                                    map.setBearing(map.getBearing() + shift * -1)
                                }, 100); // Shifting the map back and forth forces the map to refresh and draw the toggled layers
                                val > 0.0 ? this.invisible(false) : this.invisible(true);
                                this.layer_definitions.forEach(function(layer) {
                                    this.setPaintProperty(layer.id, layer.type + '-opacity', Number(val) / 100.0);
                                }, map)
                            }
                        });
                        overlay.opacity.subscribe(function(value) {
                            overlay.updateOpacity(value);
                        });
                    });

                this.overlays = ko.observableArray(overlays)

                this.basemaps = _.filter(arches.mapLayers, function(baselayer) {
                    return baselayer.isoverlay === false
                });

                this.setBasemap = function(basemapType) {
                    var lowestOverlay = _.last(_.last(overlays).layer_definitions);
                    this.basemaps.forEach(function(basemap) {
                        var self = this;
                        if (basemap.name === basemapType.name) {
                            basemap.layer_definitions.forEach(function(layer) {
                                self.map.addLayer(layer, lowestOverlay.id)
                            })
                        } else {
                            basemap.layer_definitions.forEach(function(layer) {
                                if (self.map.getLayer(layer.id) !== undefined) {
                                    self.map.removeLayer(layer.id);
                                }
                            })
                        }
                    }, this)
                };

                this.updateConfigs = function() {
                    var self = this;
                    return function() {
                        var mapCenter = this.getCenter()
                        var zoom = self.map.getZoom()
                        if (self.zoom() !== zoom) {
                            self.zoom(zoom);
                        };
                        self.centerX(mapCenter.lng);
                        self.centerY(mapCenter.lat);
                        if (Math.abs(this.getBearing()) > 0.01) {
                            self.bearing(this.getBearing());
                        }
                    }
                }

                this.saveGeometries = function() {
                    var self = this;
                    return function() {
                        self.value(self.draw.getAll());
                    }
                }

                this.map.on('moveend', this.updateConfigs());
                this.map.on('draw.create', this.saveGeometries())
                this.map.on('draw.delete', this.saveGeometries())

                this.overlays.subscribe(function(overlays) {
                    var anchorLayer = 'gl-draw-active-line.hot';
                    for (var i = overlays.length; i-- > 0;) { //Using a conventional loop because we want to go backwards over the array without creating a copy
                        overlays[i].layer_definitions.forEach(function(layer) {
                            map.removeLayer(layer.id)
                        })
                    }
                    for (var i = overlays.length; i-- > 0;) {
                        overlays[i].layer_definitions.forEach(function(layer) {
                            map.addLayer(layer, anchorLayer);
                            map.setPaintProperty(layer.id, layer.type + '-opacity', overlays[i].opacity() / 100.0);
                        })
                    }
                    this.redrawGeocodeLayer();
                }, this)

                this.geocodePoint.subscribe(function(val) {
                    var coords = this.geocodeResponseOptions()[val].geometry.coordinates;
                    var point = {
                        "type": "Feature",
                        "properties": {},
                        "geometry": {
                            "type": "Point",
                            "coordinates": coords
                        }
                    }
                    this.map.getSource('geocode-point').setData(point);
                    this.redrawGeocodeLayer();
                    var centerPoint = new mapboxgl.LngLat(coords[0], coords[1])
                    this.map.flyTo({
                        center: centerPoint
                    });
                }, this)

            }

            this.onGeocodeSelection = function(val, e) {
                this.geocoder(e.currentTarget.value)
            }

            this.toggleMapTools = function(data, event) {
                data.mapToolsExpanded(!data.mapToolsExpanded());
            }

            this.toggleMapControlPanels = function(data, event) {
                var panel = data;
                _.each(self.mapControlPanels, function(panelValue, panelName) {
                    panelName === panel ? panelValue(false) : panelValue(true);
                });
            }

            this.moveOverlay = function(overlay, direction) {
                var overlays = ko.utils.unwrapObservable(self.overlays);
                var source = ko.utils.arrayIndexOf(overlays, overlay);
                var target = (direction === 'up') ? source - 1 : source + 1;

                if (target >= 0 && target < overlays.length) {
                    self.overlays.valueWillMutate();

                    overlays.splice(source, 1);
                    overlays.splice(target, 0, overlay);

                    self.overlays.valueHasMutated();
                }
            };

            this.mapStyle = mapStyle;
            this.mapStyle.layers = this.addInitialLayers();

            this.selectBasemap = function(val) {
                self.basemap(val.name)
                self.setBasemap(val);
            }
        },
        template: {
            require: 'text!widget-templates/geometry'
        }
    });
});
<commit_msg>With mapbox-gl upgrade - no longer need to shift map to get overlays to properly refresh<commit_after>define([
    'jquery',
    'knockout',
    'underscore',
    'viewmodels/widget',
    'arches',
    'mapbox-gl',
    'mapbox-gl-draw',
    'map/mapbox-style',
    'bindings/fadeVisible',
    'bindings/mapbox-gl',
    'bindings/chosen',
    'bindings/ajax-chosen'
], function($, ko, _, WidgetViewModel, arches, mapboxgl, Draw, mapStyle) {
    /**
     * knockout components namespace used in arches
     * @external "ko.components"
     * @see http://knockoutjs.com/documentation/component-binding.html
     */

    /**
     * registers a geometry-widget component for use in forms
     * @function external:"ko.components".geometry-widget
     * @param {object} params
     * @param {boolean} params.value - the value being managed
     * @param {object} params.config -
     * @param {string} params.config.zoom - map zoom level
     * @param {string} params.config.centerX - map center longitude
     * @param {string} params.config.centerY - map center latitude
     * @param {string} params.config.geocoder - the text string id of the geocoder api (currently MapzenGeocoder or BingGeocoder).
     * @param {string} params.config.basemap - the layer name of the selected basemap to be shown in the map
     * @param {string} params.config.geometryTypes - the geometry types available for a user to edit
     * @param {string} params.config.pitch - the pitch of the map in degrees
     * @param {string} params.config.pitch - the bearing of the map in degrees with north at 0
     */
    return ko.components.register('geometry-widget', {
        viewModel: function(params) {
            var self = this;
            params.configKeys = ['zoom', 'centerX', 'centerY', 'geocoder', 'basemap', 'geometryTypes', 'pitch', 'bearing'];
            WidgetViewModel.apply(this, [params]);
            this.selectedBasemap = this.basemap;

            this.mapToolsExpanded = ko.observable(false);
            this.geocodeShimAdded = ko.observable(false);
            this.mapToolsExpanded.subscribe(function(expanded) {
                self.geocodeShimAdded(expanded);
            });

            this.geocoderOptions = ko.observableArray([{
                'id': 'BingGeocoder',
                'name': 'Bing'
            }, {
                'id': 'MapzenGeocoder',
                'name': 'Mapzen'
            }]);

            this.geocodeUrl = arches.urls.geocoder;
            this.geocodePoint = ko.observable();
            this.geocodeResponseOptions = ko.observable();
            this.mapControlPanels = {
                basemaps: ko.observable(false),
                overlays: ko.observable(true),
                maptools: ko.observable(true),
                legend: ko.observable(true)
            };

            this.addInitialLayers = function() {
                var initialLayers = [];
                var overlayLayers = _.sortBy(_.where(arches.mapLayers, {
                    isoverlay: true
                }), 'sortorder').reverse();

                arches.mapLayers.forEach(function(mapLayer) {
                    if (mapLayer.name === this.basemap()) {
                        _.each(mapLayer.layer_definitions, function(layer) {
                            initialLayers.push(layer);
                        });
                    }
                }, this);

                overlayLayers.forEach(function(overlayLayer) {
                    _.each(overlayLayer.layer_definitions, function(layer) {
                        initialLayers.push(layer);
                    });
                })

                initialLayers.push({
                    "id": "geocode-point",
                    "source": "geocode-point",
                    "type": "circle",
                    "paint": {
                        "circle-radius": 5,
                        "circle-color": "red"
                    }
                });

                return initialLayers;
            }

            this.editingToolIcons = {
                Point: 'ion-location',
                Line: 'ion-steam',
                Polygon: 'fa fa-pencil-square-o',
                Delete: 'ion-trash-a'
            }

            this.setupMap = function(map) {

                var self = this;
                var draw = Draw();
                this.map = map;
                this.draw = draw;
                this.map.addControl(draw);
                this.redrawGeocodeLayer = function() {
                    var cacheLayer = map.getLayer('geocode-point');
                    map.removeLayer('geocode-point');
                    map.addLayer(cacheLayer, 'gl-draw-active-line.hot');
                }

                this.selectEditingTool = function(val, e) {
                    switch (val) {
                        case 'Point':
                            draw.changeMode('draw_point');
                            break;
                        case 'Line':
                            draw.changeMode('draw_line_string');
                            break;
                        case 'Polygon':
                            draw.changeMode('draw_polygon');
                            break;
                        default:
                            draw.trash();
                    }
                }

                var overlays =
                    _.each(_.where(arches.mapLayers, {
                        isoverlay: true
                    }), function(overlay) {
                        _.extend(overlay, {
                            opacity: ko.observable(100),
                            color: _.filter(overlay.layer_definitions[0].paint, function(prop, key) {
                                if (key.includes('-color')) {
                                    return prop
                                };
                            })[0],
                            showingTools: ko.observable(false),
                            invisible: ko.observable(false),
                            toggleOverlayTools: function(e) {
                                this.showingTools(!this.showingTools());
                            },
                            toggleOverlayVisibility: function(e) {
                                this.opacity() > 0.0 ? this.opacity(0.0) : this.opacity(100.0);
                            },
                            updateOpacity: function(val) {
                                val > 0.0 ? this.invisible(false) : this.invisible(true);
                                this.layer_definitions.forEach(function(layer) {
                                    this.setPaintProperty(layer.id, layer.type + '-opacity', Number(val) / 100.0);
                                }, map)
                            }
                        });
                        overlay.opacity.subscribe(function(value) {
                            overlay.updateOpacity(value);
                        });
                    });

                this.overlays = ko.observableArray(overlays)

                this.basemaps = _.filter(arches.mapLayers, function(baselayer) {
                    return baselayer.isoverlay === false
                });

                this.setBasemap = function(basemapType) {
                    var lowestOverlay = _.last(_.last(overlays).layer_definitions);
                    this.basemaps.forEach(function(basemap) {
                        var self = this;
                        if (basemap.name === basemapType.name) {
                            basemap.layer_definitions.forEach(function(layer) {
                                self.map.addLayer(layer, lowestOverlay.id)
                            })
                        } else {
                            basemap.layer_definitions.forEach(function(layer) {
                                if (self.map.getLayer(layer.id) !== undefined) {
                                    self.map.removeLayer(layer.id);
                                }
                            })
                        }
                    }, this)
                };

                this.updateConfigs = function() {
                    var self = this;
                    return function() {
                        var mapCenter = this.getCenter()
                        var zoom = self.map.getZoom()
                        if (self.zoom() !== zoom) {
                            self.zoom(zoom);
                        };
                        self.centerX(mapCenter.lng);
                        self.centerY(mapCenter.lat);
                        if (Math.abs(this.getBearing()) > 0.01) {
                            self.bearing(this.getBearing());
                        }
                    }
                }

                this.saveGeometries = function() {
                    var self = this;
                    return function() {
                        self.value(self.draw.getAll());
                    }
                }

                this.map.on('moveend', this.updateConfigs());
                this.map.on('draw.create', this.saveGeometries())
                this.map.on('draw.delete', this.saveGeometries())

                this.overlays.subscribe(function(overlays) {
                    var anchorLayer = 'gl-draw-active-line.hot';
                    for (var i = overlays.length; i-- > 0;) { //Using a conventional loop because we want to go backwards over the array without creating a copy
                        overlays[i].layer_definitions.forEach(function(layer) {
                            map.removeLayer(layer.id)
                        })
                    }
                    for (var i = overlays.length; i-- > 0;) {
                        overlays[i].layer_definitions.forEach(function(layer) {
                            map.addLayer(layer, anchorLayer);
                            map.setPaintProperty(layer.id, layer.type + '-opacity', overlays[i].opacity() / 100.0);
                        })
                    }
                    this.redrawGeocodeLayer();
                }, this)

                this.geocodePoint.subscribe(function(val) {
                    var coords = this.geocodeResponseOptions()[val].geometry.coordinates;
                    var point = {
                        "type": "Feature",
                        "properties": {},
                        "geometry": {
                            "type": "Point",
                            "coordinates": coords
                        }
                    }
                    this.map.getSource('geocode-point').setData(point);
                    this.redrawGeocodeLayer();
                    var centerPoint = new mapboxgl.LngLat(coords[0], coords[1])
                    this.map.flyTo({
                        center: centerPoint
                    });
                }, this)

            }

            this.onGeocodeSelection = function(val, e) {
                this.geocoder(e.currentTarget.value)
            }

            this.toggleMapTools = function(data, event) {
                data.mapToolsExpanded(!data.mapToolsExpanded());
            }

            this.toggleMapControlPanels = function(data, event) {
                var panel = data;
                _.each(self.mapControlPanels, function(panelValue, panelName) {
                    panelName === panel ? panelValue(false) : panelValue(true);
                });
            }

            this.moveOverlay = function(overlay, direction) {
                var overlays = ko.utils.unwrapObservable(self.overlays);
                var source = ko.utils.arrayIndexOf(overlays, overlay);
                var target = (direction === 'up') ? source - 1 : source + 1;

                if (target >= 0 && target < overlays.length) {
                    self.overlays.valueWillMutate();

                    overlays.splice(source, 1);
                    overlays.splice(target, 0, overlay);

                    self.overlays.valueHasMutated();
                }
            };

            this.mapStyle = mapStyle;
            this.mapStyle.layers = this.addInitialLayers();

            this.selectBasemap = function(val) {
                self.basemap(val.name)
                self.setBasemap(val);
            }
        },
        template: {
            require: 'text!widget-templates/geometry'
        }
    });
});
<|endoftext|>


<commit_before>/*
 * InterfaceLIFT Wallpaper Auto-Downloader
 * https://github.com/stevenbenner/interfacelift-downloader
 *
 * Copyright (c) 2014 Steven Benner (http://stevenbenner.com/).
 * Released under the MIT License.
 */

'use strict';

var PageScraper = require('./pagescraper');
var Downloader = require('./downloader');
var fs = require('fs');
var path = require('path');
var resPaths = require('./respaths.json');

module.exports = {

  process: function(args) {
    var resolution = '1920x1080';
    var downloadPath = process.cwd();

    if (args.length > 4 || (args.length === 3 && args[2] === '--help')) {
      module.exports.help();
      process.exit(0);
    }

    if (args.length >= 3) {
      if (resPaths[args[2]]) {
        resolution = args[2];
      } else {
        console.log('"' + args[2] + '" is not a known resolution.');
        process.exit(1);
      }
    }

    if (args.length >= 4) {
      downloadPath = path.join(downloadPath, args[3]);
      if (!fs.existsSync(downloadPath)) {
        console.log('The path "' + args[3] + '" does not exist.');
        process.exit(1);
      }
    }

    module.exports.run(resolution, downloadPath);
  },

  run: function(resolution, downloadPath) {
    var startTime = new Date();
    var ps = new PageScraper(resolution);
    var existingFiles = 0;

    // run the page scraper
    ps.on('next', function(pageNumber) {
      console.log('Scanning Page ' + pageNumber + '...');
    });
    ps.on('end', function(downloadLinks) {
      var index = downloadLinks.length;
      var elapesdSeconds = (new Date() - startTime) / 1000;
      var dl = new Downloader(downloadLinks, downloadPath, resolution);

      dl.on('start', function() {
        console.log('Starting Download...\n');
      });
      dl.on('exist', function() {
        existingFiles++;
        index--;
      });
      dl.on('save', function(fileName) {
        console.log('[' + index + '] Saved: ' + fileName);
        index--;
      });
      dl.on('fail', function(fileName) {
        console.log('[' + index + '] Failed: ' + fileName);
        index--;
      });
      dl.on('end', function() {
        var elapesdSeconds = (new Date() - startTime) / 1000;
        console.log('Download competed in ' + elapesdSeconds + ' seconds.');
        console.log('Already had ' + existingFiles + ' images.');
      });

      console.log('Scrape completed in ' + elapesdSeconds + ' seconds. Found ' + index + ' images.');
      dl.start();
    });

    console.log('InterfaceLIFT Wallpaper Auto-Downloader\n');
    console.log([
      'Searching pages for images...',
      '(The download will begin after the page scan finishes.)'
    ].join('\n'));
    ps.start();
  },

  help: function() {
    console.log([
      'Usage: node interfacelift-downloader [resolution] [path]',
      'Resolution is the dimensions of the images you want to search for.',
      'For example: interfacelift-downloader 1920x1080'
    ].join('\n'));
  }

};
<commit_msg>Allow full path to be entered on command line.<commit_after>/*
 * InterfaceLIFT Wallpaper Auto-Downloader
 * https://github.com/stevenbenner/interfacelift-downloader
 *
 * Copyright (c) 2014 Steven Benner (http://stevenbenner.com/).
 * Released under the MIT License.
 */

'use strict';

var PageScraper = require('./pagescraper');
var Downloader = require('./downloader');
var fs = require('fs');
var path = require('path');
var resPaths = require('./respaths.json');

module.exports = {

  process: function(args) {
    var resolution = '1920x1080';
    var downloadPath = process.cwd();

    if (args.length > 4 || (args.length === 3 && args[2] === '--help')) {
      module.exports.help();
      process.exit(0);
    }

    if (args.length >= 3) {
      if (resPaths[args[2]]) {
        resolution = args[2];
      } else {
        console.log('"' + args[2] + '" is not a known resolution.');
        process.exit(1);
      }
    }

    if (args.length >= 4) {
      if (args[3].charAt(0) == "/" || args[3].charAt(0) == "~") {
        downloadPath = args[3];
      } else {
        downloadPath = path.join(downloadPath, args[3]);
      }

      if (!fs.existsSync(downloadPath)) {
        console.log('The path "' + args[3] + '" does not exist.');
        process.exit(1);
      }
    }

    module.exports.run(resolution, downloadPath);
  },

  run: function(resolution, downloadPath) {
    var startTime = new Date();
    var ps = new PageScraper(resolution);
    var existingFiles = 0;

    // run the page scraper
    ps.on('next', function(pageNumber) {
      console.log('Scanning Page ' + pageNumber + '...');
    });
    ps.on('end', function(downloadLinks) {
      var index = downloadLinks.length;
      var elapesdSeconds = (new Date() - startTime) / 1000;
      var dl = new Downloader(downloadLinks, downloadPath, resolution);

      dl.on('start', function() {
        console.log('Starting Download...\n');
      });
      dl.on('exist', function() {
        existingFiles++;
        index--;
      });
      dl.on('save', function(fileName) {
        console.log('[' + index + '] Saved: ' + fileName);
        index--;
      });
      dl.on('fail', function(fileName) {
        console.log('[' + index + '] Failed: ' + fileName);
        index--;
      });
      dl.on('end', function() {
        var elapesdSeconds = (new Date() - startTime) / 1000;
        console.log('Download competed in ' + elapesdSeconds + ' seconds.');
        console.log('Already had ' + existingFiles + ' images.');
      });

      console.log('Scrape completed in ' + elapesdSeconds + ' seconds. Found ' + index + ' images.');
      dl.start();
    });

    console.log('InterfaceLIFT Wallpaper Auto-Downloader\n');
    console.log([
      'Searching pages for images...',
      '(The download will begin after the page scan finishes.)'
    ].join('\n'));
    ps.start();
  },

  help: function() {
    console.log([
      'Usage: node interfacelift-downloader [resolution] [path]',
      'Resolution is the dimensions of the images you want to search for.',
      'For example: interfacelift-downloader 1920x1080'
    ].join('\n'));
  }

};
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import chromeAsync from '../chromeasync';
import * as states from '../states';
import * as util from '../util';
import { BackendInterface } from './backendinterface';

export class GerritBackend extends BackendInterface {
  constructor(site) {
    super();
    this.site_ = site;
  }

  /**
   * Fetches changes from this backend.
   *
   * @public
   * @return {Promise<Change[]>} A list of changes.
   */
  fetch() {
    return this.ensureLogin_()
      .then((selfAddress) => this.doFetch_(selfAddress));
  }

  /**
   * Fetches changes from this backend.
   *
   * This function assumes the user is already logged in.
   *
   * @private
   * @return {Promise<Change[]>} A list of changes.
   */
  doFetch_(selfAddress) {
    const queries = [
      'is:open owner:' + selfAddress,
      'is:open reviewer:' + selfAddress + ' -owner:' + selfAddress,
      'is:merged owner:' + selfAddress + ' limit:20'
    ];
    let changesUrl =
        this.site_['url'] +
        '/changes/?o=DETAILED_ACCOUNTS&o=REVIEWED&o=DETAILED_LABELS';
    queries.forEach((query) => {
      changesUrl += '&q=' + encodeURIComponent(query);
    });

    return fetch(changesUrl, {credentials: 'include'})
      .then((res) => res.text())
      .then((text) => {
        const data = JSON.parse(text.substring(text.indexOf('\n') + 1));
        return this.parseResponse_(data, selfAddress);
      });
  }

  /**
   * Log in to the code review site if we need.
   *
   * @private
   * @return Promise<string> Promise to return the mail address of the user.
   *   If login fails, the promise is rejected.
   */
  ensureLogin_() {
    return this.getSelfAddress_()
      .catch(() => this.attemptAutoLogin_())
      .catch(() => this.attemptManualLogin_());
  }

  /**
   * Returns the mail address of the user if they have already logged in.
   *
   * @private
   * @return Promise<string> Promise to return the mail address of the user.
   *   If the user is not logged in, the promise is rejected.
   */
  getSelfAddress_() {
    const accountsUrl = this.site_['url'] + '/accounts/self';
    return fetch(accountsUrl, {credentials: 'include'})
      .then((res) => res.text())
      .then((text) =>
        JSON.parse(text.substring(text.indexOf('\n') + 1)).email);
  }

  /**
   * Attempts login without user interaction.
   *
   * @private
   * @return Promise<string> Promise to return the mail address of the user.
   *   If login fails, the promise is rejected.
   */
  attemptAutoLogin_() {
    const loginUrl = this.site_['url'] + '/login/';
    // Set no-cors to follow redirects to other hosts (accounts.google.com).
    // Note that we can't read the response of this request and thus can't know
    // if it succeeded or not.
    return fetch(
      loginUrl, {mode: 'no-cors', credentials: 'include', redirect: 'follow'})
      .then(() => this.getSelfAddress_());
  }

  /**
   * Attempts login, showing tabs for manual interactions.
   *
   * @private
   * @return Promise<string> Promise to return the mail address of the user.
   *   If login fails, the promise is rejected.
   */
  attemptManualLogin_() {
    const loginUrl = this.site_['url'] + '/login/';
    return chromeAsync.tabs.create({url: loginUrl, active: true})
      .then((tab) => {
        const tabId = tab.id;
        const checkFinish = () => chromeAsync.tabs.get(tabId).then((tab) => {
          if (!tab) {
            throw new Error('Tab was closed');
          }

          const a = document.createElement('a');
          a.href = tab.url;
          if (!(a.pathname == '/' || a.pathname.indexOf('/dashboard/') == 0)) {
            // Not yet logged in. Wait 100ms and retry.
            return util.sleep(100).then(checkFinish);
          }

          console.log('Login success');
          chrome.tabs.remove(tabId);
          return this.getSelfAddress_();
        });
        return checkFinish();
      });
  }

  parseResponse_(data, selfAddress) {
    const changes = [];
    data.forEach((entries) => {
      entries.forEach((entry) => {
        changes.push(this.parseEntry_(entry, selfAddress));
      });
    });
    return changes;
  }

  parseEntry_(entry, selfAddress) {
    let status;
    if (entry['status'] == 'NEW' || entry['status'] == 'DRAFT') {
      if (entry['submittable']) {
        status = states.ChangeStatus.APPROVED;
      } else {
        const reviewMetadata = entry['labels']['Code-Review'] || {};
        const reviewers = reviewMetadata['all'] || [];
        const ownerId = entry['owner']['_account_id'];
        if (reviewers.findIndex(
              (user) => user['_account_id'] != ownerId) >= 0) {
          // Here non-owner user is listed.
          status = states.ChangeStatus.REVIEWING;
        } else {
          status = states.ChangeStatus.PENDING;
        }
      }
    } else if (entry['status'] == 'SUBMITTED' || entry['status'] == 'MERGED') {
      status = states.ChangeStatus.SUBMITTED;
    } else if (entry['status'] == 'ABANDONED') {
      status = states.ChangeStatus.ABANDONED;
    } else {
      status = states.ChangeStatus.UNKNOWN;
    }
    return {
      owned: entry['owner']['email'] == selfAddress,
      reviewing: true,  // Gerrit does not support CC.
      subject: entry['subject'],
      url: this.site_['url'] + '/#/c/' + entry['_number'],
      status: status,
      repository: this.site_['label'] + ': ' + entry['project'] + ' (' +
          entry['branch'] + ')',
      ownerName: entry['owner']['name'],
      updated: new Date(entry['updated'] + ' UTC').getTime()
    };
  }
}
<commit_msg>Request SUBMITTABLE state to Gerrit backends.<commit_after>// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import chromeAsync from '../chromeasync';
import * as states from '../states';
import * as util from '../util';
import { BackendInterface } from './backendinterface';

export class GerritBackend extends BackendInterface {
  constructor(site) {
    super();
    this.site_ = site;
  }

  /**
   * Fetches changes from this backend.
   *
   * @public
   * @return {Promise<Change[]>} A list of changes.
   */
  fetch() {
    return this.ensureLogin_()
      .then((selfAddress) => this.doFetch_(selfAddress));
  }

  /**
   * Fetches changes from this backend.
   *
   * This function assumes the user is already logged in.
   *
   * @private
   * @return {Promise<Change[]>} A list of changes.
   */
  doFetch_(selfAddress) {
    const queries = [
      'is:open owner:' + selfAddress,
      'is:open reviewer:' + selfAddress + ' -owner:' + selfAddress,
      'is:merged owner:' + selfAddress + ' limit:20'
    ];
    let changesUrl =
        this.site_['url'] + '/changes/' +
        '?o=DETAILED_ACCOUNTS&o=REVIEWED&o=DETAILED_LABELS&o=SUBMITTABLE';
    queries.forEach((query) => {
      changesUrl += '&q=' + encodeURIComponent(query);
    });

    return fetch(changesUrl, {credentials: 'include'})
      .then((res) => res.text())
      .then((text) => {
        const data = JSON.parse(text.substring(text.indexOf('\n') + 1));
        return this.parseResponse_(data, selfAddress);
      });
  }

  /**
   * Log in to the code review site if we need.
   *
   * @private
   * @return Promise<string> Promise to return the mail address of the user.
   *   If login fails, the promise is rejected.
   */
  ensureLogin_() {
    return this.getSelfAddress_()
      .catch(() => this.attemptAutoLogin_())
      .catch(() => this.attemptManualLogin_());
  }

  /**
   * Returns the mail address of the user if they have already logged in.
   *
   * @private
   * @return Promise<string> Promise to return the mail address of the user.
   *   If the user is not logged in, the promise is rejected.
   */
  getSelfAddress_() {
    const accountsUrl = this.site_['url'] + '/accounts/self';
    return fetch(accountsUrl, {credentials: 'include'})
      .then((res) => res.text())
      .then((text) =>
        JSON.parse(text.substring(text.indexOf('\n') + 1)).email);
  }

  /**
   * Attempts login without user interaction.
   *
   * @private
   * @return Promise<string> Promise to return the mail address of the user.
   *   If login fails, the promise is rejected.
   */
  attemptAutoLogin_() {
    const loginUrl = this.site_['url'] + '/login/';
    // Set no-cors to follow redirects to other hosts (accounts.google.com).
    // Note that we can't read the response of this request and thus can't know
    // if it succeeded or not.
    return fetch(
      loginUrl, {mode: 'no-cors', credentials: 'include', redirect: 'follow'})
      .then(() => this.getSelfAddress_());
  }

  /**
   * Attempts login, showing tabs for manual interactions.
   *
   * @private
   * @return Promise<string> Promise to return the mail address of the user.
   *   If login fails, the promise is rejected.
   */
  attemptManualLogin_() {
    const loginUrl = this.site_['url'] + '/login/';
    return chromeAsync.tabs.create({url: loginUrl, active: true})
      .then((tab) => {
        const tabId = tab.id;
        const checkFinish = () => chromeAsync.tabs.get(tabId).then((tab) => {
          if (!tab) {
            throw new Error('Tab was closed');
          }

          const a = document.createElement('a');
          a.href = tab.url;
          if (!(a.pathname == '/' || a.pathname.indexOf('/dashboard/') == 0)) {
            // Not yet logged in. Wait 100ms and retry.
            return util.sleep(100).then(checkFinish);
          }

          console.log('Login success');
          chrome.tabs.remove(tabId);
          return this.getSelfAddress_();
        });
        return checkFinish();
      });
  }

  parseResponse_(data, selfAddress) {
    const changes = [];
    data.forEach((entries) => {
      entries.forEach((entry) => {
        changes.push(this.parseEntry_(entry, selfAddress));
      });
    });
    return changes;
  }

  parseEntry_(entry, selfAddress) {
    let status;
    if (entry['status'] == 'NEW' || entry['status'] == 'DRAFT') {
      if (entry['submittable']) {
        status = states.ChangeStatus.APPROVED;
      } else {
        const reviewMetadata = entry['labels']['Code-Review'] || {};
        const reviewers = reviewMetadata['all'] || [];
        const ownerId = entry['owner']['_account_id'];
        if (reviewers.findIndex(
              (user) => user['_account_id'] != ownerId) >= 0) {
          // Here non-owner user is listed.
          status = states.ChangeStatus.REVIEWING;
        } else {
          status = states.ChangeStatus.PENDING;
        }
      }
    } else if (entry['status'] == 'SUBMITTED' || entry['status'] == 'MERGED') {
      status = states.ChangeStatus.SUBMITTED;
    } else if (entry['status'] == 'ABANDONED') {
      status = states.ChangeStatus.ABANDONED;
    } else {
      status = states.ChangeStatus.UNKNOWN;
    }
    return {
      owned: entry['owner']['email'] == selfAddress,
      reviewing: true,  // Gerrit does not support CC.
      subject: entry['subject'],
      url: this.site_['url'] + '/#/c/' + entry['_number'],
      status: status,
      repository: this.site_['label'] + ': ' + entry['project'] + ' (' +
          entry['branch'] + ')',
      ownerName: entry['owner']['name'],
      updated: new Date(entry['updated'] + ' UTC').getTime()
    };
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

const path = require('path');
const program = require('commander');
const copyfiles = require('copyfiles');

program
  .command('create <template>')
  .description('create an app')
  .action((template) => {
    const dir = path.join(__dirname, '../templates', template);
    const depth = path.join(__dirname, '../templates').split(path.sep).length;
    copyfiles([`${dir}/**/*`, '.'], depth, (err) => {
      if (err) console.error(err);
    });
  });

if (!process.argv.slice(2).length) {
  program.help();
}

program.parse(process.argv);
<commit_msg>change create to import<commit_after>#!/usr/bin/env node

const path = require('path');
const program = require('commander');
const copyfiles = require('copyfiles');

program
  .command('import <template>')
  .description('import an app')
  .action((template) => {
    const dir = path.join(__dirname, '../templates', template);
    const depth = dir.split(path.sep).length;
    copyfiles([`${dir}/**/*`, '.'], depth, (err) => {
      if (err) console.error(err);
    });
  });

if (!process.argv.slice(2).length) {
  program.help();
}

program.parse(process.argv);
<|endoftext|>