<commit_before>exports.BodyReader = BodyReader;

var _ = require("underscore");

var documents = require("../documents");
var Result = require("../results").Result;
var warning = require("../results").warning;
var uriToZipEntryName = require("./uris").uriToZipEntryName;

function BodyReader(options) {
    var fieldHyperlink = null;
    var relationships = options.relationships;
    var contentTypes = options.contentTypes;
    var docxFile = options.docxFile;
    var files = options.files;
    var numbering = options.numbering;
    var styles = options.styles;

    function readXmlElements(elements) {
        var results = elements.map(readXmlElement);
        return combineResults(results);
    }

    function readXmlElement(element) {
        if (element.type === "element") {
            var handler = xmlElementReaders[element.name];
            if (handler) {
                return handler(element);
            } else if (!Object.prototype.hasOwnProperty.call(ignoreElements, element.name)) {
                var message = warning("An unrecognised element was ignored: " + element.name);
                return emptyResultWithMessages([message]);
            }
        }
        return emptyResult();
    }

    function readRunProperties(element) {
        return readRunStyle(element).map(function(style) {
            return {
                type: "runProperties",
                styleId: style.styleId,
                styleName: style.name,
                verticalAlignment: element.firstOrEmpty("w:vertAlign").attributes["w:val"],
                font: element.firstOrEmpty("w:rFonts").attributes["w:ascii"],
                isBold: readBooleanElement(element.first("w:b")),
                isUnderline: readBooleanElement(element.first("w:u")),
                isItalic: readBooleanElement(element.first("w:i")),
                isStrikethrough: readBooleanElement(element.first("w:strike"))
            };
        });
    }

    function readBooleanElement(element) {
        if (element) {
            var value = element.attributes["w:val"];
            return value !== "false" && value !== "0";
        } else {
            return false;
        }
    }

    function readParagraphStyle(element) {
        return readStyle(element, "w:pStyle", "Paragraph", styles.findParagraphStyleById);
    }

    function readRunStyle(element) {
        return readStyle(element, "w:rStyle", "Run", styles.findCharacterStyleById);
    }

    function readStyle(element, styleTagName, styleType, findStyleById) {
        var messages = [];
        var styleElement = element.first(styleTagName);
        var styleId = null;
        var name = null;
        if (styleElement) {
            styleId = styleElement.attributes["w:val"];
            if (styleId) {
                var style = findStyleById(styleId);
                if (style) {
                    name = style.name;
                } else {
                    messages.push(undefinedStyleWarning(styleType, styleId));
                }
            }
        }
        return elementResultWithMessages({styleId: styleId, name: name}, messages);
    }

    function noteReferenceReader(noteType) {
        return function(element) {
            var noteId = element.attributes["w:id"];
            return elementResult(new documents.NoteReference({
                noteType: noteType,
                noteId: noteId
            }));
        };
    }

    function readCommentReference(element) {
        return elementResult(documents.commentReference({
            commentId: element.attributes["w:id"]
        }));
    }

    function readChildElements(element) {
        return readXmlElements(element.children);
    }

    var xmlElementReaders = {
        "w:p": function(element) {
            return readXmlElements(element.children)
                .map(function(children) {
                    var properties = _.find(children, isParagraphProperties);
                    return new documents.Paragraph(
                        children.filter(negate(isParagraphProperties)),
                        properties
                    );
                })
                .insertExtra();
        },
        "w:pPr": function(element) {
            return readParagraphStyle(element).map(function(style) {
                return {
                    type: "paragraphProperties",
                    styleId: style.styleId,
                    styleName: style.name,
                    alignment: element.firstOrEmpty("w:jc").attributes["w:val"],
                    numbering: readNumberingProperties(element.firstOrEmpty("w:numPr"))
                };
            });
        },
        "w:r": function(element) {
            return readXmlElements(element.children)
                .map(function(children) {
                    var properties = _.find(children, isRunProperties);
                    children = children.filter(negate(isRunProperties));

                    if (fieldHyperlink !== null) {
                        children = [new documents.Hyperlink(children, {href: fieldHyperlink})];
                    }

                    return new documents.Run(children, properties);
                });
        },
        "w:rPr": readRunProperties,
        "w:fldChar": function(element) {
            var type = element.attributes["w:fldCharType"];
            if (type === "end") {
                fieldHyperlink = null;
            }
            return emptyResult();
        },
        "w:instrText": function(element) {
            var hyperlink = parseHyperlinkFieldCode(element.text());
            if (hyperlink !== null) {
                fieldHyperlink = hyperlink;
            }
            return emptyResult();
        },
        "w:t": function(element) {
            return elementResult(new documents.Text(element.text()));
        },
        "w:tab": function(element) {
            return elementResult(new documents.Tab());
        },
        "w:hyperlink": function(element) {
            var relationshipId = element.attributes["r:id"];
            var anchor = element.attributes["w:anchor"];
            return readXmlElements(element.children).map(function(children) {
                if (relationshipId) {
                    var href = relationships[relationshipId].target;
                    return new documents.Hyperlink(children, {href: href});
                } else if (anchor) {
                    return new documents.Hyperlink(children, {anchor: anchor});
                } else {
                    return children;
                }
            });
        },
        "w:tbl": readTable,
        "w:tr": readTableRow,
        "w:tc": readTableCell,
        "w:footnoteReference": noteReferenceReader("footnote"),
        "w:endnoteReference": noteReferenceReader("endnote"),
        "w:commentReference": readCommentReference,
        "w:br": function(element) {
            var breakType = element.attributes["w:type"];
            if (breakType == null || breakType === "textWrapping") {
                return elementResult(documents.lineBreak);
            } else if (breakType === "page") {
                return elementResult(documents.pageBreak);
            } else if (breakType === "column") {
                return elementResult(documents.columnBreak);
            } else {
                return emptyResultWithMessages([warning("Unsupported break type: " + breakType)]);
            }
        },
        "w:bookmarkStart": function(element){
            var name = element.attributes["w:name"];
            if (name === "_GoBack") {
                return emptyResult();
            } else {
                return elementResult(new documents.BookmarkStart({name: name}));
            }
        },

        "mc:AlternateContent": function(element) {
            return readChildElements(element.first("mc:Fallback"));
        },

        "w:sdt": function(element) {
            return readXmlElements(element.firstOrEmpty("w:sdtContent").children);
        },

        "w:ins": readChildElements,
        "w:smartTag": readChildElements,
        "w:drawing": readChildElements,
        "w:pict": function(element) {
            return readChildElements(element).toExtra();
        },
        "v:roundrect": readChildElements,
        "v:shape": readChildElements,
        "v:textbox": readChildElements,
        "w:txbxContent": readChildElements,
        "wp:inline": readDrawingElement,
        "wp:anchor": readDrawingElement,
        "v:imagedata": readImageData
    };
    return {
        readXmlElement: readXmlElement,
        readXmlElements: readXmlElements,
        _readNumberingProperties: readNumberingProperties
    };

    function readNumberingProperties(element) {
        var level = element.firstOrEmpty("w:ilvl").attributes["w:val"];
        var numId = element.firstOrEmpty("w:numId").attributes["w:val"];
        if (level === undefined || numId === undefined) {
            return null;
        } else {
            return numbering.findLevel(numId, level);
        }
    }

    function readTable(element) {
        return readXmlElements(element.children)
            .flatMap(calculateRowSpans)
            .map(documents.Table);
    }

    function readTableRow(element) {
        return readXmlElements(element.children).map(documents.TableRow);
    }

    function readTableCell(element) {
        return readXmlElements(element.children).map(function(children) {
            var properties = element.firstOrEmpty("w:tcPr");

            var gridSpan = properties.firstOrEmpty("w:gridSpan").attributes["w:val"];
            var colSpan = gridSpan ? parseInt(gridSpan, 10) : 1;

            var cell = documents.TableCell(children, {colSpan: colSpan});
            cell._vMerge = readVMerge(properties);
            return cell;
        });
    }

    function readVMerge(properties) {
        var element = properties.first("w:vMerge");
        if (element) {
            var val = element.attributes["w:val"];
            return val === "continue" || !val;
        } else {
            return null;
        }
    }

    function calculateRowSpans(rows) {
        var unexpectedNonRows = _.any(rows, function(row) {
            return row.type !== documents.types.tableRow;
        });
        if (unexpectedNonRows) {
            return elementResultWithMessages(rows, [warning(
                "unexpected non-row element in table, cell merging may be incorrect"
            )]);
        }
        var unexpectedNonCells = _.any(rows, function(row) {
            return _.any(row.children, function(cell) {
                return cell.type !== documents.types.tableCell;
            });
        });
        if (unexpectedNonCells) {
            return elementResultWithMessages(rows, [warning(
                "unexpected non-cell element in table row, cell merging may be incorrect"
            )]);
        }

        var columns = {};

        rows.forEach(function(row) {
            var cellIndex = 0;
            row.children.forEach(function(cell) {
                if (cell._vMerge && columns[cellIndex]) {
                    columns[cellIndex].rowSpan++;
                } else {
                    columns[cellIndex] = cell;
                    cell._vMerge = false;
                }
                cellIndex += cell.colSpan;
            });
        });

        rows.forEach(function(row) {
            row.children = row.children.filter(function(cell) {
                return !cell._vMerge;
            });
            row.children.forEach(function(cell) {
                delete cell._vMerge;
            });
        });

        return elementResult(rows);
    }

    function readDrawingElement(element) {
        var blips = element
            .getElementsByTagName("a:graphic")
            .getElementsByTagName("a:graphicData")
            .getElementsByTagName("pic:pic")
            .getElementsByTagName("pic:blipFill")
            .getElementsByTagName("a:blip");

        return combineResults(blips.map(readBlip.bind(null, element)));
    }

    function readBlip(element, blip) {
        var properties = element.first("wp:docPr").attributes;
        var altText = isBlank(properties.descr) ? properties.title : properties.descr;
        return readImage(findBlipImageFile(blip), altText);
    }

    function isBlank(value) {
        return value == null || /^\s*$/.test(value);
    }

    function findBlipImageFile(blip) {
        var embedRelationshipId = blip.attributes["r:embed"];
        var linkRelationshipid = blip.attributes["r:link"];
        if (embedRelationshipId) {
            return findEmbeddedImageFile(embedRelationshipId);
        } else {
            var imagePath = relationships[linkRelationshipid].target;
            return {
                path: imagePath,
                read: files.read.bind(files, imagePath)
            };
        }
    }

    function readImageData(element) {
        var relationshipId = element.attributes['r:id'];

        if (relationshipId) {
            return readImage(
                findEmbeddedImageFile(relationshipId),
                element.attributes["o:title"]);
        } else {
            return emptyResultWithMessages([warning("A v:imagedata element without a relationship ID was ignored")]);
        }
    }

    function findEmbeddedImageFile(relationshipId) {
        var path = uriToZipEntryName("word", relationships[relationshipId].target);
        return {
            path: path,
            read: docxFile.read.bind(docxFile, path)
        };
    }

    function readImage(imageFile, altText) {
        var contentType = contentTypes.findContentType(imageFile.path);

        var image = documents.Image({
            readImage: imageFile.read,
            altText: altText,
            contentType: contentType
        });
        var warnings = supportedImageTypes[contentType] ?
            [] : warning("Image of type " + contentType + " is unlikely to display in web browsers");
        return elementResultWithMessages(image, warnings);
    }

    function undefinedStyleWarning(type, styleId) {
        return warning(
            type + " style with ID " + styleId + " was referenced but not defined in the document");
    }
}

var supportedImageTypes = {
    "image/png": true,
    "image/gif": true,
    "image/jpeg": true,
    "image/svg+xml": true,
    "image/tiff": true
};

var ignoreElements = {
    "office-word:wrap": true,
    "v:shadow": true,
    "v:shapetype": true,
    "w:annotationRef": true,
    "w:bookmarkEnd": true,
    "w:sectPr": true,
    "w:proofErr": true,
    "w:lastRenderedPageBreak": true,
    "w:commentRangeStart": true,
    "w:commentRangeEnd": true,
    "w:del": true,
    "w:footnoteRef": true,
    "w:endnoteRef": true,
    "w:tblPr": true,
    "w:tblGrid": true,
    "w:tcPr": true
};

function isParagraphProperties(element) {
    return element.type === "paragraphProperties";
}

function isRunProperties(element) {
    return element.type === "runProperties";
}

function negate(predicate) {
    return function(value) {
        return !predicate(value);
    };
}

function emptyResultWithMessages(messages) {
    return new ReadResult(null, null, messages);
}

function emptyResult() {
    return new ReadResult(null);
}

function elementResult(element) {
    return new ReadResult(element);
}

function elementResultWithMessages(element, messages) {
    return new ReadResult(element, null, messages);
}

function ReadResult(element, extra, messages) {
    this.value = element || [];
    this.extra = extra;
    this._result = new Result({
        element: this.value,
        extra: extra
    }, messages);
    this.messages = this._result.messages;
}

ReadResult.prototype.toExtra = function() {
    return new ReadResult(null, joinElements(this.extra, this.value), this.messages);
};

ReadResult.prototype.insertExtra = function() {
    var extra = this.extra;
    if (extra && extra.length) {
        return new ReadResult(joinElements(this.value, extra), null, this.messages);
    } else {
        return this;
    }
};

ReadResult.prototype.map = function(func) {
    var result = this._result.map(function(value) {
        return func(value.element);
    });
    return new ReadResult(result.value, this.extra, result.messages);
};

ReadResult.prototype.flatMap = function(func) {
    var result = this._result.flatMap(function(value) {
        return func(value.element)._result;
    });
    return new ReadResult(result.value.element, joinElements(this.extra, result.value.extra), result.messages);
};

function combineResults(results) {
    var result = Result.combine(_.pluck(results, "_result"));
    return new ReadResult(
        _.flatten(_.pluck(result.value, "element")),
        _.filter(_.flatten(_.pluck(result.value, "extra")), identity),
        result.messages
    );
}

function joinElements(first, second) {
    return _.flatten([first, second]);
}

function identity(value) {
    return value;
}

function parseHyperlinkFieldCode(code) {
    var result = /HYPERLINK "(.*)"/.exec(code);
    if (result) {
        return result[1];
    } else {
        return null;
    }
}
<commit_msg>Tighten regex for hyperlink field code<commit_after>exports.BodyReader = BodyReader;

var _ = require("underscore");

var documents = require("../documents");
var Result = require("../results").Result;
var warning = require("../results").warning;
var uriToZipEntryName = require("./uris").uriToZipEntryName;

function BodyReader(options) {
    var fieldHyperlink = null;
    var relationships = options.relationships;
    var contentTypes = options.contentTypes;
    var docxFile = options.docxFile;
    var files = options.files;
    var numbering = options.numbering;
    var styles = options.styles;

    function readXmlElements(elements) {
        var results = elements.map(readXmlElement);
        return combineResults(results);
    }

    function readXmlElement(element) {
        if (element.type === "element") {
            var handler = xmlElementReaders[element.name];
            if (handler) {
                return handler(element);
            } else if (!Object.prototype.hasOwnProperty.call(ignoreElements, element.name)) {
                var message = warning("An unrecognised element was ignored: " + element.name);
                return emptyResultWithMessages([message]);
            }
        }
        return emptyResult();
    }

    function readRunProperties(element) {
        return readRunStyle(element).map(function(style) {
            return {
                type: "runProperties",
                styleId: style.styleId,
                styleName: style.name,
                verticalAlignment: element.firstOrEmpty("w:vertAlign").attributes["w:val"],
                font: element.firstOrEmpty("w:rFonts").attributes["w:ascii"],
                isBold: readBooleanElement(element.first("w:b")),
                isUnderline: readBooleanElement(element.first("w:u")),
                isItalic: readBooleanElement(element.first("w:i")),
                isStrikethrough: readBooleanElement(element.first("w:strike"))
            };
        });
    }

    function readBooleanElement(element) {
        if (element) {
            var value = element.attributes["w:val"];
            return value !== "false" && value !== "0";
        } else {
            return false;
        }
    }

    function readParagraphStyle(element) {
        return readStyle(element, "w:pStyle", "Paragraph", styles.findParagraphStyleById);
    }

    function readRunStyle(element) {
        return readStyle(element, "w:rStyle", "Run", styles.findCharacterStyleById);
    }

    function readStyle(element, styleTagName, styleType, findStyleById) {
        var messages = [];
        var styleElement = element.first(styleTagName);
        var styleId = null;
        var name = null;
        if (styleElement) {
            styleId = styleElement.attributes["w:val"];
            if (styleId) {
                var style = findStyleById(styleId);
                if (style) {
                    name = style.name;
                } else {
                    messages.push(undefinedStyleWarning(styleType, styleId));
                }
            }
        }
        return elementResultWithMessages({styleId: styleId, name: name}, messages);
    }

    function noteReferenceReader(noteType) {
        return function(element) {
            var noteId = element.attributes["w:id"];
            return elementResult(new documents.NoteReference({
                noteType: noteType,
                noteId: noteId
            }));
        };
    }

    function readCommentReference(element) {
        return elementResult(documents.commentReference({
            commentId: element.attributes["w:id"]
        }));
    }

    function readChildElements(element) {
        return readXmlElements(element.children);
    }

    var xmlElementReaders = {
        "w:p": function(element) {
            return readXmlElements(element.children)
                .map(function(children) {
                    var properties = _.find(children, isParagraphProperties);
                    return new documents.Paragraph(
                        children.filter(negate(isParagraphProperties)),
                        properties
                    );
                })
                .insertExtra();
        },
        "w:pPr": function(element) {
            return readParagraphStyle(element).map(function(style) {
                return {
                    type: "paragraphProperties",
                    styleId: style.styleId,
                    styleName: style.name,
                    alignment: element.firstOrEmpty("w:jc").attributes["w:val"],
                    numbering: readNumberingProperties(element.firstOrEmpty("w:numPr"))
                };
            });
        },
        "w:r": function(element) {
            return readXmlElements(element.children)
                .map(function(children) {
                    var properties = _.find(children, isRunProperties);
                    children = children.filter(negate(isRunProperties));

                    if (fieldHyperlink !== null) {
                        children = [new documents.Hyperlink(children, {href: fieldHyperlink})];
                    }

                    return new documents.Run(children, properties);
                });
        },
        "w:rPr": readRunProperties,
        "w:fldChar": function(element) {
            var type = element.attributes["w:fldCharType"];
            if (type === "end") {
                fieldHyperlink = null;
            }
            return emptyResult();
        },
        "w:instrText": function(element) {
            var hyperlink = parseHyperlinkFieldCode(element.text());
            if (hyperlink !== null) {
                fieldHyperlink = hyperlink;
            }
            return emptyResult();
        },
        "w:t": function(element) {
            return elementResult(new documents.Text(element.text()));
        },
        "w:tab": function(element) {
            return elementResult(new documents.Tab());
        },
        "w:hyperlink": function(element) {
            var relationshipId = element.attributes["r:id"];
            var anchor = element.attributes["w:anchor"];
            return readXmlElements(element.children).map(function(children) {
                if (relationshipId) {
                    var href = relationships[relationshipId].target;
                    return new documents.Hyperlink(children, {href: href});
                } else if (anchor) {
                    return new documents.Hyperlink(children, {anchor: anchor});
                } else {
                    return children;
                }
            });
        },
        "w:tbl": readTable,
        "w:tr": readTableRow,
        "w:tc": readTableCell,
        "w:footnoteReference": noteReferenceReader("footnote"),
        "w:endnoteReference": noteReferenceReader("endnote"),
        "w:commentReference": readCommentReference,
        "w:br": function(element) {
            var breakType = element.attributes["w:type"];
            if (breakType == null || breakType === "textWrapping") {
                return elementResult(documents.lineBreak);
            } else if (breakType === "page") {
                return elementResult(documents.pageBreak);
            } else if (breakType === "column") {
                return elementResult(documents.columnBreak);
            } else {
                return emptyResultWithMessages([warning("Unsupported break type: " + breakType)]);
            }
        },
        "w:bookmarkStart": function(element){
            var name = element.attributes["w:name"];
            if (name === "_GoBack") {
                return emptyResult();
            } else {
                return elementResult(new documents.BookmarkStart({name: name}));
            }
        },

        "mc:AlternateContent": function(element) {
            return readChildElements(element.first("mc:Fallback"));
        },

        "w:sdt": function(element) {
            return readXmlElements(element.firstOrEmpty("w:sdtContent").children);
        },

        "w:ins": readChildElements,
        "w:smartTag": readChildElements,
        "w:drawing": readChildElements,
        "w:pict": function(element) {
            return readChildElements(element).toExtra();
        },
        "v:roundrect": readChildElements,
        "v:shape": readChildElements,
        "v:textbox": readChildElements,
        "w:txbxContent": readChildElements,
        "wp:inline": readDrawingElement,
        "wp:anchor": readDrawingElement,
        "v:imagedata": readImageData
    };
    return {
        readXmlElement: readXmlElement,
        readXmlElements: readXmlElements,
        _readNumberingProperties: readNumberingProperties
    };

    function readNumberingProperties(element) {
        var level = element.firstOrEmpty("w:ilvl").attributes["w:val"];
        var numId = element.firstOrEmpty("w:numId").attributes["w:val"];
        if (level === undefined || numId === undefined) {
            return null;
        } else {
            return numbering.findLevel(numId, level);
        }
    }

    function readTable(element) {
        return readXmlElements(element.children)
            .flatMap(calculateRowSpans)
            .map(documents.Table);
    }

    function readTableRow(element) {
        return readXmlElements(element.children).map(documents.TableRow);
    }

    function readTableCell(element) {
        return readXmlElements(element.children).map(function(children) {
            var properties = element.firstOrEmpty("w:tcPr");

            var gridSpan = properties.firstOrEmpty("w:gridSpan").attributes["w:val"];
            var colSpan = gridSpan ? parseInt(gridSpan, 10) : 1;

            var cell = documents.TableCell(children, {colSpan: colSpan});
            cell._vMerge = readVMerge(properties);
            return cell;
        });
    }

    function readVMerge(properties) {
        var element = properties.first("w:vMerge");
        if (element) {
            var val = element.attributes["w:val"];
            return val === "continue" || !val;
        } else {
            return null;
        }
    }

    function calculateRowSpans(rows) {
        var unexpectedNonRows = _.any(rows, function(row) {
            return row.type !== documents.types.tableRow;
        });
        if (unexpectedNonRows) {
            return elementResultWithMessages(rows, [warning(
                "unexpected non-row element in table, cell merging may be incorrect"
            )]);
        }
        var unexpectedNonCells = _.any(rows, function(row) {
            return _.any(row.children, function(cell) {
                return cell.type !== documents.types.tableCell;
            });
        });
        if (unexpectedNonCells) {
            return elementResultWithMessages(rows, [warning(
                "unexpected non-cell element in table row, cell merging may be incorrect"
            )]);
        }

        var columns = {};

        rows.forEach(function(row) {
            var cellIndex = 0;
            row.children.forEach(function(cell) {
                if (cell._vMerge && columns[cellIndex]) {
                    columns[cellIndex].rowSpan++;
                } else {
                    columns[cellIndex] = cell;
                    cell._vMerge = false;
                }
                cellIndex += cell.colSpan;
            });
        });

        rows.forEach(function(row) {
            row.children = row.children.filter(function(cell) {
                return !cell._vMerge;
            });
            row.children.forEach(function(cell) {
                delete cell._vMerge;
            });
        });

        return elementResult(rows);
    }

    function readDrawingElement(element) {
        var blips = element
            .getElementsByTagName("a:graphic")
            .getElementsByTagName("a:graphicData")
            .getElementsByTagName("pic:pic")
            .getElementsByTagName("pic:blipFill")
            .getElementsByTagName("a:blip");

        return combineResults(blips.map(readBlip.bind(null, element)));
    }

    function readBlip(element, blip) {
        var properties = element.first("wp:docPr").attributes;
        var altText = isBlank(properties.descr) ? properties.title : properties.descr;
        return readImage(findBlipImageFile(blip), altText);
    }

    function isBlank(value) {
        return value == null || /^\s*$/.test(value);
    }

    function findBlipImageFile(blip) {
        var embedRelationshipId = blip.attributes["r:embed"];
        var linkRelationshipid = blip.attributes["r:link"];
        if (embedRelationshipId) {
            return findEmbeddedImageFile(embedRelationshipId);
        } else {
            var imagePath = relationships[linkRelationshipid].target;
            return {
                path: imagePath,
                read: files.read.bind(files, imagePath)
            };
        }
    }

    function readImageData(element) {
        var relationshipId = element.attributes['r:id'];

        if (relationshipId) {
            return readImage(
                findEmbeddedImageFile(relationshipId),
                element.attributes["o:title"]);
        } else {
            return emptyResultWithMessages([warning("A v:imagedata element without a relationship ID was ignored")]);
        }
    }

    function findEmbeddedImageFile(relationshipId) {
        var path = uriToZipEntryName("word", relationships[relationshipId].target);
        return {
            path: path,
            read: docxFile.read.bind(docxFile, path)
        };
    }

    function readImage(imageFile, altText) {
        var contentType = contentTypes.findContentType(imageFile.path);

        var image = documents.Image({
            readImage: imageFile.read,
            altText: altText,
            contentType: contentType
        });
        var warnings = supportedImageTypes[contentType] ?
            [] : warning("Image of type " + contentType + " is unlikely to display in web browsers");
        return elementResultWithMessages(image, warnings);
    }

    function undefinedStyleWarning(type, styleId) {
        return warning(
            type + " style with ID " + styleId + " was referenced but not defined in the document");
    }
}

var supportedImageTypes = {
    "image/png": true,
    "image/gif": true,
    "image/jpeg": true,
    "image/svg+xml": true,
    "image/tiff": true
};

var ignoreElements = {
    "office-word:wrap": true,
    "v:shadow": true,
    "v:shapetype": true,
    "w:annotationRef": true,
    "w:bookmarkEnd": true,
    "w:sectPr": true,
    "w:proofErr": true,
    "w:lastRenderedPageBreak": true,
    "w:commentRangeStart": true,
    "w:commentRangeEnd": true,
    "w:del": true,
    "w:footnoteRef": true,
    "w:endnoteRef": true,
    "w:tblPr": true,
    "w:tblGrid": true,
    "w:tcPr": true
};

function isParagraphProperties(element) {
    return element.type === "paragraphProperties";
}

function isRunProperties(element) {
    return element.type === "runProperties";
}

function negate(predicate) {
    return function(value) {
        return !predicate(value);
    };
}

function emptyResultWithMessages(messages) {
    return new ReadResult(null, null, messages);
}

function emptyResult() {
    return new ReadResult(null);
}

function elementResult(element) {
    return new ReadResult(element);
}

function elementResultWithMessages(element, messages) {
    return new ReadResult(element, null, messages);
}

function ReadResult(element, extra, messages) {
    this.value = element || [];
    this.extra = extra;
    this._result = new Result({
        element: this.value,
        extra: extra
    }, messages);
    this.messages = this._result.messages;
}

ReadResult.prototype.toExtra = function() {
    return new ReadResult(null, joinElements(this.extra, this.value), this.messages);
};

ReadResult.prototype.insertExtra = function() {
    var extra = this.extra;
    if (extra && extra.length) {
        return new ReadResult(joinElements(this.value, extra), null, this.messages);
    } else {
        return this;
    }
};

ReadResult.prototype.map = function(func) {
    var result = this._result.map(function(value) {
        return func(value.element);
    });
    return new ReadResult(result.value, this.extra, result.messages);
};

ReadResult.prototype.flatMap = function(func) {
    var result = this._result.flatMap(function(value) {
        return func(value.element)._result;
    });
    return new ReadResult(result.value.element, joinElements(this.extra, result.value.extra), result.messages);
};

function combineResults(results) {
    var result = Result.combine(_.pluck(results, "_result"));
    return new ReadResult(
        _.flatten(_.pluck(result.value, "element")),
        _.filter(_.flatten(_.pluck(result.value, "extra")), identity),
        result.messages
    );
}

function joinElements(first, second) {
    return _.flatten([first, second]);
}

function identity(value) {
    return value;
}

function parseHyperlinkFieldCode(code) {
    var result = /\s*HYPERLINK "(.*)"/.exec(code);
    if (result) {
        return result[1];
    } else {
        return null;
    }
}
<|endoftext|>


<commit_before>
/*

  /app/models/Item

 */

var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;

var ItemSchema = new Schema({
  id          : ObjectId,
  title       : { type : String },
  content     : { type : String },
  created_at  : { type : Date, default: Date.now }
});

var Item = mongoose.model("Item", ItemSchema);

/*
  Setup a small mongodb doc
*/

var itemA = new Item({
  title : "Item A",
  content : "This is item A's content!"
});

var itemB = new Item({
  title : "Item B",
  content : "This is item B's content, excited yet?"
});

// cleanup
Item.find({}, function(error, items) {
  items.forEach(function(item) {
    item.remove();
  });
});

itemA.save();
itemB.save();

module.exports = Item;

/* EOF */<commit_msg>items<commit_after>
/*

  /app/models/Item

 */

var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;

var ItemSchema = new Schema({
  id          : ObjectId,
  title       : { type : String },
  content     : { type : String },
  created_at  : { type : Date, default: Date.now }
});

var Item = mongoose.model("Item", ItemSchema);

/*
  Setup a small mongodb doc
*/

/*Item.find({}, function(error, items) {
  if (error) {
    console.error(error);
  } else {
    items.forEach(function(item) {
      item.remove();
    });
  };
});

var itemA = new Item({
  title : "Item A",
  content : "This is item A's content!"
});

var itemB = new Item({
  title : "Item B",
  content : "This is item B's content, excited yet?"
});

itemA.save();
itemB.save();
*/

module.exports = Item;

/* EOF */<|endoftext|>


<commit_before>'use strict';

class Relay16 {

  constructor() {
    const USB_VID = 0x0416;
    const USB_PID = 0x5020;
    const HID = require('node-hid');

    // Generated by mapRelays()
    this.relayBitmap = [128, 256, 64, 512, 32, 1024, 16, 2048, 8, 4096, 4, 8192, 2, 16384, 1, 32768];

    console.log('Detected devices:', HID.devices(USB_VID, USB_PID));
    this.hid = new HID.HID(USB_VID, USB_PID);
  }

  set(id, state) {
    if (typeof id !== 'number') {
      throw new Error(`Invalid relay ID type: ${id}`);
    }
    if (id < 0 || id > 15) {
      throw new Error(`Invalid relay ID: ${id}`);
    }
    if (typeof state !== 'boolean') {
      throw new Error(`Invalid state: ${state}`);
    }

    return this.read()
    .then((readmask) => {
      let bit = Math.pow(2, id);

      // Map the read mask into the write mask
      let mask = 0;
      for (let i = 0; i < 16; i++) {
        if (readmask & this.relayBitmap[i]) {
          mask = mask | Math.pow(2, i);
        }
      }

      if (state) {
        mask = mask | bit;
      } else {
        mask = mask ^ bit;
      }
      return this.write(mask);
    });
  }

  read() {
    return new Promise((resolve, reject) => {
      let readCmd = [
        0xD2, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x48, 0x49, 0x44,
        0x43, 0x80, 0x02, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
        0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
        0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
        0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      ];
      this.hid.write(readCmd);

      this.hid.read((err, data) => {
        if (err) {
          return reject('hid read error: ${err}');
        }

        let arr16 = new Uint16Array(1);
        let arr8 = new Uint8Array(arr16.buffer);
        arr8[0] = data[2];
        arr8[1] = data[3];

        let mask = arr16[0];
        return resolve(mask);
      });
    });
  }

  reset() {
    let resetCmd = [
      0x71, 0x0E, 0x71, 0x00, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x48, 0x49, 0x44,
      0x43, 0x2A, 0x02, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
    ];
    this.hid.write(resetCmd);
    return Promise.resolve();
  }

  write(mask) {
    if (typeof mask !== 'number' || mask < 0 || mask > 0xFFFF) {
      throw new Error(`Invalid write mask: ${mask}`);
    }

    let writeCmd = [
      0xC3, 0x0E, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x49, 0x44,
      0x43, 0xEE, 0x01, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
    ];

    let arr16 = new Uint16Array(1);
    let arr8 = new Uint8Array(arr16.buffer);
    arr16[0] = mask;

    writeCmd[2] = arr8[0];
    writeCmd[3] = arr8[1];

    const addCommandChecksum = (cmd) => {
      let size = cmd[1];
      let checksum = cmd.slice(0, size).reduce((a, b) => a + b, 0);

      let arr32 = new Uint32Array(1);
      let arr8 = new Uint8Array(arr32.buffer);
      arr32[0] = checksum;

      for (let i = 0; i < 4; i++) {
        cmd[size + i] = arr8[i];
      }
      return cmd;
    };

    this.hid.write(addCommandChecksum(writeCmd));
    return Promise.resolve();
  }
}

module.exports = Relay16;
<commit_msg>Add close<commit_after>'use strict';

class Relay16 {

  constructor() {
    const USB_VID = 0x0416;
    const USB_PID = 0x5020;
    const HID = require('node-hid');

    // Generated by mapRelays()
    this.relayBitmap = [128, 256, 64, 512, 32, 1024, 16, 2048, 8, 4096, 4, 8192, 2, 16384, 1, 32768];

    console.log('Detected devices:', HID.devices(USB_VID, USB_PID));
    this.hid = new HID.HID(USB_VID, USB_PID);
  }

  close() {
    this.hid.close();
  }

  set(id, state) {
    if (typeof id !== 'number') {
      throw new Error(`Invalid relay ID type: ${typeof id}`);
    }
    if (id < 0 || id > 15) {
      throw new Error(`Invalid relay ID: ${id}`);
    }
    if (typeof state !== 'boolean') {
      throw new Error(`Invalid state: ${state}`);
    }

    return this.read()
    .then((readmask) => {
      let bit = Math.pow(2, id);

      // Map the read mask into the write mask
      let mask = 0;
      for (let i = 0; i < 16; i++) {
        if (readmask & this.relayBitmap[i]) {
          mask = mask | Math.pow(2, i);
        }
      }

      if (state) {
        mask = mask | bit;
      } else {
        mask = mask ^ bit;
      }
      return this.write(mask);
    });
  }

  read() {
    return new Promise((resolve, reject) => {
      let readCmd = [
        0xD2, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x48, 0x49, 0x44,
        0x43, 0x80, 0x02, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
        0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
        0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
        0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      ];
      this.hid.write(readCmd);

      this.hid.read((err, data) => {
        if (err) {
          return reject('hid read error: ${err}');
        }

        let arr16 = new Uint16Array(1);
        let arr8 = new Uint8Array(arr16.buffer);
        arr8[0] = data[2];
        arr8[1] = data[3];

        let mask = arr16[0];
        return resolve(mask);
      });
    });
  }

  reset() {
    let resetCmd = [
      0x71, 0x0E, 0x71, 0x00, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x48, 0x49, 0x44,
      0x43, 0x2A, 0x02, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
    ];
    this.hid.write(resetCmd);
    return Promise.resolve();
  }

  write(mask) {
    if (typeof mask !== 'number' || mask < 0 || mask > 0xFFFF) {
      throw new Error(`Invalid write mask: ${mask}`);
    }

    let writeCmd = [
      0xC3, 0x0E, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x49, 0x44,
      0x43, 0xEE, 0x01, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
      0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
    ];

    let arr16 = new Uint16Array(1);
    let arr8 = new Uint8Array(arr16.buffer);
    arr16[0] = mask;

    writeCmd[2] = arr8[0];
    writeCmd[3] = arr8[1];

    const addCommandChecksum = (cmd) => {
      let size = cmd[1];
      let checksum = cmd.slice(0, size).reduce((a, b) => a + b, 0);

      let arr32 = new Uint32Array(1);
      let arr8 = new Uint8Array(arr32.buffer);
      arr32[0] = checksum;

      for (let i = 0; i < 4; i++) {
        cmd[size + i] = arr8[i];
      }
      return cmd;
    };

    this.hid.write(addCommandChecksum(writeCmd));
    return Promise.resolve();
  }
}

module.exports = Relay16;
<|endoftext|>


<commit_before>const React = require('react-native');
const {
  Animated,
  Component,
  Dimensions,
  ScrollView,
  StyleSheet,
  View
} = React;

const {
  any,
  func,
  number,
  string
} = React.PropTypes;

// Properties accepted by `ParallaxScrollView`.
const IPropTypes = {
  headerBackgroundColor: string,
  contentBackgroundColor: string,
  parallaxHeaderHeight: number.isRequired,
  renderParallaxHeader: func.isRequired,
  renderStickyHeader: func,
  renderFixedHeader: func,
  renderBackground: func,
  stickyHeaderHeight: number,
  style: any
};

class ParallaxScrollView extends Component {
  constructor(props) {
    super(props);
    if (props.renderStickyHeader && !props.stickyHeaderHeight) {
      console.error('Property `stickyHeaderHeight` must be set if `renderStickyHeader` is used')
    }
    this.state = { scrollY: new Animated.Value(0) };
    this._footerHeight = 0;
    this._animatedEvent = Animated.event([{nativeEvent: { contentOffset: { y: this.state.scrollY } } }]);
  }

  render() {
    const { scrollY } = this.state;
    const {
      children,
      contentBackgroundColor,
      headerBackgroundColor,
      parallaxHeaderHeight,
      stickyHeaderHeight,
      renderBackground,
      renderFixedHeader,
      renderParallaxHeader,
      renderStickyHeader,
      onScroll: prevOnScroll = () => {},
      style,
      ...scrollViewProps
    } = this.props;

    return (
      <View style={styles.container}>
        <Animated.View
          style={[styles.backgroundImage, {
            backgroundColor: headerBackgroundColor,
            height: parallaxHeaderHeight,
            width: window.width,
            transform: [{
              translateY: scrollY.interpolate({
                inputRange: [0, parallaxHeaderHeight - stickyHeaderHeight],
                outputRange: [0, -parallaxHeaderHeight],
                extrapolateRight: 'extend',
                extrapolateLeft: 'clamp'
              })
            }, {
              scale: scrollY.interpolate({
                inputRange: [-window.height, 0],
                outputRange: [5, 1],
                extrapolate: 'clamp'
              })
            }]
          }]}>
          <View>
            { renderBackground && renderBackground() }
          </View>
        </Animated.View>
        <ScrollView {...scrollViewProps}
            ref="ScrollView"
            style={[style, styles.scrollView]}
            scrollEventThrottle={16}
            onScroll={e => {
              this._animatedEvent(e);
              prevOnScroll(e);
            }}>
          <View style={styles.parallaxHeaderContainer}>
            <Animated.View
                style={[styles.parallaxHeader, {
                  height: scrollY.interpolate({
                    inputRange: [0, parallaxHeaderHeight - stickyHeaderHeight],
                    outputRange: [parallaxHeaderHeight, stickyHeaderHeight],
                    extrapolate: 'clamp'
                  }),
                  opacity: scrollY.interpolate({
                    inputRange: [0, (parallaxHeaderHeight - stickyHeaderHeight) / 2 - 20, (parallaxHeaderHeight - stickyHeaderHeight) / 2],
                    outputRange: [1, .9, 0],
                    extrapolate: 'extend'
                  })
                }]}>
              { renderParallaxHeader && renderParallaxHeader() }
            </Animated.View>
          </View>
          <View
              style={{ backgroundColor: contentBackgroundColor }}
              onLayout={e => {
                // Adjust the bottom height so we can scroll the parallax header all the way up.
                const { nativeEvent: { layout: { height } } } = e;
                const footerHeight = Math.max(0, window.height - height - stickyHeaderHeight);
                if (this._footerHeight !== footerHeight) {
                  this.refs.Footer.setNativeProps({ style: { height: footerHeight }});
                  this._footerHeight = footerHeight;
                }
              }}>
            { children }
          </View>
          <View ref="Footer" style={{ backgroundColor: contentBackgroundColor }}/>
        </ScrollView>
        { renderStickyHeader
          ? (
          <View style={[styles.stickyHeader, { height: stickyHeaderHeight }]}>
            <Animated.View
              style={{
                    backgroundColor: headerBackgroundColor,
                    height: stickyHeaderHeight,
                    opacity: scrollY.interpolate({
                      inputRange: [-window.height, 0, stickyHeaderHeight],
                      outputRange: [0, 0, 1],
                      extrapolate: 'clamp'
                    })
                  }}>
              <Animated.View
                style={{
                        transform: [{
                          translateY: scrollY.interpolate({
                            inputRange: [-window.height, 0, stickyHeaderHeight],
                            outputRange: [stickyHeaderHeight, stickyHeaderHeight, 0],
                            extrapolate: 'clamp'
                          })
                        }]
                      }}>
                { renderStickyHeader() }
              </Animated.View>
            </Animated.View>
            { renderFixedHeader && renderFixedHeader() }
          </View>
        )
          : null
        }
      </View>
    );
  }

  getScrollResponder() {
    return this.refs.ScrollView.getScrollResponder();
  }

  getInnerViewNode() {
    return this.getScrollResponder().getInnerViewNode();
  }

  scrollTo(destY, destX) {
    this.getScrollResponder().scrollTo(destY, destX);
  }

  scrollWithoutAnimationTo(destY, destX) {
    this.getScrollResponder().scrollWithoutAnimationTo(destY, destX);
  }

  setNativeProps(props) {
    this.refs.ScrollView.setNativeProps(props);
  }
}

ParallaxScrollView.propTypes = IPropTypes;

ParallaxScrollView.defaultProps = {
  headerBackgroundColor: '#000',
  contentBackgroundColor: '#fff',
  stickyHeaderHeight: 0,
  style: {}
};

const window = Dimensions.get('window');

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'transparent'
  },
  parallaxHeaderContainer: {
    backgroundColor: 'transparent',
    overflow: 'hidden'
  },
  parallaxHeader: {
    backgroundColor: 'transparent',
    overflow: 'hidden'
  },
  backgroundImage: {
    position: 'absolute',
    backgroundColor: 'transparent',
    top: 0
  },
  stickyHeader: {
    backgroundColor: 'transparent',
    position: 'absolute',
    overflow: 'hidden',
    top: 0,
    left: 0,
    width: window.width
  },
  scrollView: {
    backgroundColor: 'transparent'
  }
});

module.exports = ParallaxScrollView;
<commit_msg>exposes `renderScrollComponent` so users can provide their own ScrollView-like component<commit_after>const React = require('react-native');
const {
  Animated,
  Component,
  Dimensions,
  ScrollView,
  StyleSheet,
  View
} = React;

const {
  any,
  func,
  number,
  string
} = React.PropTypes;

const SCROLLVIEW_REF = 'ScrollView';

// Properties accepted by `ParallaxScrollView`.
const IPropTypes = {
  headerBackgroundColor: string,
  contentBackgroundColor: string,
  parallaxHeaderHeight: number.isRequired,
  renderParallaxHeader: func.isRequired,
  renderStickyHeader: func,
  renderScrollComponent: func,
  renderFixedHeader: func,
  renderBackground: func,
  stickyHeaderHeight: number,
  style: any
};

class ParallaxScrollView extends Component {
  constructor(props) {
    super(props);
    if (props.renderStickyHeader && !props.stickyHeaderHeight) {
      console.error('Property `stickyHeaderHeight` must be set if `renderStickyHeader` is used')
    }
    this.state = { scrollY: new Animated.Value(0) };
    this._footerComponent = null;
    this._footerHeight = 0;
    this._animatedEvent = Animated.event([{nativeEvent: { contentOffset: { y: this.state.scrollY } } }]);
  }

  render() {
    const {
      children,
      contentBackgroundColor,
      headerBackgroundColor,
      parallaxHeaderHeight,
      stickyHeaderHeight,
      renderBackground,
      renderFixedHeader,
      renderParallaxHeader,
      renderScrollComponent,
      renderStickyHeader,
      style,
      ...scrollViewProps
    } = this.props;

    const background = this._renderBackground({ headerBackgroundColor, parallaxHeaderHeight, stickyHeaderHeight, renderBackground });
    const parallaxHeader = this._renderParallaxHeader({ parallaxHeaderHeight, stickyHeaderHeight, renderParallaxHeader });
    const bodyComponent = this._wrapChildren(children, { contentBackgroundColor, stickyHeaderHeight });
    const footerSpacer = this._renderFooterSpacer({ contentBackgroundColor });
    const maybeStickyHeader = this._maybeRenderStickyHeader({ stickyHeaderHeight, headerBackgroundColor, renderFixedHeader, renderStickyHeader });

    return (
      <View style={styles.container}>
        { background }
        {
          React.cloneElement(renderScrollComponent(scrollViewProps), {
            ref: SCROLLVIEW_REF,
            style: [style, styles.scrollView],
            scrollEventThrottle: 16,
            onScroll: this._onScroll.bind(this),
          },
            parallaxHeader,
            bodyComponent,
            footerSpacer
          )
        }
        { maybeStickyHeader }
      </View>
    );
  }

  /*
   * Expose `ScrollView` API so this component is composable with any component that expects a `ScrollView`.
   */
  getScrollResponder() {
    return this.refs[SCROLLVIEW_REF].getScrollResponder();
  }
  getInnerViewNode() {
    return this.getScrollResponder().getInnerViewNode();
  }
  scrollTo(destY, destX) {
    this.getScrollResponder().scrollTo(destY, destX);
  }
  scrollWithoutAnimationTo(destY, destX) {
    this.getScrollResponder().scrollWithoutAnimationTo(destY, destX);
  }
  setNativeProps(props) {
    this.refs[SCROLLVIEW_REF].setNativeProps(props);
  }

  /*
   * Private helpers
   */

  _onScroll(e) {
    const { onScroll: prevOnScroll = () => {} } = this.props;
    this._animatedEvent(e);
    prevOnScroll(e);
  }

  _renderBackground({ headerBackgroundColor, parallaxHeaderHeight, stickyHeaderHeight, renderBackground }) {
    return (
      <Animated.View
        style={[styles.backgroundImage, {
            backgroundColor: headerBackgroundColor,
            height: parallaxHeaderHeight,
            width: window.width,
            transform: [{
              translateY: this.state.scrollY.interpolate({
                inputRange: [0, parallaxHeaderHeight - stickyHeaderHeight],
                outputRange: [0, -parallaxHeaderHeight],
                extrapolateRight: 'extend',
                extrapolateLeft: 'clamp'
              })
            }, {
              scale: this.state.scrollY.interpolate({
                inputRange: [-window.height, 0],
                outputRange: [5, 1],
                extrapolate: 'clamp'
              })
            }]
          }]}>
        <View>
          { renderBackground && renderBackground() }
        </View>
      </Animated.View>
    );
  }

  _renderParallaxHeader({ parallaxHeaderHeight, stickyHeaderHeight, renderParallaxHeader }) {
    return (
      <View style={styles.parallaxHeaderContainer}>
        <Animated.View
          style={[styles.parallaxHeader, {
                  height: this.state.scrollY.interpolate({
                    inputRange: [0, parallaxHeaderHeight - stickyHeaderHeight],
                    outputRange: [parallaxHeaderHeight, stickyHeaderHeight],
                    extrapolate: 'clamp'
                  }),
                  opacity: this.state.scrollY.interpolate({
                    inputRange: [0, (parallaxHeaderHeight - stickyHeaderHeight) / 2 - 20, (parallaxHeaderHeight - stickyHeaderHeight) / 2],
                    outputRange: [1, .9, 0],
                    extrapolate: 'extend'
                  })
                }]}>
          { renderParallaxHeader && renderParallaxHeader() }
        </Animated.View>
      </View>
    );
  }

  _wrapChildren(children, { contentBackgroundColor, stickyHeaderHeight }) {
    return (
      <View
        style={{ backgroundColor: contentBackgroundColor }}
        onLayout={e => {
                // Adjust the bottom height so we can scroll the parallax header all the way up.
                const { nativeEvent: { layout: { height } } } = e;
                const footerHeight = Math.max(0, window.height - height - stickyHeaderHeight);
                if (this._footerHeight !== footerHeight) {
                  this._footerComponent.setNativeProps({ style: { height: footerHeight }});
                  this._footerHeight = footerHeight;
                }
              }}>
        { children }
      </View>
    );
  }

  _renderFooterSpacer({ contentBackgroundColor }) {
    return (
      <View ref={ref => this._footerComponent = ref } style={{ backgroundColor: contentBackgroundColor }}/>
    );
  }

  _maybeRenderStickyHeader({ stickyHeaderHeight, headerBackgroundColor, renderFixedHeader, renderStickyHeader }) {
    if (renderStickyHeader) {
      return (
        <View style={[styles.stickyHeader, { height: stickyHeaderHeight }]}>
          <Animated.View
              style={{
                backgroundColor: headerBackgroundColor,
                height: stickyHeaderHeight,
                opacity: this.state.scrollY.interpolate({
                  inputRange: [-window.height, 0, stickyHeaderHeight],
                  outputRange: [0, 0, 1],
                  extrapolate: 'clamp'
                })
              }}>
            <Animated.View
                style={{
                  transform: [{
                    translateY: this.state.scrollY.interpolate({
                      inputRange: [-window.height, 0, stickyHeaderHeight],
                      outputRange: [stickyHeaderHeight, stickyHeaderHeight, 0],
                      extrapolate: 'clamp'
                    })
                  }]
                }}>
              { renderStickyHeader() }
            </Animated.View>
          </Animated.View>
          { renderFixedHeader && renderFixedHeader() }
        </View>
      );
    } else {
      return null;
    }
  }
}

ParallaxScrollView.propTypes = IPropTypes;

ParallaxScrollView.defaultProps = {
  headerBackgroundColor: '#000',
  contentBackgroundColor: '#fff',
  renderScrollComponent: props => <ScrollView {...props}/>,
  stickyHeaderHeight: 0,
  style: {}
};

const window = Dimensions.get('window');

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'transparent'
  },
  parallaxHeaderContainer: {
    backgroundColor: 'transparent',
    overflow: 'hidden'
  },
  parallaxHeader: {
    backgroundColor: 'transparent',
    overflow: 'hidden'
  },
  backgroundImage: {
    position: 'absolute',
    backgroundColor: 'transparent',
    top: 0
  },
  stickyHeader: {
    backgroundColor: 'transparent',
    position: 'absolute',
    overflow: 'hidden',
    top: 0,
    left: 0,
    width: window.width
  },
  scrollView: {
    backgroundColor: 'transparent'
  }
});

module.exports = ParallaxScrollView;
<|endoftext|>


<commit_before>"use strict";

var React      = require('react');
var TestUtils  = require('react-addons-test-utils');

/**
 * Wrapper class for React's TestUtils ShallowRenderer to overcome some of its limitations.
 */
function ShallowRenderer() {
    this._renderer = TestUtils.createRenderer();
}

ShallowRenderer.prototype.getRenderOutput = function getRenderOutput() {
    return this._renderer.getRenderOutput();
};

/**
 * Get the component instance used by the renderer.
 */
ShallowRenderer.prototype.getMountedInstance = function getMountedInstance() {
    // Hack to get component instance: https://github.com/facebook/react/pull/4918
    return this._renderer._instance ? this._renderer._instance._instance : null;
};

/**
 * Render the component with context.
 */
ShallowRenderer.prototype.render = function render(createElement, context) {
    if (!createElement) {
        throw new Error('Must pass `createElement` function to `ShallowRenderer#render`');
    }

    context = context || {};
    this._renderer.render(createElement(), context);

    return this;
};

ShallowRenderer.prototype.unmount = function unmount() {
    this._renderer.unmount();
};

var filterComponent = function (component, predicate) {
        var i;
        var results = [];
        if (predicate(component)) {
            results.push(component);
        }

        if (component && component.props && component.props.children && component.props.children.length) {
            for (i = 0; i < component.props.children.length; i++) {
                results = results.concat(filterComponent(component.props.children[i], predicate));
            }
        } else if (component && component.props && component.props.children) {
            results = results.concat(filterComponent(component.props.children, predicate));
        }

        return results;
};

var shallowHelpers = module.exports = {
    createRenderer: function () {
        return new ShallowRenderer();
    },
    renderWithContext: function (makeComponent, context) {
        var shallowRenderer = shallowHelpers.createRenderer();
        shallowRenderer.render(makeComponent, context);
        return shallowRenderer.getRenderOutput();
    },
    renderFactory: function(makeComponent) {
        return function(props, context) {
            return shallowHelpers.renderWithContext(function () {
                return React.createElement(makeComponent, props);
            }, context);
        }
    },
    filter: filterComponent,
    find: function () {
        return filterComponent.apply(this, arguments)[0];
    },
    hasClass: function (component, className) {
        var classes = component && component.props && component.props.className || '';
        var classArr = classes.split(' ');
        return classArr.indexOf(className) !== -1;
    },
    isType: function (component, type) {
        return component && component.type === type;
    },
    findType: function (component, type) {
        return shallowHelpers.find(component, function (c) {
            return shallowHelpers.isType(c, type);
        });
    },
    filterType: function (component, type) {
        return shallowHelpers.filter(component, function (c) {
            return shallowHelpers.isType(c, type);
        });
    },
    findClass: function (component, className) {
        return shallowHelpers.find(component, function(c) {
            return shallowHelpers.hasClass(c, className);
        });
    },
    filterClass: function (component, className) {
        return shallowHelpers.filter(component, function(c) {
            return shallowHelpers.hasClass(c, className);
        });
    }
};
<commit_msg>fix filter if props.children is an array and has an array at one of its indices<commit_after>"use strict";

var React      = require('react');
var TestUtils  = require('react-addons-test-utils');

/**
 * Wrapper class for React's TestUtils ShallowRenderer to overcome some of its limitations.
 */
function ShallowRenderer() {
    this._renderer = TestUtils.createRenderer();
}

ShallowRenderer.prototype.getRenderOutput = function getRenderOutput() {
    return this._renderer.getRenderOutput();
};

/**
 * Get the component instance used by the renderer.
 */
ShallowRenderer.prototype.getMountedInstance = function getMountedInstance() {
    // Hack to get component instance: https://github.com/facebook/react/pull/4918
    return this._renderer._instance ? this._renderer._instance._instance : null;
};

/**
 * Render the component with context.
 */
ShallowRenderer.prototype.render = function render(createElement, context) {
    if (!createElement) {
        throw new Error('Must pass `createElement` function to `ShallowRenderer#render`');
    }

    context = context || {};
    this._renderer.render(createElement(), context);

    return this;
};

ShallowRenderer.prototype.unmount = function unmount() {
    this._renderer.unmount();
};

var filterComponent = function (component, predicate) {
        var i;
        var j;
        var results = [];

        if (predicate(component)) {
            results.push(component);
        }

        if (component && component.props && component.props.children && component.props.children.length) {
            for (i = 0; i < component.props.children.length; i++) {
                results = results.concat(filterComponent(component.props.children[i], predicate));
            }
        } else if (component && component.props && component.props.children) {
            results = results.concat(filterComponent(component.props.children, predicate));
        } else if (Array.isArray(component)) {
            for (j = 0; j < component.length; j++) {
                results = results.concat(filterComponent(component[j], predicate));
            }
        }

        return results;
};

var shallowHelpers = module.exports = {
    createRenderer: function () {
        return new ShallowRenderer();
    },
    renderWithContext: function (makeComponent, context) {
        var shallowRenderer = shallowHelpers.createRenderer();
        shallowRenderer.render(makeComponent, context);
        return shallowRenderer.getRenderOutput();
    },
    renderFactory: function(makeComponent) {
        return function(props, context) {
            return shallowHelpers.renderWithContext(function () {
                return React.createElement(makeComponent, props);
            }, context);
        }
    },
    filter: filterComponent,
    find: function () {
        return filterComponent.apply(this, arguments)[0];
    },
    hasClass: function (component, className) {
        var classes = component && component.props && component.props.className || '';
        var classArr = classes.split(' ');
        return classArr.indexOf(className) !== -1;
    },
    isType: function (component, type) {
        return component && component.type === type;
    },
    findType: function (component, type) {
        return shallowHelpers.find(component, function (c) {
            return shallowHelpers.isType(c, type);
        });
    },
    filterType: function (component, type) {
        return shallowHelpers.filter(component, function (c) {
            return shallowHelpers.isType(c, type);
        });
    },
    findClass: function (component, className) {
        return shallowHelpers.find(component, function(c) {
            return shallowHelpers.hasClass(c, className);
        });
    },
    filterClass: function (component, className) {
        return shallowHelpers.filter(component, function(c) {
            return shallowHelpers.hasClass(c, className);
        });
    }
};
<|endoftext|>


<commit_before>'use strict';
var argv = process.argv;

module.exports = (function () {
	if ('FORCE_COLOR' in process.env) {
		return true;
	}

	if (argv.indexOf('--no-color') !== -1 ||
		argv.indexOf('--no-colors') !== -1 ||
		argv.indexOf('--color=false') !== -1) {
		return false;
	}

	if (argv.indexOf('--color') !== -1 ||
		argv.indexOf('--colors') !== -1 ||
		argv.indexOf('--color=true') !== -1 ||
		argv.indexOf('--color=always') !== -1) {
		return true;
	}

	if (process.stdout && !process.stdout.isTTY) {
		return false;
	}

	if ('UPSTART_JOB' in process.env) {
		return false;
	}

	if (process.platform === 'win32') {
		return true;
	}

	if ('COLORTERM' in process.env) {
		return true;
	}

	if (process.env.TERM === 'dumb') {
		return false;
	}

	if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) {
		return true;
	}

	return false;
})();
<commit_msg>remove the `UPSTART_JOB` check for now, until #21 is resolved<commit_after>'use strict';
var argv = process.argv;

module.exports = (function () {
	if ('FORCE_COLOR' in process.env) {
		return true;
	}

	if (argv.indexOf('--no-color') !== -1 ||
		argv.indexOf('--no-colors') !== -1 ||
		argv.indexOf('--color=false') !== -1) {
		return false;
	}

	if (argv.indexOf('--color') !== -1 ||
		argv.indexOf('--colors') !== -1 ||
		argv.indexOf('--color=true') !== -1 ||
		argv.indexOf('--color=always') !== -1) {
		return true;
	}

	if (process.stdout && !process.stdout.isTTY) {
		return false;
	}

	if (process.platform === 'win32') {
		return true;
	}

	if ('COLORTERM' in process.env) {
		return true;
	}

	if (process.env.TERM === 'dumb') {
		return false;
	}

	if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) {
		return true;
	}

	return false;
})();
<|endoftext|>


<commit_before>var crypto = require('crypto')
var EventEmitter = require('events').EventEmitter
var fs = require('fs')
var path = require('path')
var os = require('os')
var spawn = require('child_process').spawn
var Step = require('step')

var isWindows = /^win/.test(process.platform)
var PATH = isWindows ? process.env.Path : process.env.PATH;
var SEPARATOR = isWindows ? ';' : ':';

var emitter

// Template string for wrapper script.
var GIT_SSH_TEMPLATE = '#!/bin/sh\n' +
'exec ssh -i $key -o StrictHostKeyChecking=no "$@"\n'

function mkTempFile(prefix, suffix) {
    var randomStr = crypto.randomBytes(4).toString('hex')
    var name = prefix + randomStr + suffix
    var file = path.join(os.tmpDir(), name)
    // Hack for weird environments (nodejitsu didn't guarantee os.tmpDir() to already exist)
    try {
        fs.mkdirSync(os.tmpDir())
    } catch (e) {}

    return file
}

//
// Write the Git script template to enable use of the SSH private key
//
// *privKey* SSH private key.
// *file* (optional) filename of script.
// *keyMode* (optional) mode of key.
// *cb* callback function of signature function(err, tempateFile, keyFile).
//
function writeFiles(privKey, file, keyMode, cb) {
  // No file name - generate a random one under the system temp dir
  if (!file) {
    file = mkTempFile("_gitane", ".sh")
  }

  if (typeof(keyMode) === 'function') {
    cb = keyMode
    keyMode = 0600
  }

  var keyfile = mkTempFile("_gitaneid", ".key")
  var keyfileName = keyfile
  if (isWindows) {
    keyfileName = "\"" + keyfile.replace(/\\/g,"\\\\") + "\"";
  }

  var data = GIT_SSH_TEMPLATE.replace('$key', keyfileName)
  Step(
    function() {
      fs.writeFile(file, data, this.parallel())
      fs.writeFile(keyfile, privKey, this.parallel())
    },
    function(err) {
      if (err) {
        return cb(err, null)
      }
      // make script executable
      fs.chmod(file, 0755, this.parallel())
      // make key secret
      fs.chmod(keyfile, keyMode, this.parallel())
    },
    function(err) {
      if (err) {
        return cb(err, null)
      }

      return cb(null, file, keyfile)
    }
  )
}

//
// Run a command in a subprocess with GIT_SSH set to the correct value for
// SSH key.
//
// *baseDir* current working dir from which to execute git
// *privKey* SSH private key to use
// *cmd* command to run
// *keyMode* optional unix file mode of key
// *cb* callback function of signature function(err, stdout, stderr)
//
// or first argument may be an object with params same as above,
// with addition of *emitter* which is an EventEmitter for real-time stdout
// and stderr events. An optional *detached* option specifies whether the
// spawned process should be detached from this one, and defaults to true.
// Detachment means the git process won't hang trying to prompt for a password.
function run(baseDir, privKey, cmd, keyMode, cb) {
  var detached = true
  var spawnFn = spawn
  if (typeof(keyMode) === 'function') {
    cb = keyMode
    keyMode = 0600
  }

  if (typeof(baseDir) === 'object') {
    var opts = baseDir
    cb = privKey
    cmd = opts.cmd
    privKey = opts.privKey
    keyMode = opts.keyMode || 0600
    emitter = opts.emitter
    baseDir = opts.baseDir
    spawnFn = opts.spawn || spawn
    if (typeof(opts.detached) !== 'undefined') {
      detached = opts.detached
    }
  }

  var args;
  if (Array.isArray(cmd)) {
    args = cmd.slice(1);
    cmd = cmd[0];
  }
  else {
    var split = cmd.match(/"[^"]+"|'[^']+'|\S+/g)
    cmd = split[0]
    args = split.slice(1)
  }

  Step(
    function() {
      writeFiles(privKey, null, keyMode, this)
    },
    function(err, file, keyfile) {
      if (err) {
        console.log("Error writing files: %s", err)
        return cb(err, null)
      }
      this.file = file
      this.keyfile = keyfile
      var proc = spawnFn(cmd, args, {cwd: baseDir, env: {GIT_SSH: file, PATH:PATH}, detached: detached})
      proc.stdoutBuffer = ""
      proc.stderrBuffer = ""
      proc.stdout.setEncoding('utf8')
      proc.stderr.setEncoding('utf8')

      proc.stdout.on('data', function(buf) {
        if (typeof(emitter) === 'object') {
          emitter.emit('stdout', buf)
        }
        proc.stdoutBuffer += buf
      })

      proc.stderr.on('data', function(buf) {
        if (typeof(emitter) === 'object') {
          emitter.emit('stderr', buf)
        }
        proc.stderrBuffer += buf
      })

      var self = this
      proc.on('close', function(exitCode) {
        var err = null
        if (exitCode !== 0) {
          err = "process exited with status " + exitCode
        }
        self(err, proc.stdoutBuffer, proc.stderrBuffer, exitCode)
      })
      proc.on('error', function (err) {
        // prevent node from throwing an error. The error handling is
        // done in the 'close' handler.
      })
    },
    function(err, stdout, stderr, exitCode) {
      // cleanup temp files
      try {
        fs.unlink(this.file)
        fs.unlink(this.keyfile)
      } catch(e) {}

      cb(err, stdout, stderr, exitCode)
    }
  )
}

//
// convenience wrapper for clone. maybe add more later.
//
function clone(args, baseDir, privKey, cb) {
  run(baseDir, privKey, "git clone " + args, cb)
}

function addPath(str) {
  PATH = PATH + SEPARATOR + str
}

module.exports = {
  clone:clone,
  run:run,
  writeFiles:writeFiles,
  addPath:addPath,
}

<commit_msg>fix: deprecation warnings (#11)<commit_after>var crypto = require('crypto')
var EventEmitter = require('events').EventEmitter
var fs = require('fs')
var path = require('path')
var os = require('os')
var spawn = require('child_process').spawn
var Step = require('step')

var isWindows = /^win/.test(process.platform)
var PATH = isWindows ? process.env.Path : process.env.PATH;
var SEPARATOR = isWindows ? ';' : ':';

var emitter

// Template string for wrapper script.
var GIT_SSH_TEMPLATE = '#!/bin/sh\n' +
'exec ssh -i $key -o StrictHostKeyChecking=no "$@"\n'

function mkTempFile(prefix, suffix) {
    var randomStr = crypto.randomBytes(4).toString('hex')
    var name = prefix + randomStr + suffix
    var file = path.join(os.tmpdir(), name)
    // Hack for weird environments (nodejitsu didn't guarantee os.tmpdir() to already exist)
    try {
        fs.mkdirSync(os.tmpdir())
    } catch (e) {}

    return file
}

//
// Write the Git script template to enable use of the SSH private key
//
// *privKey* SSH private key.
// *file* (optional) filename of script.
// *keyMode* (optional) mode of key.
// *cb* callback function of signature function(err, tempateFile, keyFile).
//
function writeFiles(privKey, file, keyMode, cb) {
  // No file name - generate a random one under the system temp dir
  if (!file) {
    file = mkTempFile("_gitane", ".sh")
  }

  if (typeof(keyMode) === 'function') {
    cb = keyMode
    keyMode = 0600
  }

  var keyfile = mkTempFile("_gitaneid", ".key")
  var keyfileName = keyfile
  if (isWindows) {
    keyfileName = "\"" + keyfile.replace(/\\/g,"\\\\") + "\"";
  }

  var data = GIT_SSH_TEMPLATE.replace('$key', keyfileName)
  Step(
    function() {
      fs.writeFile(file, data, this.parallel())
      fs.writeFile(keyfile, privKey, this.parallel())
    },
    function(err) {
      if (err) {
        return cb(err, null)
      }
      // make script executable
      fs.chmod(file, 0755, this.parallel())
      // make key secret
      fs.chmod(keyfile, keyMode, this.parallel())
    },
    function(err) {
      if (err) {
        return cb(err, null)
      }

      return cb(null, file, keyfile)
    }
  )
}

//
// Run a command in a subprocess with GIT_SSH set to the correct value for
// SSH key.
//
// *baseDir* current working dir from which to execute git
// *privKey* SSH private key to use
// *cmd* command to run
// *keyMode* optional unix file mode of key
// *cb* callback function of signature function(err, stdout, stderr)
//
// or first argument may be an object with params same as above,
// with addition of *emitter* which is an EventEmitter for real-time stdout
// and stderr events. An optional *detached* option specifies whether the
// spawned process should be detached from this one, and defaults to true.
// Detachment means the git process won't hang trying to prompt for a password.
function run(baseDir, privKey, cmd, keyMode, cb) {
  var detached = true
  var spawnFn = spawn
  if (typeof(keyMode) === 'function') {
    cb = keyMode
    keyMode = 0600
  }

  if (typeof(baseDir) === 'object') {
    var opts = baseDir
    cb = privKey
    cmd = opts.cmd
    privKey = opts.privKey
    keyMode = opts.keyMode || 0600
    emitter = opts.emitter
    baseDir = opts.baseDir
    spawnFn = opts.spawn || spawn
    if (typeof(opts.detached) !== 'undefined') {
      detached = opts.detached
    }
  }

  var args;
  if (Array.isArray(cmd)) {
    args = cmd.slice(1);
    cmd = cmd[0];
  }
  else {
    var split = cmd.match(/"[^"]+"|'[^']+'|\S+/g)
    cmd = split[0]
    args = split.slice(1)
  }

  Step(
    function() {
      writeFiles(privKey, null, keyMode, this)
    },
    function(err, file, keyfile) {
      if (err) {
        console.log("Error writing files: %s", err)
        return cb(err, null)
      }
      this.file = file
      this.keyfile = keyfile
      var proc = spawnFn(cmd, args, {cwd: baseDir, env: {GIT_SSH: file, PATH:PATH}, detached: detached})
      proc.stdoutBuffer = ""
      proc.stderrBuffer = ""
      proc.stdout.setEncoding('utf8')
      proc.stderr.setEncoding('utf8')

      proc.stdout.on('data', function(buf) {
        if (typeof(emitter) === 'object') {
          emitter.emit('stdout', buf)
        }
        proc.stdoutBuffer += buf
      })

      proc.stderr.on('data', function(buf) {
        if (typeof(emitter) === 'object') {
          emitter.emit('stderr', buf)
        }
        proc.stderrBuffer += buf
      })

      var self = this
      proc.on('close', function(exitCode) {
        var err = null
        if (exitCode !== 0) {
          err = "process exited with status " + exitCode
        }
        self(err, proc.stdoutBuffer, proc.stderrBuffer, exitCode)
      })
      proc.on('error', function (err) {
        // prevent node from throwing an error. The error handling is
        // done in the 'close' handler.
      })
    },
    function(err, stdout, stderr, exitCode) {
      // cleanup temp files
      try {
        fs.unlinkSync(this.file)
        fs.unlinkSync(this.keyfile)
      } catch(e) {}

      cb(err, stdout, stderr, exitCode)
    }
  )
}

//
// convenience wrapper for clone. maybe add more later.
//
function clone(args, baseDir, privKey, cb) {
  run(baseDir, privKey, "git clone " + args, cb)
}

function addPath(str) {
  PATH = PATH + SEPARATOR + str
}

module.exports = {
  clone:clone,
  run:run,
  writeFiles:writeFiles,
  addPath:addPath,
}

<|endoftext|>


<commit_before>var fs = require("fs")
var lineReader = require('line-reader');

function getRoute(rootPath){
    return new Promise(function(resolve,reject){
        var counter = 0
        var result
        function readLine(file_path,route){
            if(typeof route!="object")return;
            lineReader.eachLine(file_path, function(line, last) {
                if(/^\/\//.test(line)){
                    line = line.substring(2).replace(/\s/g,"")
                    var arr = line.split(":")
                    if(arr.length>1)route[arr[0]] = arr[1]
                    else route.title = arr[0]
                    if(!last)return
                }
                counter--
                if(counter<1)resolve(result)
                return false;
            });
        }
        function walk(dir,path) {
            var url = path.length>rootPath.length?"/"+dir.substring(rootPath.length,dir.length-1):"/"
            var data = {path:path,url:url,component:"",childRoutes:[]}
            dir = /\/$/.test(dir) ? dir : dir + '/';
            var files = fs.readdirSync(dir);
            files.forEach(function (item, next) {
                var info = fs.statSync(dir + item);
                if (info.isDirectory()) {
                    data.childRoutes.push(walk(dir + item + '/',item))
                }
                else if(/\.jsx?$/.test(item)) {
                    var name = item.replace(/\.jsx?$/,"")
                    // 路由相对地址
                    var importPath = "./"+(dir+item).substring(rootPath.length)
                    var route = {component:importPath,url:"/"+(dir+name).substring(rootPath.length)}
                    // 添加 indexRoute
                    if(name=="index")data.indexRoute = route
                    // 添加 childRoutes
                    else {
                        route.path = name
                        data.childRoutes.push(route)
                    }
                    counter++
                    // 添加 Route属性
                    readLine(dir+item,route)
                }
            })
            return data
        }
        result = walk(rootPath,"/")
    })
}

module.exports = function(source, map) {
    this.cacheable();
    var callback = this.async();
    getRoute("src/views/").then((data)=>{
        source = "module.exports = " + JSON.stringify(data, undefined, "\t") + ";";
        callback(null, source, map);
    })
};
<commit_msg>修复参数中包含:时读取不到<commit_after>var fs = require("fs")
var lineReader = require('line-reader');

function getRoute(rootPath){
    return new Promise(function(resolve,reject){
        var counter = 0
        var result
        function readLine(file_path,route){
            if(typeof route!="object")return;
            lineReader.eachLine(file_path, function(line, last) {
                if(/^\/\//.test(line)){
                    line = line.substring(2).replace(/\s/g,"")
                    var index = line.indexOf(":")
                    if(index>0)route[line.substring(0,index)] = line.substring(index+1)
                    else route.title = line
                    if(!last)return
                }
                counter--
                if(counter<1)resolve(result)
                return false;
            });
        }
        function walk(dir,path) {
            var url = path.length>rootPath.length?"/"+dir.substring(rootPath.length,dir.length-1):"/"
            var data = {path:path,url:url,component:"",childRoutes:[]}
            dir = /\/$/.test(dir) ? dir : dir + '/';
            var files = fs.readdirSync(dir);
            files.forEach(function (item, next) {
                var info = fs.statSync(dir + item);
                if (info.isDirectory()) {
                    data.childRoutes.push(walk(dir + item + '/',item))
                }
                else if(/\.jsx?$/.test(item)) {
                    var name = item.replace(/\.jsx?$/,"")
                    // 路由相对地址
                    var importPath = "./"+(dir+item).substring(rootPath.length)
                    var route = {component:importPath,url:"/"+(dir+name).substring(rootPath.length)}
                    // 添加 indexRoute
                    if(name=="index")data.indexRoute = route
                    // 添加 childRoutes
                    else {
                        route.path = name
                        data.childRoutes.push(route)
                    }
                    counter++
                    // 添加 Route属性
                    readLine(dir+item,route)
                }
            })
            return data
        }
        result = walk(rootPath,"/")
    })
}

module.exports = function(source, map) {
    this.cacheable();
    var callback = this.async();
    getRoute("src/views/").then((data)=>{
        source = "module.exports = " + JSON.stringify(data, undefined, "\t") + ";";
        callback(null, source, map);
    })
};
<|endoftext|>


<commit_before>var Bundle  = require("../plugins/bundle");

module.exports = Bundle;<commit_msg>fixing index<commit_after>var Bundle  = require("./plugins/bundle");

module.exports = Bundle;<|endoftext|>


<commit_before>enterUrlMsg = "Drag-and-drop the video or paste its URL here...";

removeImgSrc = "//cdn.rawgit.com/iconic/open-iconic/master/png/x-4x.png";
pauseImgSrc = "//cdn.rawgit.com/iconic/open-iconic/master/png/media-pause-4x.png";
playImgSrc = "//cdn.rawgit.com/iconic/open-iconic/master/png/media-play-4x.png";

var videoUrl;
var videoName;
var videoTime = null;

var videos = [];
var videoCounter = 0;
var videoIteration = 0;
var videoPaused;
var timer;

function Timer(callback, delay) {
  var timerId, start, remaining = delay;

  this.pause = function() {
      window.clearTimeout(timerId);
      remaining -= new Date() - start;
  };

  this.resume = function() {
      start = new Date();
      window.clearTimeout(timerId);
      timerId = window.setTimeout(callback, remaining);
  };

  this.resume();
}

function msConversion(millis) {
  var minutes = Math.floor(millis / 60000);
  var seconds = ((millis % 60000) / 1000).toFixed(0);
  return minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
}

function highlight(i) {
  $("tr:nth-child(" + i + ")").attr("id", "newSelected");
  $("tr.selected").removeClass("selected");
  $("#newSelected").addClass("selected");
  $("#newSelected").removeAttr("id");
}

function addVideoToList(name, time) {
  $("#videosTable").append("
" + name + "<button class=\"tableButton\" onclick=\"removeVideo(this);\"></button>	" + time + "
");
}

function playVideo() {
  highlight(videoIteration);
  document.title = "Streamly - " + videos[videoIteration]["name"];

  var embedUrl = videos[videoIteration]["url"].replace("/watch?v=", "/embed/") + "?autoplay=1";
  $("#youtube").attr("src", embedUrl);
  $("#pauseImg").attr("src", pauseImgSrc);
}

function loopVideo() {
  videoIteration++;
  playVideo();
  timer = new Timer(function() {
    if (videoIteration < videoCounter) {
      loopVideo();
    }
    else {
      timer.pause();
      timer = 0;
      $("#youtube").attr("src", "");
      document.title = "Streamly";
    }
  }, videos[videoIteration]["time"] + 2000);
}

function pauseVideo() {
  if (!videoPaused) {
    timer.pause();
    $("#pauseImg").attr("src", playImgSrc);
    videoPaused = true;
  }
  else {
    timer.resume();
    $("#pauseImg").attr("src", pauseImgSrc);
    videoPaused = false;
  }
}

function backVideo() {
  if (videoIteration - 2 > -1) {
    videoIteration = videoIteration - 2;
    if (timer != 0) {
      timer.pause();
    }
    timer = 0;
    loopVideo();
  }
}

function forwardVideo() {
  if (videoIteration + 1 <= videoCounter) {
    if (timer != 0) {
      timer.pause();
    }
    timer = 0;
    loopVideo();
  }
}

function setPlaylist() {
  if (videos.length > 1) {
    var playlist = JSON.stringify(videos);
    playlist = window.btoa(playlist);
    playlist = encodeURIComponent(playlist);
    window.location.hash = playlist;
  }
  else {
    window.location.hash = "";
  }
}

function getPlaylist() {
  if (window.location.hash.substr(1) !== "") {
    var playlist = window.location.hash.substr(1);
    playlist = decodeURIComponent(playlist);
    playlist = window.atob(playlist);
    playlist = JSON.parse(playlist);
    videos = playlist;

    for (i = 1; i < videos.length; i++) {
      videoCounter = i;
      var printTime = msConversion(videos[videoCounter]["time"]);
      addVideoToList(videos[videoCounter]["name"], printTime);
    }
    loopVideo();
  }
}

function getVideoData() {
  $.ajax({
    url: videoUrl,
    type: 'GET',
    success: function(res) {
      var data = $(res.responseText);
      try {
        videoName = data.find("span#eow-title");
        videoName = videoName[0].textContent;
        videoName = videoName.trim();
      } catch(err) {
        videoName = prompt("Please enter the name of the video", "");
      }
      try {
        videoTime = null;
        for (iteration in data) {
          var str = data[iteration].innerHTML;
          if (videoTime == null && typeof str != "undefined") {
            videoTime = str.match(/,"length_seconds":"\d+",/g);
          }
        }
        videoTime = videoTime[0];
        videoTime = videoTime.replace(/,"length_seconds":"/g, "").replace(/",/g, "");
        videoTime = +videoTime * 1000;
      } catch(err) {
        videoTime = prompt("Please enter the length of the video", "3:00");
        videoTime = videoTime.split(":");
        videoTime = (+videoTime[0]) * 60 + (+videoTime[1]);
        videoTime = videoTime * 1000;
      }
    }
  });
}

function addVideo() {
  videoCounter++;
  var video = {};
  video["name"] = videoName;
  video["time"] = videoTime;
  video["url"] = videoUrl;
  videos[videoCounter] = video;

  var printTime = msConversion(videos[videoCounter]["time"]);

  addVideoToList(videoName, printTime);

  setPlaylist();

  if (videoCounter == 1 || timer == 0) {
    loopVideo();
  }
}

function removeVideo(element) {
  var index = $(".tableButton").index(element) + 1;
  if (index == videoIteration) {
    if (videoIteration + 1 <= videoCounter) {
      forwardVideo();
    }
    else {
      if (timer != 0) {
        timer.pause();
      }
      timer = 0;
      $("#youtube").attr("src", "");
      document.title = "Streamly";
    }
  }
  videoCounter--;
  videos.splice(index, 1);
  $("tr:nth-child(" + index + ")").remove();
  setPlaylist();
}

function input() {
  switch ($("#inputBox").attr("placeholder")) {
    case enterUrlMsg:
      if ($("#inputBox").val() != "") {
        videoUrl = $("#inputBox").val();
        videoUrl = videoUrl.trim();
        $("#inputBox").val("").attr("placeholder", "Loading video data from YouTube...");
        getVideoData();
      }
      else {
        window.open("https://www.youtube.com");
      }
      break;
  }
}

$(document).ajaxStop(function() {
  $("#inputBox").val("").attr("placeholder", enterUrlMsg);
  addVideo();
});

function onDrop(event) {
  var data = event.dataTransfer.getData("URL");
  event.target.textContent = data;
  $("#inputBox").val(data);
  input();
  event.preventDefault();
}
<commit_msg>if video behind current iteration deleted, bring iteration down<commit_after>enterUrlMsg = "Drag-and-drop the video or paste its URL here...";

removeImgSrc = "//cdn.rawgit.com/iconic/open-iconic/master/png/x-4x.png";
pauseImgSrc = "//cdn.rawgit.com/iconic/open-iconic/master/png/media-pause-4x.png";
playImgSrc = "//cdn.rawgit.com/iconic/open-iconic/master/png/media-play-4x.png";

var videoUrl;
var videoName;
var videoTime = null;

var videos = [];
var videoCounter = 0;
var videoIteration = 0;
var videoPaused;
var timer;

function Timer(callback, delay) {
  var timerId, start, remaining = delay;

  this.pause = function() {
      window.clearTimeout(timerId);
      remaining -= new Date() - start;
  };

  this.resume = function() {
      start = new Date();
      window.clearTimeout(timerId);
      timerId = window.setTimeout(callback, remaining);
  };

  this.resume();
}

function msConversion(millis) {
  var minutes = Math.floor(millis / 60000);
  var seconds = ((millis % 60000) / 1000).toFixed(0);
  return minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
}

function highlight(i) {
  $("tr:nth-child(" + i + ")").attr("id", "newSelected");
  $("tr.selected").removeClass("selected");
  $("#newSelected").addClass("selected");
  $("#newSelected").removeAttr("id");
}

function addVideoToList(name, time) {
  $("#videosTable").append("
" + name + "<button class=\"tableButton\" onclick=\"removeVideo(this);\"></button>	" + time + "
");
}

function playVideo() {
  highlight(videoIteration);
  document.title = "Streamly - " + videos[videoIteration]["name"];

  var embedUrl = videos[videoIteration]["url"].replace("/watch?v=", "/embed/") + "?autoplay=1";
  $("#youtube").attr("src", embedUrl);
  $("#pauseImg").attr("src", pauseImgSrc);
}

function loopVideo() {
  videoIteration++;
  playVideo();
  timer = new Timer(function() {
    if (videoIteration < videoCounter) {
      loopVideo();
    }
    else {
      timer.pause();
      timer = 0;
      $("#youtube").attr("src", "");
      document.title = "Streamly";
    }
  }, videos[videoIteration]["time"] + 2000);
}

function pauseVideo() {
  if (!videoPaused) {
    timer.pause();
    $("#pauseImg").attr("src", playImgSrc);
    videoPaused = true;
  }
  else {
    timer.resume();
    $("#pauseImg").attr("src", pauseImgSrc);
    videoPaused = false;
  }
}

function backVideo() {
  if (videoIteration - 2 > -1) {
    videoIteration = videoIteration - 2;
    if (timer != 0) {
      timer.pause();
    }
    timer = 0;
    loopVideo();
  }
}

function forwardVideo() {
  if (videoIteration + 1 <= videoCounter) {
    if (timer != 0) {
      timer.pause();
    }
    timer = 0;
    loopVideo();
  }
}

function setPlaylist() {
  if (videos.length > 1) {
    var playlist = JSON.stringify(videos);
    playlist = window.btoa(playlist);
    playlist = encodeURIComponent(playlist);
    window.location.hash = playlist;
  }
  else {
    window.location.hash = "";
  }
}

function getPlaylist() {
  if (window.location.hash.substr(1) !== "") {
    var playlist = window.location.hash.substr(1);
    playlist = decodeURIComponent(playlist);
    playlist = window.atob(playlist);
    playlist = JSON.parse(playlist);
    videos = playlist;

    for (i = 1; i < videos.length; i++) {
      videoCounter = i;
      var printTime = msConversion(videos[videoCounter]["time"]);
      addVideoToList(videos[videoCounter]["name"], printTime);
    }
    loopVideo();
  }
}

function getVideoData() {
  $.ajax({
    url: videoUrl,
    type: 'GET',
    success: function(res) {
      var data = $(res.responseText);
      try {
        videoName = data.find("span#eow-title");
        videoName = videoName[0].textContent;
        videoName = videoName.trim();
      } catch(err) {
        videoName = prompt("Please enter the name of the video", "");
      }
      try {
        videoTime = null;
        for (iteration in data) {
          var str = data[iteration].innerHTML;
          if (videoTime == null && typeof str != "undefined") {
            videoTime = str.match(/,"length_seconds":"\d+",/g);
          }
        }
        videoTime = videoTime[0];
        videoTime = videoTime.replace(/,"length_seconds":"/g, "").replace(/",/g, "");
        videoTime = +videoTime * 1000;
      } catch(err) {
        videoTime = prompt("Please enter the length of the video", "3:00");
        videoTime = videoTime.split(":");
        videoTime = (+videoTime[0]) * 60 + (+videoTime[1]);
        videoTime = videoTime * 1000;
      }
    }
  });
}

function addVideo() {
  videoCounter++;
  var video = {};
  video["name"] = videoName;
  video["time"] = videoTime;
  video["url"] = videoUrl;
  videos[videoCounter] = video;

  var printTime = msConversion(videos[videoCounter]["time"]);

  addVideoToList(videoName, printTime);

  setPlaylist();

  if (videoCounter == 1 || timer == 0) {
    loopVideo();
  }
}

function removeVideo(element) {
  var index = $(".tableButton").index(element) + 1;
  if (index == videoIteration) {
    if (videoIteration + 1 <= videoCounter) {
      forwardVideo();
    }
    else {
      if (timer != 0) {
        timer.pause();
      }
      timer = 0;
      $("#youtube").attr("src", "");
      document.title = "Streamly";
    }
  } else if (index < videoIteration) {
    videoIteration--;
  }
  videoCounter--;
  videos.splice(index, 1);
  $("tr:nth-child(" + index + ")").remove();
  setPlaylist();
}

function input() {
  switch ($("#inputBox").attr("placeholder")) {
    case enterUrlMsg:
      if ($("#inputBox").val() != "") {
        videoUrl = $("#inputBox").val();
        videoUrl = videoUrl.trim();
        $("#inputBox").val("").attr("placeholder", "Loading video data from YouTube...");
        getVideoData();
      }
      else {
        window.open("https://www.youtube.com");
      }
      break;
  }
}

$(document).ajaxStop(function() {
  $("#inputBox").val("").attr("placeholder", enterUrlMsg);
  addVideo();
});

function onDrop(event) {
  var data = event.dataTransfer.getData("URL");
  event.target.textContent = data;
  $("#inputBox").val(data);
  input();
  event.preventDefault();
}
<|endoftext|>