<commit_before>//  Copyright (c) 2014 Readium Foundation and/or its licensees. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification,
//  are permitted provided that the following conditions are met:
//  1. Redistributions of source code must retain the above copyright notice, this
//  list of conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright notice,
//  this list of conditions and the following disclaimer in the documentation and/or
//  other materials provided with the distribution.
//  3. Neither the name of the organization nor the names of its contributors may be
//  used to endorse or promote products derived from this software without specific
//  prior written permission.

(function(global) {

var init = function($, _, cfiRuntimeErrors) {

var obj = {

// Description: This model contains the implementation for "instructions" included in the EPUB CFI domain specific language (DSL).
//   Lexing and parsing a CFI produces a set of executable instructions for processing a CFI (represented in the AST).
//   This object contains a set of functions that implement each of the executable instructions in the AST.

    // ------------------------------------------------------------------------------------ //
    //  "PUBLIC" METHODS (THE API)                                                          //
    // ------------------------------------------------------------------------------------ //

    // Description: Follows a step
    // Rationale: The use of children() is important here, as this jQuery method returns a tree of xml nodes, EXCLUDING
    //   CDATA and text nodes. When we index into the set of child elements, we are assuming that text nodes have been
    //   excluded.
    // REFACTORING CANDIDATE: This should be called "followIndexStep"
    getNextNode : function (CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist) {

        // Find the jquery index for the current node
        var $targetNode;
        if (CFIStepValue % 2 == 0) {

            $targetNode = this.elementNodeStep(CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist);
        }
        else {

            $targetNode = this.inferTargetTextNode(CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist);
        }

        return $targetNode;
    },

    // Description: This instruction executes an indirection step, where a resource is retrieved using a
    //   link contained on a attribute of the target element. The attribute that contains the link differs
    //   depending on the target.
    // Note: Iframe indirection will (should) fail if the iframe is not from the same domain as its containing script due to
    //   the cross origin security policy
    followIndirectionStep : function (CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist) {

        var that = this;
        var $contentDocument;
        var $blacklistExcluded;
        var $startElement;
        var $targetNode;

        // TODO: This check must be expanded to all the different types of indirection step
        // Only expects iframes, at the moment
        if ($currNode === undefined || !this._matchesLocalNameOrElement($currNode[0], 'iframe')) {

            throw cfiRuntimeErrors.NodeTypeError($currNode, "expected an iframe element");
        }

        // Check node type; only iframe indirection is handled, at the moment
        if (this._matchesLocalNameOrElement($currNode[0], 'iframe')) {

            // Get content
            $contentDocument = $currNode.contents();

            // Go to the first XHTML element, which will be the first child of the top-level document object
            $blacklistExcluded = this.applyBlacklist($contentDocument.children(), classBlacklist, elementBlacklist, idBlacklist);
            $startElement = $($blacklistExcluded[0]);

            // Follow an index step
            $targetNode = this.getNextNode(CFIStepValue, $startElement, classBlacklist, elementBlacklist, idBlacklist);

            // Return that shit!
            return $targetNode;
        }

        // TODO: Other types of indirection
        // TODO: $targetNode.is("embed")) : src
        // TODO: ($targetNode.is("object")) : data
        // TODO: ($targetNode.is("image") || $targetNode.is("xlink:href")) : xlink:href
    },

    // Description: Injects an element at the specified text node
    // Arguments: a cfi text termination string, a jquery object to the current node
    // REFACTORING CANDIDATE: Rename this to indicate that it injects into a text terminus
    textTermination : function ($currNode, textOffset, elementToInject) {

        var $injectedElement;
        // Get the first node, this should be a text node
        if ($currNode === undefined) {

            throw cfiRuntimeErrors.NodeTypeError($currNode, "expected a terminating node, or node list");
        }
        else if ($currNode.length === 0) {

            throw cfiRuntimeErrors.TerminusError("Text", "Text offset:" + textOffset, "no nodes found for termination condition");
        }

        $injectedElement = this.injectCFIMarkerIntoText($currNode, textOffset, elementToInject);
        return $injectedElement;
    },

    // Description: Checks that the id assertion for the node target matches that on
    //   the found node.
    targetIdMatchesIdAssertion : function ($foundNode, idAssertion) {

        if ($foundNode.attr("id") === idAssertion) {

            return true;
        }
        else {

            return false;
        }
    },

    // ------------------------------------------------------------------------------------ //
    //  "PRIVATE" HELPERS                                                                   //
    // ------------------------------------------------------------------------------------ //

    // Description: Step reference for xml element node. Expected that CFIStepValue is an even integer
    elementNodeStep : function (CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist) {

        var $targetNode;
        var $blacklistExcluded;
        var numElements;
        var jqueryTargetNodeIndex = (CFIStepValue / 2) - 1;

        $blacklistExcluded = this.applyBlacklist($currNode.children(), classBlacklist, elementBlacklist, idBlacklist);
        numElements = $blacklistExcluded.length;

        if (this.indexOutOfRange(jqueryTargetNodeIndex, numElements)) {

            throw cfiRuntimeErrors.OutOfRangeError(jqueryTargetNodeIndex, numElements - 1, "");
        }

        $targetNode = $($blacklistExcluded[jqueryTargetNodeIndex]);
        return $targetNode;
    },

    retrieveItemRefHref : function ($itemRefElement, $packageDocument) {

        return $("#" + $itemRefElement.attr("idref"), $packageDocument).attr("href");
    },

    indexOutOfRange : function (targetIndex, numChildElements) {

        return (targetIndex > numChildElements - 1) ? true : false;
    },

    // Rationale: In order to inject an element into a specific position, access to the parent object
    //   is required. This is obtained with the jquery parent() method. An alternative would be to
    //   pass in the parent with a filtered list containing only children that are part of the target text node.
    injectCFIMarkerIntoText : function ($textNodeList, textOffset, elementToInject) {
        var document = $textNodeList[0].ownerDocument;

        var nodeNum;
        var currNodeLength;
        var currTextPosition = 0;
        var nodeOffset;
        var originalText;
        var $injectedNode;
        var $newTextNode;
        // The iteration counter may be incorrect here (should be $textNodeList.length - 1 ??)
        for (nodeNum = 0; nodeNum <= $textNodeList.length; nodeNum++) {

            if ($textNodeList[nodeNum].nodeType === Node.TEXT_NODE) {

                currNodeMaxIndex = $textNodeList[nodeNum].nodeValue.length  + currTextPosition;
                nodeOffset = textOffset - currTextPosition;

                if (currNodeMaxIndex > textOffset) {

                    // This node is going to be split and the components re-inserted
                    originalText = $textNodeList[nodeNum].nodeValue;

                    // Before part
                    $textNodeList[nodeNum].nodeValue = originalText.slice(0, nodeOffset);

                    // Injected element
                    $injectedNode = $(elementToInject).insertAfter($textNodeList.eq(nodeNum));

                    // After part
                    $newTextNode = $(document.createTextNode(originalText.slice(nodeOffset, originalText.length)));
                    $($newTextNode).insertAfter($injectedNode);

                    return $injectedNode;
                } else if (currNodeMaxIndex == textOffset){
                    $injectedNode = $(elementToInject).insertAfter($textNodeList.eq(nodeNum));
                    return $injectedNode;
                }
                else {
                    currTextPosition = currNodeMaxIndex;
                }
            } else if($textNodeList[nodeNum].nodeType === Node.COMMENT_NODE){
                currNodeMaxIndex = $textNodeList[nodeNum].nodeValue.length + 7 + currTextPosition;
                currTextPosition = currNodeMaxIndex;
            } else if($textNodeList[nodeNum].nodeType === Node.PROCESSING_INSTRUCTION_NODE){
                currNodeMaxIndex = $textNodeList[nodeNum].nodeValue.length + $textNodeList[nodeNum].target.length + 5
                currTextPosition = currNodeMaxIndex;
            }
        }

        throw cfiRuntimeErrors.TerminusError("Text", "Text offset:" + textOffset, "The offset exceeded the length of the text");
    },

    // Rationale: In order to inject an element into a specific position, access to the parent object
    //   is required. This is obtained with the jquery parent() method. An alternative would be to
    //   pass in the parent with a filtered list containing only children that are part of the target text node.

    // Description: This method finds a target text node and then injects an element into the appropriate node
    // Rationale: The possibility that cfi marker elements have been injected into a text node at some point previous to
    //   this method being called (and thus splitting the original text node into two separate text nodes) necessitates that
    //   the set of nodes that compromised the original target text node are inferred and returned.
    // Notes: Passed a current node. This node should have a set of elements under it. This will include at least one text node,
    //   element nodes (maybe), or possibly a mix.
    // REFACTORING CANDIDATE: This method is pretty long (and confusing). Worth investigating to see if it can be refactored into something clearer.
    inferTargetTextNode : function (CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist) {

        var $elementsWithoutMarkers;
        var currLogicalTextNodeIndex;
        var targetLogicalTextNodeIndex;
        var nodeNum;
        var $targetTextNodeList;
        var prevNodeWasTextNode;

        // Remove any cfi marker elements from the set of elements.
        // Rationale: A filtering function is used, as simply using a class selector with jquery appears to
        //   result in behaviour where text nodes are also filtered out, along with the class element being filtered.
        $elementsWithoutMarkers = this.applyBlacklist($currNode.contents(), classBlacklist, elementBlacklist, idBlacklist);

        // Convert CFIStepValue to logical index; assumes odd integer for the step value
        targetLogicalTextNodeIndex = ((parseInt(CFIStepValue) + 1) / 2) - 1;

        // Set text node position counter
        currLogicalTextNodeIndex = 0;
        prevNodeWasTextNode = false;
        $targetTextNodeList = $elementsWithoutMarkers.filter(
            function () {

                if (currLogicalTextNodeIndex === targetLogicalTextNodeIndex) {

                    // If it's a text node
                    if (this.nodeType === Node.TEXT_NODE || this.nodeType === Node.COMMENT_NODE || this.nodeType === Node.PROCESSING_INSTRUCTION_NODE) {
                        prevNodeWasTextNode = true;
                        return true;
                    }
                    // Rationale: The logical text node position is only incremented once a group of text nodes (a single logical
                    //   text node) has been passed by the loop.
                    else if (prevNodeWasTextNode && (this.nodeType !== Node.TEXT_NODE)) {
                        currLogicalTextNodeIndex++;
                        prevNodeWasTextNode = false;
                        return false;
                    }
                }
                // Don't return any elements
                else {

                    if (this.nodeType === Node.TEXT_NODE || this.nodeType === Node.COMMENT_NODE || this.nodeType === Node.PROCESSING_INSTRUCTION_NODE) {
                        prevNodeWasTextNode = true;
                    }else if (!prevNodeWasTextNode && this.nodeType === Node.ELEMENT_NODE){
                        currLogicalTextNodeIndex++;
                        prevNodeWasTextNode = true;
                    }
                    else if (prevNodeWasTextNode && (this.nodeType !== Node.TEXT_NODE) && (this !== $elementsWithoutMarkers.lastChild)) {
                        currLogicalTextNodeIndex++;
                        prevNodeWasTextNode = false;
                    }

                    return false;
                }
            }
        );

        // The filtering above should have counted the number of "logical" text nodes; this can be used to
        // detect out of range errors
        if ($targetTextNodeList.length === 0) {
            throw cfiRuntimeErrors.OutOfRangeError(targetLogicalTextNodeIndex, currLogicalTextNodeIndex, "Index out of range");
        }

        // return the text node list
        return $targetTextNodeList;
    },

    applyBlacklist : function ($elements, classBlacklist, elementBlacklist, idBlacklist) {
        var self = this;
        var $filteredElements;

        $filteredElements = $elements.filter(
            function () {

                var element = this;

                if (classBlacklist && classBlacklist.length) {
                    var classAttribute = element.className;
                    var classList = classAttribute ? classAttribute.split(' ') : [];
                    if (classList.length === 1 && _.contains(classBlacklist, classList[0])) {
                        return false;
                    } else if (classList.length && _.intersection(classBlacklist, classList).length) {
                        return false;
                    }
                }

                if (elementBlacklist && elementBlacklist.length) {
                    if (element.tagName) {
                        var isElementBlacklisted = _.find(elementBlacklist, function (blacklistedTag) {
                            blacklistedTag = blacklistedTag.toLowerCase();
                            return self._matchesLocalNameOrElement(element, blacklistedTag)
                        });
                        if (isElementBlacklisted) {
                            return false;
                        }
                    }
                }

                if (idBlacklist && idBlacklist.length) {
                    var id = element.id;
                    if (id && id.length && _.contains(idBlacklist, id)) {
                        return false;
                    }
                }

                return true;
            }
        );

        return $filteredElements;
    },

    _matchesLocalNameOrElement: function (element, otherNameOrElement) {
        if (typeof otherNameOrElement === 'string') {
            return (element.localName || element.nodeName) === otherNameOrElement;
        } else {
            return element.isSameNode(otherNameOrElement);
        }
    }
};

return obj;
}

if (typeof define == 'function' && typeof define.amd == 'object') {
    console.log("RequireJS ... cfi_instructions");

    define(['jquery', 'underscore', './cfi_runtime_errors'],
    function ($, _, cfiRuntimeErrors) {
        return init($, _, cfiRuntimeErrors);
    });
} else {
    console.log("!RequireJS ... cfi_instructions");

    if (!global["EPUBcfi"]) {
        throw new Error("EPUBcfi not initialised on global object?! (window or this context)");
    }
    global.EPUBcfi.CFIInstructions =
    init($, _,
        {
            NodeTypeError: global.EPUBcfi.NodeTypeError,
            OutOfRangeError: global.EPUBcfi.OutOfRangeError,
            TerminusError: global.EPUBcfi.TerminusError,
            CFIAssertionError: global.EPUBcfi.CFIAssertionError
        });
}

})(typeof window !== "undefined" ? window : this);
<commit_msg>Add fallback for `Element.isSameNode`, not all browsers might have it<commit_after>//  Copyright (c) 2014 Readium Foundation and/or its licensees. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification,
//  are permitted provided that the following conditions are met:
//  1. Redistributions of source code must retain the above copyright notice, this
//  list of conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright notice,
//  this list of conditions and the following disclaimer in the documentation and/or
//  other materials provided with the distribution.
//  3. Neither the name of the organization nor the names of its contributors may be
//  used to endorse or promote products derived from this software without specific
//  prior written permission.

(function(global) {

var init = function($, _, cfiRuntimeErrors) {

var obj = {

// Description: This model contains the implementation for "instructions" included in the EPUB CFI domain specific language (DSL).
//   Lexing and parsing a CFI produces a set of executable instructions for processing a CFI (represented in the AST).
//   This object contains a set of functions that implement each of the executable instructions in the AST.

    // ------------------------------------------------------------------------------------ //
    //  "PUBLIC" METHODS (THE API)                                                          //
    // ------------------------------------------------------------------------------------ //

    // Description: Follows a step
    // Rationale: The use of children() is important here, as this jQuery method returns a tree of xml nodes, EXCLUDING
    //   CDATA and text nodes. When we index into the set of child elements, we are assuming that text nodes have been
    //   excluded.
    // REFACTORING CANDIDATE: This should be called "followIndexStep"
    getNextNode : function (CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist) {

        // Find the jquery index for the current node
        var $targetNode;
        if (CFIStepValue % 2 == 0) {

            $targetNode = this.elementNodeStep(CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist);
        }
        else {

            $targetNode = this.inferTargetTextNode(CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist);
        }

        return $targetNode;
    },

    // Description: This instruction executes an indirection step, where a resource is retrieved using a
    //   link contained on a attribute of the target element. The attribute that contains the link differs
    //   depending on the target.
    // Note: Iframe indirection will (should) fail if the iframe is not from the same domain as its containing script due to
    //   the cross origin security policy
    followIndirectionStep : function (CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist) {

        var that = this;
        var $contentDocument;
        var $blacklistExcluded;
        var $startElement;
        var $targetNode;

        // TODO: This check must be expanded to all the different types of indirection step
        // Only expects iframes, at the moment
        if ($currNode === undefined || !this._matchesLocalNameOrElement($currNode[0], 'iframe')) {

            throw cfiRuntimeErrors.NodeTypeError($currNode, "expected an iframe element");
        }

        // Check node type; only iframe indirection is handled, at the moment
        if (this._matchesLocalNameOrElement($currNode[0], 'iframe')) {

            // Get content
            $contentDocument = $currNode.contents();

            // Go to the first XHTML element, which will be the first child of the top-level document object
            $blacklistExcluded = this.applyBlacklist($contentDocument.children(), classBlacklist, elementBlacklist, idBlacklist);
            $startElement = $($blacklistExcluded[0]);

            // Follow an index step
            $targetNode = this.getNextNode(CFIStepValue, $startElement, classBlacklist, elementBlacklist, idBlacklist);

            // Return that shit!
            return $targetNode;
        }

        // TODO: Other types of indirection
        // TODO: $targetNode.is("embed")) : src
        // TODO: ($targetNode.is("object")) : data
        // TODO: ($targetNode.is("image") || $targetNode.is("xlink:href")) : xlink:href
    },

    // Description: Injects an element at the specified text node
    // Arguments: a cfi text termination string, a jquery object to the current node
    // REFACTORING CANDIDATE: Rename this to indicate that it injects into a text terminus
    textTermination : function ($currNode, textOffset, elementToInject) {

        var $injectedElement;
        // Get the first node, this should be a text node
        if ($currNode === undefined) {

            throw cfiRuntimeErrors.NodeTypeError($currNode, "expected a terminating node, or node list");
        }
        else if ($currNode.length === 0) {

            throw cfiRuntimeErrors.TerminusError("Text", "Text offset:" + textOffset, "no nodes found for termination condition");
        }

        $injectedElement = this.injectCFIMarkerIntoText($currNode, textOffset, elementToInject);
        return $injectedElement;
    },

    // Description: Checks that the id assertion for the node target matches that on
    //   the found node.
    targetIdMatchesIdAssertion : function ($foundNode, idAssertion) {

        if ($foundNode.attr("id") === idAssertion) {

            return true;
        }
        else {

            return false;
        }
    },

    // ------------------------------------------------------------------------------------ //
    //  "PRIVATE" HELPERS                                                                   //
    // ------------------------------------------------------------------------------------ //

    // Description: Step reference for xml element node. Expected that CFIStepValue is an even integer
    elementNodeStep : function (CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist) {

        var $targetNode;
        var $blacklistExcluded;
        var numElements;
        var jqueryTargetNodeIndex = (CFIStepValue / 2) - 1;

        $blacklistExcluded = this.applyBlacklist($currNode.children(), classBlacklist, elementBlacklist, idBlacklist);
        numElements = $blacklistExcluded.length;

        if (this.indexOutOfRange(jqueryTargetNodeIndex, numElements)) {

            throw cfiRuntimeErrors.OutOfRangeError(jqueryTargetNodeIndex, numElements - 1, "");
        }

        $targetNode = $($blacklistExcluded[jqueryTargetNodeIndex]);
        return $targetNode;
    },

    retrieveItemRefHref : function ($itemRefElement, $packageDocument) {

        return $("#" + $itemRefElement.attr("idref"), $packageDocument).attr("href");
    },

    indexOutOfRange : function (targetIndex, numChildElements) {

        return (targetIndex > numChildElements - 1) ? true : false;
    },

    // Rationale: In order to inject an element into a specific position, access to the parent object
    //   is required. This is obtained with the jquery parent() method. An alternative would be to
    //   pass in the parent with a filtered list containing only children that are part of the target text node.
    injectCFIMarkerIntoText : function ($textNodeList, textOffset, elementToInject) {
        var document = $textNodeList[0].ownerDocument;

        var nodeNum;
        var currNodeLength;
        var currTextPosition = 0;
        var nodeOffset;
        var originalText;
        var $injectedNode;
        var $newTextNode;
        // The iteration counter may be incorrect here (should be $textNodeList.length - 1 ??)
        for (nodeNum = 0; nodeNum <= $textNodeList.length; nodeNum++) {

            if ($textNodeList[nodeNum].nodeType === Node.TEXT_NODE) {

                currNodeMaxIndex = $textNodeList[nodeNum].nodeValue.length  + currTextPosition;
                nodeOffset = textOffset - currTextPosition;

                if (currNodeMaxIndex > textOffset) {

                    // This node is going to be split and the components re-inserted
                    originalText = $textNodeList[nodeNum].nodeValue;

                    // Before part
                    $textNodeList[nodeNum].nodeValue = originalText.slice(0, nodeOffset);

                    // Injected element
                    $injectedNode = $(elementToInject).insertAfter($textNodeList.eq(nodeNum));

                    // After part
                    $newTextNode = $(document.createTextNode(originalText.slice(nodeOffset, originalText.length)));
                    $($newTextNode).insertAfter($injectedNode);

                    return $injectedNode;
                } else if (currNodeMaxIndex == textOffset){
                    $injectedNode = $(elementToInject).insertAfter($textNodeList.eq(nodeNum));
                    return $injectedNode;
                }
                else {
                    currTextPosition = currNodeMaxIndex;
                }
            } else if($textNodeList[nodeNum].nodeType === Node.COMMENT_NODE){
                currNodeMaxIndex = $textNodeList[nodeNum].nodeValue.length + 7 + currTextPosition;
                currTextPosition = currNodeMaxIndex;
            } else if($textNodeList[nodeNum].nodeType === Node.PROCESSING_INSTRUCTION_NODE){
                currNodeMaxIndex = $textNodeList[nodeNum].nodeValue.length + $textNodeList[nodeNum].target.length + 5
                currTextPosition = currNodeMaxIndex;
            }
        }

        throw cfiRuntimeErrors.TerminusError("Text", "Text offset:" + textOffset, "The offset exceeded the length of the text");
    },

    // Rationale: In order to inject an element into a specific position, access to the parent object
    //   is required. This is obtained with the jquery parent() method. An alternative would be to
    //   pass in the parent with a filtered list containing only children that are part of the target text node.

    // Description: This method finds a target text node and then injects an element into the appropriate node
    // Rationale: The possibility that cfi marker elements have been injected into a text node at some point previous to
    //   this method being called (and thus splitting the original text node into two separate text nodes) necessitates that
    //   the set of nodes that compromised the original target text node are inferred and returned.
    // Notes: Passed a current node. This node should have a set of elements under it. This will include at least one text node,
    //   element nodes (maybe), or possibly a mix.
    // REFACTORING CANDIDATE: This method is pretty long (and confusing). Worth investigating to see if it can be refactored into something clearer.
    inferTargetTextNode : function (CFIStepValue, $currNode, classBlacklist, elementBlacklist, idBlacklist) {

        var $elementsWithoutMarkers;
        var currLogicalTextNodeIndex;
        var targetLogicalTextNodeIndex;
        var nodeNum;
        var $targetTextNodeList;
        var prevNodeWasTextNode;

        // Remove any cfi marker elements from the set of elements.
        // Rationale: A filtering function is used, as simply using a class selector with jquery appears to
        //   result in behaviour where text nodes are also filtered out, along with the class element being filtered.
        $elementsWithoutMarkers = this.applyBlacklist($currNode.contents(), classBlacklist, elementBlacklist, idBlacklist);

        // Convert CFIStepValue to logical index; assumes odd integer for the step value
        targetLogicalTextNodeIndex = ((parseInt(CFIStepValue) + 1) / 2) - 1;

        // Set text node position counter
        currLogicalTextNodeIndex = 0;
        prevNodeWasTextNode = false;
        $targetTextNodeList = $elementsWithoutMarkers.filter(
            function () {

                if (currLogicalTextNodeIndex === targetLogicalTextNodeIndex) {

                    // If it's a text node
                    if (this.nodeType === Node.TEXT_NODE || this.nodeType === Node.COMMENT_NODE || this.nodeType === Node.PROCESSING_INSTRUCTION_NODE) {
                        prevNodeWasTextNode = true;
                        return true;
                    }
                    // Rationale: The logical text node position is only incremented once a group of text nodes (a single logical
                    //   text node) has been passed by the loop.
                    else if (prevNodeWasTextNode && (this.nodeType !== Node.TEXT_NODE)) {
                        currLogicalTextNodeIndex++;
                        prevNodeWasTextNode = false;
                        return false;
                    }
                }
                // Don't return any elements
                else {

                    if (this.nodeType === Node.TEXT_NODE || this.nodeType === Node.COMMENT_NODE || this.nodeType === Node.PROCESSING_INSTRUCTION_NODE) {
                        prevNodeWasTextNode = true;
                    }else if (!prevNodeWasTextNode && this.nodeType === Node.ELEMENT_NODE){
                        currLogicalTextNodeIndex++;
                        prevNodeWasTextNode = true;
                    }
                    else if (prevNodeWasTextNode && (this.nodeType !== Node.TEXT_NODE) && (this !== $elementsWithoutMarkers.lastChild)) {
                        currLogicalTextNodeIndex++;
                        prevNodeWasTextNode = false;
                    }

                    return false;
                }
            }
        );

        // The filtering above should have counted the number of "logical" text nodes; this can be used to
        // detect out of range errors
        if ($targetTextNodeList.length === 0) {
            throw cfiRuntimeErrors.OutOfRangeError(targetLogicalTextNodeIndex, currLogicalTextNodeIndex, "Index out of range");
        }

        // return the text node list
        return $targetTextNodeList;
    },

    applyBlacklist : function ($elements, classBlacklist, elementBlacklist, idBlacklist) {
        var self = this;
        var $filteredElements;

        $filteredElements = $elements.filter(
            function () {

                var element = this;

                if (classBlacklist && classBlacklist.length) {
                    var classAttribute = element.className;
                    var classList = classAttribute ? classAttribute.split(' ') : [];
                    if (classList.length === 1 && _.contains(classBlacklist, classList[0])) {
                        return false;
                    } else if (classList.length && _.intersection(classBlacklist, classList).length) {
                        return false;
                    }
                }

                if (elementBlacklist && elementBlacklist.length) {
                    if (element.tagName) {
                        var isElementBlacklisted = _.find(elementBlacklist, function (blacklistedTag) {
                            blacklistedTag = blacklistedTag.toLowerCase();
                            return self._matchesLocalNameOrElement(element, blacklistedTag)
                        });
                        if (isElementBlacklisted) {
                            return false;
                        }
                    }
                }

                if (idBlacklist && idBlacklist.length) {
                    var id = element.id;
                    if (id && id.length && _.contains(idBlacklist, id)) {
                        return false;
                    }
                }

                return true;
            }
        );

        return $filteredElements;
    },

    _matchesLocalNameOrElement: function (element, otherNameOrElement) {
        if (typeof otherNameOrElement === 'string') {
            return (element.localName || element.nodeName) === otherNameOrElement;
        } else {
            return element.isSameNode ? element.isSameNode(otherNameOrElement) : element === otherNameOrElement;
        }
    }
};

return obj;
}

if (typeof define == 'function' && typeof define.amd == 'object') {
    console.log("RequireJS ... cfi_instructions");

    define(['jquery', 'underscore', './cfi_runtime_errors'],
    function ($, _, cfiRuntimeErrors) {
        return init($, _, cfiRuntimeErrors);
    });
} else {
    console.log("!RequireJS ... cfi_instructions");

    if (!global["EPUBcfi"]) {
        throw new Error("EPUBcfi not initialised on global object?! (window or this context)");
    }
    global.EPUBcfi.CFIInstructions =
    init($, _,
        {
            NodeTypeError: global.EPUBcfi.NodeTypeError,
            OutOfRangeError: global.EPUBcfi.OutOfRangeError,
            TerminusError: global.EPUBcfi.TerminusError,
            CFIAssertionError: global.EPUBcfi.CFIAssertionError
        });
}

})(typeof window !== "undefined" ? window : this);
<|endoftext|>


<commit_before>const webpack = require('webpack');
const ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin;

const cache = {};

module.exports = {
  cache: cache,

  entry: {
    main: 'ts/main.ts',
    vendor: 'ts/vendor.ts',
  },

  resolve: {
    root: __dirname,
    extensions: ['', '.ts', '.js'],
    modulesDirectories: ['node_modules'],
    alias: {
      '@angular/common': '@angular/common/bundles/common.umd.min.js',
      '@angular/core': '@angular/core/bundles/core.umd.min.js',
      '@angular/http': '@angular/http/bundles/http.umd.min.js',
      '@angular/platform-browser': '@angular/platform-browser/bundles/platform-browser.umd.min.js',
      '@angular/platform-browser-dynamic': '@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.min.js',
      '@angular/router': '@angular/router/bundles/router.umd.min.js',
      '@angular/router-deprecated': '@angular/router-deprecated/bundles/router-deprecated.umd.min.js',
    }
  },

  module: {
    preLoaders: [
      {
        test: /.js$/,
        loader: 'source-map-loader'
      },
      {
        test: /\.ts$/,
        loader: "tslint"
      }
    ],

    loaders: [
      {
        test: /\.ts$/,
        loader: 'awesome-typescript-loader',
        exclude: [/\.(spec|e2e)\.ts$/]
      },

      {
        test: /\.html$/,
        loader: 'raw-loader',
        exclude: ['html/index.html', 'html/offline.html']
      },

      {
        test: /\.json$/,
        loader: 'json-loader',
      }
    ]
  },

  plugins: [
    new webpack.optimize.OccurenceOrderPlugin(true),
    new webpack.optimize.CommonsChunkPlugin({
      name: ['vendor']
    }),
    new ForkCheckerPlugin(),
  ],

  devtool: 'cheap-eval-source-map',

  output: {
    filename: 'bundle-[name].js',
    sourceMapFilename: 'bundle-[name].map',
    chunkFilename: '[chunkhash].js',
  },
}
<commit_msg>Extracting manifest from the vendor bundle to keep its hash constant<commit_after>const webpack = require('webpack');
const ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin;

const cache = {};

module.exports = {
  cache: cache,

  entry: {
    main: 'ts/main.ts',
    vendor: 'ts/vendor.ts',
  },

  resolve: {
    root: __dirname,
    extensions: ['', '.ts', '.js'],
    modulesDirectories: ['node_modules'],
    alias: {
      '@angular/common': '@angular/common/bundles/common.umd.min.js',
      '@angular/core': '@angular/core/bundles/core.umd.min.js',
      '@angular/http': '@angular/http/bundles/http.umd.min.js',
      '@angular/platform-browser': '@angular/platform-browser/bundles/platform-browser.umd.min.js',
      '@angular/platform-browser-dynamic': '@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.min.js',
      '@angular/router': '@angular/router/bundles/router.umd.min.js',
      '@angular/router-deprecated': '@angular/router-deprecated/bundles/router-deprecated.umd.min.js',
    }
  },

  module: {
    preLoaders: [
      {
        test: /.js$/,
        loader: 'source-map-loader'
      },
      {
        test: /\.ts$/,
        loader: "tslint"
      }
    ],

    loaders: [
      {
        test: /\.ts$/,
        loader: 'awesome-typescript-loader',
        exclude: [/\.(spec|e2e)\.ts$/]
      },

      {
        test: /\.html$/,
        loader: 'raw-loader',
        exclude: ['html/index.html', 'html/offline.html']
      },

      {
        test: /\.json$/,
        loader: 'json-loader',
      }
    ]
  },

  plugins: [
    new webpack.optimize.OccurenceOrderPlugin(true),
    new webpack.optimize.CommonsChunkPlugin({
      name: ['vendor', 'manifest']
    }),
    new ForkCheckerPlugin(),
  ],

  devtool: 'cheap-eval-source-map',

  output: {
    filename: 'bundle-[name].js',
    sourceMapFilename: 'bundle-[name].map',
    chunkFilename: '[chunkhash].js',
  },
}
<|endoftext|>


<commit_before>// Splash
var splash = ""
  + "                                                                     ..;===+.\n"
  + "                                                                  .:=iiiiii=+=\n"
  + "                                                               .=i))=;::+)i=+,\n"
  + "                                                            ,=i);)I)))I):=i=;\n"
  + "                                                         .=i==))))ii)))I:i++\n"
  + "                                                       +)+))iiiiiiii))I=i+:'\n"
  + "                                  .,:;;++++++;:,.       )iii+:::;iii))+i='\n"
  + "                               .:;++=iiiiiiiiii=++;.    =::,,,:::=i));=+'\n"
  + "                             ,;+==ii)))))))))))ii==+;,      ,,,:=i))+=:\n"
  + "                           ,;+=ii))))))IIIIII))))ii===;.    ,,:=i)=i+\n"
  + "                          ;+=ii)))IIIIITIIIIII))))iiii=+,   ,:=));=,\n"
  + "                        ,+=i))IIIIIITTTTTITIIIIII)))I)i=+,,:+i)=i+\n"
  + "                       ,+i))IIIIIITTTTTTTTTTTTI))IIII))i=::i))i='\n"
  + "                      ,=i))IIIIITLLTTTTTTTTTTIITTTTIII)+;+i)+i`\n"
  + "                      =i))IIITTLTLTTTTTTTTTIITTLLTTTII+:i)ii:'\n"
  + "                     +i))IITTTLLLTTTTTTTTTTTTLLLTTTT+:i)))=,\n"
  + "                     =))ITTTTTTTTTTTLTTTTTTLLLLLLTi:=)IIiii;\n"
  + "                    .i)IIITTTTTTTTLTTTITLLLLLLLT);=)I)))))i;\n"
  + "                    :))IIITTTTTLTTTTTTLLHLLLLL);=)II)IIIIi=:\n"
  + "                    :i)IIITTTTTTTTTLLLHLLHLL)+=)II)ITTTI)i=\n"
  + "                    .i)IIITTTTITTLLLHHLLLL);=)II)ITTTTII)i+\n"
  + "                    =i)IIIIIITTLLLLLLHLL=:i)II)TTTTTTIII)i'\n"
  + "                  +i)i)))IITTLLLLLLLLT=:i)II)TTTTLTTIII)i;\n"
  + "                +ii)i:)IITTLLTLLLLT=;+i)I)ITTTTLTTTII))i;\n"
  + "               =;)i=:,=)ITTTTLTTI=:i))I)TTTLLLTTTTTII)i;\n"
  + "             +i)ii::,  +)IIITI+:+i)I))TTTTLLTTTTTII))=,\n"
  + "           :=;)i=:,,    ,i++::i))I)ITTTTTTTTTTIIII)=+'\n"
  + "         .+ii)i=::,,   ,,::=i)))iIITTTTTTTTIIIII)=+\n"
  + "        ,==)ii=;:,,,,:::=ii)i)iIIIITIIITIIII))i+:'\n"
  + "       +=:))i==;:::;=iii)+)=  `:i)))IIIII)ii+'\n"
  + "     .+=:))iiiiiiii)))+ii;\n"
  + "    .+=;))iiiiii)));ii+\n"
  + "   .+=i:)))))))=+ii+\n"
  + "  .;==i+::::=)i=;\n"
  + "  ,+==iiiiii+,\n"
  + "  `+=+++;`\n";

// Details
var name = "John Doe";
var loc = "Timbuktu";
var lookingfor = "Raking in the money";
var pdfLink = "http://en.wikipedia.org/wiki/R%C3%A9sum%C3%A9";

// Settings
var showForks = false; // Shows forks of repositories on Github

// Github and Bitbucket accounts
var githubUsername = "example";
var bitbucketUsername = "example";
var githubURL = "https://github.com/" + githubUsername;
var bitbucketURL = "https://bitbucket.org/" + bitbucketUsername;

// If left blank e.g. url = "" then it won't appear
var websiteURL = "http://www.example.com/";
var blogURL = "";
var portfolioURL = "";
var twitterURL = "https://twitter.com/";
var facebookURL = "https://facebook.com/";
var skypeAccount = "test";
var socialMedia = [["Github", githubURL], ["Bitbucket", bitbucketURL], ["Website", websiteURL],
	["Blog", blogURL], ["Portfolio", portfolioURL], ["Twitter", twitterURL], ["Facebook", facebookURL],
	["Skype", skypeAccount]];

// Education list
var education = [["University of Queensland", "Bachelor of Engineering (Software Engineering)", "Graduate July, 2014"],
	["Summer University", "Summer Program", "June-July 2013"],["Tianjin University", "Research", "June-July 2012"],
	["Chinese University of Hong Kong", "Exchange Student", "September-December 2011"],
	["Beijing University of Internation Relations", "Language course", "July-August"]];

// Employment list
var employment = [["Westpac Banking Group", "Software Engineering Intern", "November 2013 - February 2014"],
	["APS Mining", "Test Engineering Intern", "December 2012 - November 2013"],
	["Keays Software", "Software Engineering Intern", "April 2012 - November 2013"],
	["Google Australia", "Student Ambassador", "January 2013 - November 2013"],
	["Wali Information Technology", "IT Intern", "January 2012 - February 2012"]];

// Volunteering list
var volunteering = [["Mentor", "Coder Dojo Brisbane", "July - September 2013"],
	["Director of IT", "Australia China Youth Association (National)", "January - September 2013"],
	["Rescue Referee", "RoboCup Junior Australia", "August 2012"]];

// Awards list
var awards = [["HSK Level 5", "Hanban Institute", "December 2013"],
	["Certificate of Achievement", "ACM South Pacific Regional Programming Contest", "2013"],
	["Dean's Commendation", "The University of Queensland", "2011 and 2012"]];

// Membership list
var membership = [["Australian Computer Society", "Associate Member"],
	["Engineers Australia", "Student Member"],
	["Institute of Electrical and Electronics Engineers", "Student Member"]];

// Skills list
var skillsLanguages = [["Proficient", "C/C++ Java Python"],
	["Experienced", "C# .NET"],
	["Familiar", "PHP Javascript Bash"]];

var skillsTools = [["Proficient", "Git Wordpress"],
	["Experienced", "Android Github"],
	["Familiar", "TCP/IP SVN"]];

var skillsConcepts = [["Proficient", "Kanban Agile"],
	["Experienced", "Functional Testing"],
	["Familiar", "Functional Programming"]];

var publicPGPkey = `
YOUR PGP KEY HERE
`;
<commit_msg>Anonymized details<commit_after>// Splash
var splash = ""
  + "                                                                     ..;===+.\n"
  + "                                                                  .:=iiiiii=+=\n"
  + "                                                               .=i))=;::+)i=+,\n"
  + "                                                            ,=i);)I)))I):=i=;\n"
  + "                                                         .=i==))))ii)))I:i++\n"
  + "                                                       +)+))iiiiiiii))I=i+:'\n"
  + "                                  .,:;;++++++;:,.       )iii+:::;iii))+i='\n"
  + "                               .:;++=iiiiiiiiii=++;.    =::,,,:::=i));=+'\n"
  + "                             ,;+==ii)))))))))))ii==+;,      ,,,:=i))+=:\n"
  + "                           ,;+=ii))))))IIIIII))))ii===;.    ,,:=i)=i+\n"
  + "                          ;+=ii)))IIIIITIIIIII))))iiii=+,   ,:=));=,\n"
  + "                        ,+=i))IIIIIITTTTTITIIIIII)))I)i=+,,:+i)=i+\n"
  + "                       ,+i))IIIIIITTTTTTTTTTTTI))IIII))i=::i))i='\n"
  + "                      ,=i))IIIIITLLTTTTTTTTTTIITTTTIII)+;+i)+i`\n"
  + "                      =i))IIITTLTLTTTTTTTTTIITTLLTTTII+:i)ii:'\n"
  + "                     +i))IITTTLLLTTTTTTTTTTTTLLLTTTT+:i)))=,\n"
  + "                     =))ITTTTTTTTTTTLTTTTTTLLLLLLTi:=)IIiii;\n"
  + "                    .i)IIITTTTTTTTLTTTITLLLLLLLT);=)I)))))i;\n"
  + "                    :))IIITTTTTLTTTTTTLLHLLLLL);=)II)IIIIi=:\n"
  + "                    :i)IIITTTTTTTTTLLLHLLHLL)+=)II)ITTTI)i=\n"
  + "                    .i)IIITTTTITTLLLHHLLLL);=)II)ITTTTII)i+\n"
  + "                    =i)IIIIIITTLLLLLLHLL=:i)II)TTTTTTIII)i'\n"
  + "                  +i)i)))IITTLLLLLLLLT=:i)II)TTTTLTTIII)i;\n"
  + "                +ii)i:)IITTLLTLLLLT=;+i)I)ITTTTLTTTII))i;\n"
  + "               =;)i=:,=)ITTTTLTTI=:i))I)TTTLLLTTTTTII)i;\n"
  + "             +i)ii::,  +)IIITI+:+i)I))TTTTLLTTTTTII))=,\n"
  + "           :=;)i=:,,    ,i++::i))I)ITTTTTTTTTTIIII)=+'\n"
  + "         .+ii)i=::,,   ,,::=i)))iIITTTTTTTTIIIII)=+\n"
  + "        ,==)ii=;:,,,,:::=ii)i)iIIIITIIITIIII))i+:'\n"
  + "       +=:))i==;:::;=iii)+)=  `:i)))IIIII)ii+'\n"
  + "     .+=:))iiiiiiii)))+ii;\n"
  + "    .+=;))iiiiii)));ii+\n"
  + "   .+=i:)))))))=+ii+\n"
  + "  .;==i+::::=)i=;\n"
  + "  ,+==iiiiii+,\n"
  + "  `+=+++;`\n";

// Details
var name = "John Doe";
var loc = "Timbuktu";
var lookingfor = "Raking in the money";
var pdfLink = "http://en.wikipedia.org/wiki/R%C3%A9sum%C3%A9";

// Settings
var showForks = false; // Shows forks of repositories on Github

// Github and Bitbucket accounts
var githubUsername = "example";
var bitbucketUsername = "example";
var githubURL = "https://github.com/" + githubUsername;
var bitbucketURL = "https://bitbucket.org/" + bitbucketUsername;

// If left blank e.g. url = "" then it won't appear
var websiteURL = "http://www.example.com/";
var blogURL = "";
var portfolioURL = "";
var twitterURL = "https://twitter.com/";
var facebookURL = "https://facebook.com/";
var skypeAccount = "test";
var socialMedia = [["Github", githubURL], ["Bitbucket", bitbucketURL], ["Website", websiteURL],
	["Blog", blogURL], ["Portfolio", portfolioURL], ["Twitter", twitterURL], ["Facebook", facebookURL],
	["Skype", skypeAccount]];

// Education list
var education = [["South Hamptom Institute of Technology", "Bachelor of Computers", "Graduate July, 2014"]];

// Employment list
var employment = [["Prestige Worldwide", "Beat Lab Technician", "November 2013 - Present"]];

// Volunteering list
var volunteering = [["Volunteer", "Voluntation", "November 2013 - Present"]];

// Awards list
var awards = [["Mad Coder", "Programmer Institute", "December 2013"]];

// Membership list
var membership = [["Some Society", "Pizza Box Opener"]];

// Skills list
var skillsLanguages = [["Proficient", "C/C++ Java Python"],
	["Experienced", "C# .NET"],
	["Familiar", "PHP Javascript Bash"]];

var skillsTools = [["Proficient", "Git Wordpress"],
	["Experienced", "Android Github"],
	["Familiar", "TCP/IP SVN"]];

var skillsConcepts = [["Proficient", "Kanban Agile"],
	["Experienced", "Functional Testing"],
	["Familiar", "Functional Programming"]];

var publicPGPkey = `
YOUR PGP KEY HERE
`;
<|endoftext|>


<commit_before>function checkAll(){
  var inputs = document.getElementsByTagName('input');
  for(var i = 0; i < inputs.length; i++){
    if(inputs[i].type=='checkbox'){
      inputs[i].checked = true;
    }
  }
}

var remove_pages = function(){
  var n_pages = $(".page-content").length;
  //console.log('There are '+n_pages+' pages.');
  var counter = 0;
  for(var i=0;i<n_pages;i++){
    var chosen_page = $(".page-content").eq(i);

    if(chosen_page.html() == "<cssregion></cssregion>"){
      //chosen_page.parents(".sheet").remove();
      chosen_page.parents(".sheet").css('display','none');
      counter += 1;
    }else if(chosen_page.html() == ""){
      //chosen_page.parents(".sheet").remove();
      chosen_page.parents(".sheet").css('display','none');
      counter += 1;
    }
  }
  console.log(counter + " sheets removed.");
  $("#trim_pages_button").css("text-decoration","line-through");
  $("#print_button").css("display","inline");
}

function writeLinks(links){

  for(var i=0; i<links.length; i++){

    var link = links.eq(i);
    var name = link.html();
    var href = link.attr('href');
    var temp = href;

    // strip "http://"
    var start = temp.indexOf("://");
    var end = temp.length;
    if(start !== -1){
      start = start + 3;
      temp = temp.substring(start,end);
    }

    href = temp; // Now stripped & clean=looking.

    //$( ""+href+"" ).insertAfter(link); // This line currently not working. Going the css :after route for now.

    $("#footnotes").append(name+" → "+href+"
");

  }
}

function refreshContent(){
  $(".sheet").css('display','block'); // Reverses anything hidden by remove_pages.
  $("#footnotes").html("
Links
");
  $("#trim_pages_button").css("text-decoration","none");
  $("#print_button").css("display","none");

  var n_checkboxes = $("input[type=checkbox]").length;
  var checked_contents = []; // contains string names of content types whose boxes are checked.
  for(var i=0; i<n_checkboxes;i++){
    var n_checked = checked_contents.length;
    if($('input[type=checkbox]').eq(i).is(':checked')){
      // Slice off the "checkbox-" prefix in id attribute.
      checked_contents[n_checked] = $('input[type=checkbox]').eq(i).attr('id').slice(9,$('input[type=checkbox]').eq(i).attr('id').length);
    }
  }
  $('.content').css('display','none'); // Clear the slate, then re-flow.
  $('.content').css('break-after','never');

  n_checked = checked_contents.length;
  for(var j=0;j<n_checked;j++){
    $('.content.'+checked_contents[j]).parents().css('display','block');
    $('.content.'+checked_contents[j]).css('display','block');
    $('.content.'+checked_contents[j]).css('break-after','always'); // If this is in all .content blocks, even the hidden .content cause region-breaks. (so we only put it on visible ones)

    var links = $('.content.'+checked_contents[j]).find('a');
    links = links.slice(0,links.length/2); // links in here twice for polyfill reasons.
    //console.log(links);
    writeLinks(links);

    // This line could be more informative.
    console.log('Flowing in '+checked_contents[j]+' contents.');
  }
}

// on loading...
$('.content').css('display','none');
checkAll();
refreshContent();

// on interaction...

$("#trim_pages_button").click(function(){
  remove_pages();
});

$("input[type=checkbox]").on("click", function(){
  refreshContent();
});

$("#print_button").click(function(){
  window.print();
});
<commit_msg>Link index working as best I can tell.<commit_after>function checkAll(){
  var inputs = document.getElementsByTagName('input');
  for(var i = 0; i < inputs.length; i++){
    if(inputs[i].type=='checkbox'){
      inputs[i].checked = true;
    }
  }
}

var remove_pages = function(){
  var n_pages = $(".page-content").length;
  //console.log('There are '+n_pages+' pages.');
  var counter = 0;
  for(var i=0;i<n_pages;i++){
    var chosen_page = $(".page-content").eq(i);

    if(chosen_page.html() == "<cssregion></cssregion>"){
      //chosen_page.parents(".sheet").remove();
      chosen_page.parents(".sheet").css('display','none');
      counter += 1;
    }else if(chosen_page.html() == ""){
      //chosen_page.parents(".sheet").remove();
      chosen_page.parents(".sheet").css('display','none');
      counter += 1;
    }
  }
  console.log(counter + " sheets removed.");
  $("#trim_pages_button").css("text-decoration","line-through");
  $("#print_button").css("display","inline");
}

function writeLinks(links){

  var link_string = "";

  for(var i=0; i<links.length; i++){

    var link = links.eq(i);
    var name = link.html();
    var href = link.attr('href');
    var temp = href;

    // strip "http://"
    var start = temp.indexOf("://");
    var end = temp.length;
    if(start !== -1){
      start = start + 3;
      temp = temp.substring(start,end);
    }

    href = temp; // Now stripped & clean=looking.
    link_string = link_string + name+" → "+href+"
";
  }
  return link_string;
}

function refreshContent(){
  $(".sheet").css('display','block'); // Reverses anything hidden by remove_pages.
  $("#trim_pages_button").css("text-decoration","none");
  $("#print_button").css("display","none");
  $("#footnotes").html("
Links
");

  var n_checkboxes = $("input[type=checkbox]").length;
  var checked_contents = []; // contains string names of content types whose boxes are checked.
  for(var i=0; i<n_checkboxes;i++){
    var n_checked = checked_contents.length;
    if($('input[type=checkbox]').eq(i).is(':checked')){
      // Slice off the "checkbox-" prefix in id attribute and take the content type name that's on the checkbox.
      checked_contents[n_checked] = $('input[type=checkbox]').eq(i).attr('id').slice(9,$('input[type=checkbox]').eq(i).attr('id').length);
    }
  }
  $('.content').css('display','none'); // Clear the slate, then re-flow.
  $('.content').css('break-after','never');

  var link_html = "";
  n_checked = checked_contents.length;
  for(var j=0;j<n_checked;j++){
    $('.content.'+checked_contents[j]).parents().css('display','block'); // Display sheets of content types selected.
    $('.content.'+checked_contents[j]).css('display','block');
    $('.content.'+checked_contents[j]).css('break-after','always'); // If this is in all .content blocks, even the hidden .content cause region-breaks. (so we only put it on visible ones)

    var links = $('.content.'+checked_contents[j]).find('a');

    link_html = link_html + writeLinks(links);

    // This line could be more informative.
    console.log('Flowing in '+checked_contents[j]+' contents.');
  }

  $("#footnotes").html("
Links
"+link_html);
}

// on loading...
$('.content').css('display','none');
checkAll();
refreshContent();

// on interaction...

$("#trim_pages_button").click(function(){
  remove_pages();
});

$("input[type=checkbox]").on("click", function(){
  refreshContent();
});

$("#print_button").click(function(){
  window.print();
});
<|endoftext|>


<commit_before>var webpack = require('webpack'),
    cssnano = require('cssnano'),
    SassLintPlugin = require('sasslint-webpack-plugin'),
    AureliaWebPackPlugin = require('aurelia-webpack-plugin'),
    ExtractTextPlugin = require('extract-text-webpack-plugin'),
    OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

var prodEnv = (process.env.NODE_ENV === 'production');
var testEnv = (process.env.NODE_ENV === 'test');

module.exports = {
    entry: {
        app: [
            './src/main.ts'
        ],
        vendor: [
            'aurelia-binding',
            'aurelia-bootstrapper-webpack',
            'aurelia-dependency-injection',
            'aurelia-event-aggregator',
            'aurelia-framework',
            'aurelia-history',
            'aurelia-history-browser',
            'aurelia-http-client',
            'aurelia-loader',
            'aurelia-loader-webpack',
            'aurelia-logging',
            'aurelia-logging-console',
            'aurelia-metadata',
            'aurelia-pal',
            'aurelia-pal-browser',
            'aurelia-path',
            'aurelia-polyfills',
            'aurelia-route-recognizer',
            'aurelia-router',
            'aurelia-task-queue',
            'aurelia-templating',
            'aurelia-templating-binding',
            'aurelia-templating-router',
            'aurelia-templating-resources'
        ]
    },

    stats: {
        hash: true,
        version: true,
        timings: true,
        assets: false,
        entrypoints: false,
        chunks: false,
        modules: false,
        reasons: false,
        usedExports: false,
        providedExports: false,
        children: false,
        source: false,
        errors: true,
        errorDetails: true,
        warnings: false,
        publicPath: false
    },

    output: {
        path: './dist',
        publicPath: '/dist/',
        filename: '[name].js'
    },

    resolve: {
        extensions: [
            '',
            '.webpack.js',
            '.web.js',
            '.js',
            '.jsx',
            '.ts',
            '.tsx'
        ]
    },

    module: {
        preLoaders: [
            {
                test: /\.ts$/,
                loader: 'tslint'
            }
        ],
        loaders: [
            {
                test: /\.ts$/,
                loader: 'awesome-typescript-loader',
                query: {
                    target: 'es5'
                }
            },
            {
                test: /\.html$/,
                loader: 'html'
            },
            {
                test: /\.json$/,
                loader: 'json'
            },
            {
                test: /\.scss$/,
                loader: ExtractTextPlugin.extract('style', 'css!resolve-url!sass?sourceMap')
            },
            {
                test: /\.(png|jpg|gif)$/,
                loader: 'file-loader?name=images/[name].[ext]'
            },
            {
                test: /\.(eot|svg|ttf|otf|woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'file?name=fonts/[name].[ext]'
            }
        ]
    },

    devServer: {
        stats: 'errors-only',
        proxy: {
            '/api': {
                target: 'https://hacker-news.firebaseio.com/v0/',
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    },

    plugins: [
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: process.env.NODE_ENV
            }
        }),
        new AureliaWebPackPlugin(),
        new SassLintPlugin({
            configFile: 'sass-lint.yml',
            glob: 'style/**/*.s?(a|c)ss',
            failOnWarning: prodEnv,
            failOnError: prodEnv,
            ignorePlugins: [
                'extract-text-webpack-plugin'
            ]
        }),
        new ExtractTextPlugin('[name].css'),
        new OptimizeCssAssetsPlugin({
            canPrint: false,
            cssProcessor: cssnano,
            cssProcessorOptions: {
                autoprefixer: {
                    add: true,
                    remove: false
                },
                discardUnused: false
            }
        })
    ],

    tslint: {
        emitErrors: true,
        failOnHint: true,
        fileOutput: {
            dir: 'reports/tslint',
            clean: true
        }
    }
};

if (!testEnv) {
    module.exports.plugins.push(new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'));
}

if (prodEnv) {
    module.exports.plugins.push(
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        }),
        new webpack.optimize.DedupePlugin(),
        new webpack.optimize.OccurenceOrderPlugin()
    );
} else {
    module.exports.devtool = '#source-map';
}

<commit_msg>Remove webpack-dev-server API proxy<commit_after>var webpack = require('webpack'),
    cssnano = require('cssnano'),
    SassLintPlugin = require('sasslint-webpack-plugin'),
    AureliaWebPackPlugin = require('aurelia-webpack-plugin'),
    ExtractTextPlugin = require('extract-text-webpack-plugin'),
    OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

var prodEnv = (process.env.NODE_ENV === 'production');
var testEnv = (process.env.NODE_ENV === 'test');

module.exports = {
    entry: {
        app: [
            './src/main.ts'
        ],
        vendor: [
            'aurelia-binding',
            'aurelia-bootstrapper-webpack',
            'aurelia-dependency-injection',
            'aurelia-event-aggregator',
            'aurelia-framework',
            'aurelia-history',
            'aurelia-history-browser',
            'aurelia-http-client',
            'aurelia-loader',
            'aurelia-loader-webpack',
            'aurelia-logging',
            'aurelia-logging-console',
            'aurelia-metadata',
            'aurelia-pal',
            'aurelia-pal-browser',
            'aurelia-path',
            'aurelia-polyfills',
            'aurelia-route-recognizer',
            'aurelia-router',
            'aurelia-task-queue',
            'aurelia-templating',
            'aurelia-templating-binding',
            'aurelia-templating-router',
            'aurelia-templating-resources'
        ]
    },

    stats: {
        hash: true,
        version: true,
        timings: true,
        assets: false,
        entrypoints: false,
        chunks: false,
        modules: false,
        reasons: false,
        usedExports: false,
        providedExports: false,
        children: false,
        source: false,
        errors: true,
        errorDetails: true,
        warnings: false,
        publicPath: false
    },

    output: {
        path: './dist',
        publicPath: '/dist/',
        filename: '[name].js'
    },

    resolve: {
        extensions: [
            '',
            '.webpack.js',
            '.web.js',
            '.js',
            '.jsx',
            '.ts',
            '.tsx'
        ]
    },

    module: {
        preLoaders: [
            {
                test: /\.ts$/,
                loader: 'tslint'
            }
        ],
        loaders: [
            {
                test: /\.ts$/,
                loader: 'awesome-typescript-loader',
                query: {
                    target: 'es5'
                }
            },
            {
                test: /\.html$/,
                loader: 'html'
            },
            {
                test: /\.json$/,
                loader: 'json'
            },
            {
                test: /\.scss$/,
                loader: ExtractTextPlugin.extract('style', 'css!resolve-url!sass?sourceMap')
            },
            {
                test: /\.(png|jpg|gif)$/,
                loader: 'file-loader?name=images/[name].[ext]'
            },
            {
                test: /\.(eot|svg|ttf|otf|woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'file?name=fonts/[name].[ext]'
            }
        ]
    },

    devServer: {
        stats: 'errors-only'
    },

    plugins: [
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: process.env.NODE_ENV
            }
        }),
        new AureliaWebPackPlugin(),
        new SassLintPlugin({
            configFile: 'sass-lint.yml',
            glob: 'style/**/*.s?(a|c)ss',
            failOnWarning: prodEnv,
            failOnError: prodEnv,
            ignorePlugins: [
                'extract-text-webpack-plugin'
            ]
        }),
        new ExtractTextPlugin('[name].css'),
        new OptimizeCssAssetsPlugin({
            canPrint: false,
            cssProcessor: cssnano,
            cssProcessorOptions: {
                autoprefixer: {
                    add: true,
                    remove: false
                },
                discardUnused: false
            }
        })
    ],

    tslint: {
        emitErrors: true,
        failOnHint: true,
        fileOutput: {
            dir: 'reports/tslint',
            clean: true
        }
    }
};

if (!testEnv) {
    module.exports.plugins.push(new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'));
}

if (prodEnv) {
    module.exports.plugins.push(
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        }),
        new webpack.optimize.DedupePlugin(),
        new webpack.optimize.OccurenceOrderPlugin()
    );
} else {
    module.exports.devtool = '#source-map';
}

<|endoftext|>


<commit_before>BR.Iternity = BR.Control.extend({
	  options: {
        heading: 'Iternity'
    },

    onAdd: function (map) {
        var container = BR.Control.prototype.onAdd.call(this, map);
        this.update();
        return container;
    },

    update: function (polyline, segments) {
        var i, j, iter, html = '';

        html += '
';
        for (i = 0; segments && i < segments.length; i++)
        {
            iter = segments[i].feature.iternity;
            for (j = 0; iter && j < iter.length; j++)
            {
              html += iter[j] + '\n';
            }
        }
        html += '
';

        this._content.innerHTML = html;
    },

    calcStats: function(polyline, segments) {
        var stats = {
            trackLength: 0,
            filteredAscend: 0,
            plainAscend: 0,
            cost: 0
        };
        var i, props;

        for (i = 0; segments && i < segments.length; i++) {
            props = segments[i].feature.properties;
            stats.trackLength += +props['track-length'];
            stats.filteredAscend += +props['filtered ascend'];
            stats.plainAscend += +props['plain-ascend'];
            stats.cost += +props['cost'];
        }

        return stats;
    }
});<commit_msg>remove unused calcStats<commit_after>BR.Iternity = BR.Control.extend({
	  options: {
        heading: 'Iternity'
    },

    onAdd: function (map) {
        var container = BR.Control.prototype.onAdd.call(this, map);
        this.update();
        return container;
    },

    update: function (polyline, segments) {
        var i, j, iter, html = '';

        html += '
';
        for (i = 0; segments && i < segments.length; i++)
        {
            iter = segments[i].feature.iternity;
            for (j = 0; iter && j < iter.length; j++)
            {
              html += iter[j] + '\n';
            }
        }
        html += '
';

        this._content.innerHTML = html;
    }
});
<|endoftext|>


<commit_before>var path = require('path');
var entry = ['./src/index'];

if (process.env.NODE_ENV === undefined) {
  entry.unshift('webpack/hot/only-dev-server');
  entry.unshift('webpack-dev-server/client?http://localhost:8080');
}

module.exports = {
  devtool: 'cheap-module-eval-source-map',
  entry: entry,
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'bundle.js'
  },
  plugins: [

  ],
  module: {
    loaders: [
      {
        test: /\.js$/,
        loaders: [ 'babel' ],
        exclude: /node_modules/,
        include: __dirname
      }
    ]
  }
}

<commit_msg>use umd to export<commit_after>var path = require('path');
var entry = ['./src/index'];

if (process.env.NODE_ENV === undefined) {
  entry.unshift('webpack/hot/only-dev-server');
  entry.unshift('webpack-dev-server/client?http://localhost:8080');
}

module.exports = {
  devtool: 'cheap-module-eval-source-map',
  entry: entry,
  output: {
    path: path.join(__dirname, 'build'),
    libraryTarget:'umd',
    filename: 'bundle.js'
  },
  plugins: [

  ],
  module: {
    loaders: [
      {
        test: /\.js$/,
        loaders: [ 'babel' ],
        exclude: /node_modules/,
        include: __dirname
      }
    ]
  }
}

<|endoftext|>


<commit_before>// ## draeton.github.com
//
// Site code for my personal site
// [http://draeton.github.com/draeton.github.com](http://draeton.github.com/draeton.github.com)
//
// Copyright 2011, Matthew Cobbs
// Licensed under the MIT license.
//
/*global jQuery*/
(function (window, $) {

    var document = window.document;

    var Draeton = function () {
        this.init();
    };

    Draeton.prototype = {
        init: function () {
            this.bindHandlers();
            this.initMenu();
        },

        bindHandlers: function () {
            var self = this;
            var $search = $(".navbar form");
            var $freelanceModal = $("#freelance");
            var $freelanceForm = $("#freelance form");
            var $freelanceSend = $("#freelanceSend");

            // search form
            $search.on("click", "a.add-on", function () {
                $search.submit();
            });

            // tooltips
            if ($.fn.tooltip) {
                $('[data-toggle="tooltip"]').tooltip();
            }

            // freelance
            $freelanceSend.on("click", function () {
                $freelanceForm.submit();
            });

            $freelanceForm.on("submit", function () {
                var isValid = self.validateForm(this);

                if (isValid) {
                    $freelanceModal.modal("hide");
                }

                return isValid;
            });

            $freelanceForm.on("input", "change", function () {
                var isValid = self.validate(this);
            });
        },

        initMenu: function () {
            var href = document.location.href;

            $("ul.nav li > a").each(function () {
                if (this.href.replace(/[/#]+$/, "") === href.replace(/[/#]+$/, "")) {
                    $(this).parents("li").addClass("active");
                }
            });
        },

        validateForm: function (form) {
            var $form = $(form);
            var $inputs = $form.find(":input");
            var $errors = $inputs.filter(this.validate);
            var isValid = $errors.length === 0;

            return isValid;
        },

        validate: function (input) {
            var $input = $(input);
            var $group = $input.parents(".control-group");
            var isRequired = $input.is(":required");
            var isBlank = $input.val() === "";
            var isValid = true;

            if (isRequired && isBlank) {
                isValid = false;
            }

            return isValid;
        }
    };

    $(document).ready(function () {
        window.draeton = new Draeton();
    });

})(window, jQuery);<commit_msg>selection issues<commit_after>// ## draeton.github.com
//
// Site code for my personal site
// [http://draeton.github.com/draeton.github.com](http://draeton.github.com/draeton.github.com)
//
// Copyright 2011, Matthew Cobbs
// Licensed under the MIT license.
//
/*global jQuery*/
(function (window, $) {

    var document = window.document;

    var Draeton = function () {
        this.init();
    };

    Draeton.prototype = {
        init: function () {
            this.bindHandlers();
            this.initMenu();
        },

        bindHandlers: function () {
            var self = this;
            var $search = $(".navbar form");
            var $freelanceModal = $("#freelance");
            var $freelanceForm = $("#freelance form");
            var $freelanceSend = $("#freelanceSend");

            // search form
            $search.on("click", "a.add-on", function () {
                $search.submit();
            });

            // tooltips
            if ($.fn.tooltip) {
                $('[data-toggle="tooltip"]').tooltip();
            }

            // freelance
            $freelanceSend.on("click", function () {
                $freelanceForm.submit();
            });

            $freelanceForm.on("submit", function () {
                var isValid = self.validateForm(this);

                if (isValid) {
                    $freelanceModal.modal("hide");
                }

                return isValid;
            });

            $freelanceForm.on("input,textarea", "change", function () {
                var isValid = self.validate(this);
            });
        },

        initMenu: function () {
            var href = document.location.href;

            $("ul.nav li > a").each(function () {
                if (this.href.replace(/[/#]+$/, "") === href.replace(/[/#]+$/, "")) {
                    $(this).parents("li").addClass("active");
                }
            });
        },

        validateForm: function (form) {
            var $form = $(form);
            var $inputs = $form.find(":input");
            var $errors = $inputs.filter(this.validate);
            var isValid = $errors.length === 0;

            return isValid;
        },

        validate: function (input) {
            var $input = $(input);
            var $group = $input.parents(".control-group");
            var isRequired = $input.is(":required");
            var isBlank = $input.val() === "";
            var isValid = true;

            if (isRequired && isBlank) {
                isValid = false;
            }

            return isValid;
        }
    };

    $(document).ready(function () {
        window.draeton = new Draeton();
    });

})(window, jQuery);<|endoftext|>


<commit_before>define(
'controllers/main',
[
  'jQuery',
  'bookmarksApp',
  'services/bookmarksStorage',
  'filters/fieldsFilter',
  'controllers/editBookmark',
  'ui.bootstrap'
],
function($, bookmarksApp) { 'use strict';

/*
* Application controller.
*/
var MainController = function($scope, $filter, $modal, bookmarksStorage) {

  // Constant: default value of how many items we want to display on main page.
  var defaultTotalDisplayed = 20;

  $scope.searchText = ''; // Search text
  $scope.bookmarks = []; // All bookmarks
  $scope.orders = [ // Different sorting orders
                    {title:'Date', value: 'date'},
                    {title:'Title', value: 'title'},
                    {title:'URL', value: 'url'}
                  ];
  $scope.currentOrder = $scope.orders[0]; // date is default sorting order

  // Maximum number of items currently displayed
  $scope.totalDisplayed = defaultTotalDisplayed;

  $scope.selectedIndex = 0;

  $scope.hideTopLevelFolders = false;
  $scope.showThumbnails = true;

  // Auto add showing bookmarks when user scroll to page down
  var loadMorePlaceholder = $('#loadMorePlaceholder').get(0);
  $(window).scroll(function () {
    if (getFilteredBookmarks().length > $scope.totalDisplayed) {
      if (loadMorePlaceholder.getBoundingClientRect().top <= window.innerHeight) {
        $scope.totalDisplayed += defaultTotalDisplayed;
        $scope.$apply();
      }
    }
  });

  var getAllPanels = function() {
    return $('#list-bookmarks div.panel');
  };

  var isElementInViewport = function(el) {
    var rect = el.getBoundingClientRect();
    return rect.top >= 0 && rect.left >= 0 && rect.bottom <= $(window).height() && rect.right <= $(window).width();
  };

  // Key down events handlers
  $('#mainContent').keydown(function(e) {
    var updated = false;
    if (e.which === 13) { // Enter press on page - go to the selected bookmark
      _gaq.push(['_trackEvent', 'Navigation', 'keydown', 'Navigation via enter']);
      var result = getFilteredBookmarks();
      if (result.length > $scope.selectedIndex) {
        window.location.href = result[$scope.selectedIndex].url;
      }
    } else if (e.which === 37) { // left arrow key
      if ($scope.selectedIndex > 0) {
        $scope.selectedIndex--;
        updated = true;
      }
    } else if (e.which === 39) { // right arrow key
      if (getAllPanels().length > $scope.selectedIndex + 1) {
        $scope.selectedIndex++;
        updated = true;
      }

    } else if (e.which === 9 && e.shiftKey) { // shift+tab key
      if ($scope.selectedIndex > 0) {
        $scope.selectedIndex--;
        updated = true;
      }

    } else if (e.which === 9) { // tab key
      if (getAllPanels().length > $scope.selectedIndex + 1) {
        $scope.selectedIndex++;
        updated = true;
      }
    }
    if (updated) { // right arrow, left arrow, tab, and shift+tab key pressed - select next element
      $scope.$apply();
      var panels = getAllPanels();
      var selectedElement = panels.get($scope.selectedIndex);
      if (selectedElement) {
        var rect = selectedElement.getBoundingClientRect(); // If element is not visible - scroll to it
        if (!(rect.top >= 0 && rect.left >= 0 && rect.bottom <= $(window).height() && rect.right <= $(window).width())) {
          $("body").animate({
            scrollTop: ($(panels.get($scope.selectedIndex)).offset().top - $(panels.get(0)).offset().top)
          }, 500);
        }
      }
      return false;
    }
  });

  // Get bookmarks we show on the page (in right order)
  var getFilteredBookmarks = function() {
    var bookmarksFilter = $filter('fieldsFilter');
    return bookmarksFilter($scope.bookmarks, $scope.searchText, $scope.currentOrder.value);
  };

  var loadBookmarks = function() {
    bookmarksStorage.getAll(function(bookmarks, setttings) {
      bookmarksApp.appSettings = setttings;
      $scope.hideTopLevelFolders = setttings.hideTopLevelFolders;
      $scope.showThumbnails = setttings.showThumbnails;
      $scope.bookmarks = bookmarks;
      $scope.$apply();
    }.bind(this));
  }.bind(this);
  loadBookmarks();

  // Set maximum total displayed items to default and scroll to top of the page
  var resetView = function() {
    $scope.totalDisplayed = defaultTotalDisplayed;
    $scope.selectedIndex = 0;
    setTimeout(function() {
      window.scroll(0, 0);
    }, 10);
  };

  // When user change search string we scroll to top of the page and set total displayed items to default
  $scope.$watch('searchText', function() {
    resetView();
  });

  // On tag click we set search text
  $scope.selectTag = function(tag) {
    _gaq.push(['_trackEvent', 'Navigation', 'selectTag']);
    $scope.searchText = 'tag:' + tag;
  };

  // Change sorting order
  $scope.changeOrder = function(order) {
    _gaq.push(['_trackEvent', 'Navigation', 'changeOrder',  'Change order to ' + order]);
    $scope.currentOrder = order;
    resetView();
  };

  // Show modal dialog for adding tags
  $scope.editBookmark = function(bookmark) {
    _gaq.push(['_trackEvent', 'Navigation', 'editBookmark']);
     var modalInstance = $modal.open({
      scope: $scope.$new(true /* isolate */),
      templateUrl: 'partials/editBookmark.tpl.html',
      controller: 'editBookmarkController',
      resolve: {
        bookmark: function() {
          return bookmark;
        }
      },
      keyboard: true,
      backdrop: 'static'
    });

    modalInstance.result.then(function (updatedBookmark) {
      if (!updatedBookmark) {
        // Bookmark was deleted
        $scope.bookmarks.splice(_.indexOf($scope.bookmarks, bookmark), 1);
      }
    });

    return false;
  };

  $scope.selectBookmark = function(index) {
    $scope.selectedIndex = index;
  };

  $scope.setHideTopLevelFolders = function() {
    _gaq.push(['_trackEvent', 'ChangeSettings', 'HideTopLevelFolders changed to ' + !$scope.hideTopLevelFolders]);
    bookmarksStorage.setHideTopLevelFolders(!$scope.hideTopLevelFolders, loadBookmarks);
  };

  $scope.setShowThumbnails = function() {
    _gaq.push(['_trackEvent', 'ChangeSettings', 'ShowThumbnails changed to ' + !$scope.showThumbnails]);
    bookmarksStorage.setShowThumbnails(!$scope.showThumbnails, loadBookmarks);
  };
};

bookmarksApp.controller('mainController', ['$scope', '$filter', '$modal', 'bookmarksStorage', MainController]);

});

<commit_msg>Allow to use search engines from the search field (#56)<commit_after>define(
'controllers/main',
[
  'underscore',
  'jQuery',
  'bookmarksApp',
  'services/bookmarksStorage',
  'filters/fieldsFilter',
  'controllers/editBookmark',
  'ui.bootstrap'
],
function(_, $, bookmarksApp) { 'use strict';

/*
* Application controller.
*/
var MainController = function($scope, $filter, $modal, bookmarksStorage) {

  // Constant: default value of how many items we want to display on main page.
  var defaultTotalDisplayed = 20;

  $scope.searchText = ''; // Search text
  $scope.bookmarks = []; // All bookmarks
  $scope.orders = [ // Different sorting orders
                    {title:'Date', value: 'date'},
                    {title:'Title', value: 'title'},
                    {title:'URL', value: 'url'}
                  ];
  $scope.currentOrder = $scope.orders[0]; // date is default sorting order

  // Maximum number of items currently displayed
  $scope.totalDisplayed = defaultTotalDisplayed;

  $scope.selectedIndex = 0;

  $scope.hideTopLevelFolders = false;
  $scope.showThumbnails = true;

  // Auto add showing bookmarks when user scroll to page down
  var loadMorePlaceholder = $('#loadMorePlaceholder').get(0);
  $(window).scroll(function () {
    if (getFilteredBookmarks().length > $scope.totalDisplayed) {
      if (loadMorePlaceholder.getBoundingClientRect().top <= window.innerHeight) {
        $scope.totalDisplayed += defaultTotalDisplayed;
        $scope.$apply();
      }
    }
  });

  var getAllPanels = function() {
    return $('#list-bookmarks div.panel');
  };

  var isElementInViewport = function(el) {
    var rect = el.getBoundingClientRect();
    return rect.top >= 0 && rect.left >= 0 && rect.bottom <= $(window).height() && rect.right <= $(window).width();
  };

  // Key down events handlers
  $('#mainContent').keydown(function(e) {
    var updated = false;
    if (e.which === 13) { // Enter press on page - go to the selected bookmark
      _gaq.push(['_trackEvent', 'Navigation', 'keydown', 'Navigation via enter']);

      // If first pattern is not our filter let's assume that user wants to search on this domain
      var match = /^(\w+)(\.\w+)?:(.+)/i.exec($scope.searchText);
      if (match && !_(['tag', 'url', 'title']).contains(match[1])) {
        window.location = 'https://' + match[1] + (match[2] || '.com') + '/?q=' + encodeURIComponent(match[3].trim());
      } else {
        var result = getFilteredBookmarks();
        if (result.length > $scope.selectedIndex) {
          window.location.href = result[$scope.selectedIndex].url;
        }
      }
    } else if (e.which === 37) { // left arrow key
      if ($scope.selectedIndex > 0) {
        $scope.selectedIndex--;
        updated = true;
      }
    } else if (e.which === 39) { // right arrow key
      if (getAllPanels().length > $scope.selectedIndex + 1) {
        $scope.selectedIndex++;
        updated = true;
      }

    } else if (e.which === 9 && e.shiftKey) { // shift+tab key
      if ($scope.selectedIndex > 0) {
        $scope.selectedIndex--;
        updated = true;
      }

    } else if (e.which === 9) { // tab key
      if (getAllPanels().length > $scope.selectedIndex + 1) {
        $scope.selectedIndex++;
        updated = true;
      }
    }
    if (updated) { // right arrow, left arrow, tab, and shift+tab key pressed - select next element
      $scope.$apply();
      var panels = getAllPanels();
      var selectedElement = panels.get($scope.selectedIndex);
      if (selectedElement) {
        var rect = selectedElement.getBoundingClientRect(); // If element is not visible - scroll to it
        if (!(rect.top >= 0 && rect.left >= 0 && rect.bottom <= $(window).height() && rect.right <= $(window).width())) {
          $("body").animate({
            scrollTop: ($(panels.get($scope.selectedIndex)).offset().top - $(panels.get(0)).offset().top)
          }, 500);
        }
      }
      return false;
    }
  });

  // Get bookmarks we show on the page (in right order)
  var getFilteredBookmarks = function() {
    var bookmarksFilter = $filter('fieldsFilter');
    return bookmarksFilter($scope.bookmarks, $scope.searchText, $scope.currentOrder.value);
  };

  var loadBookmarks = function() {
    bookmarksStorage.getAll(function(bookmarks, setttings) {
      bookmarksApp.appSettings = setttings;
      $scope.hideTopLevelFolders = setttings.hideTopLevelFolders;
      $scope.showThumbnails = setttings.showThumbnails;
      $scope.bookmarks = bookmarks;
      $scope.$apply();
    }.bind(this));
  }.bind(this);
  loadBookmarks();

  // Set maximum total displayed items to default and scroll to top of the page
  var resetView = function() {
    $scope.totalDisplayed = defaultTotalDisplayed;
    $scope.selectedIndex = 0;
    setTimeout(function() {
      window.scroll(0, 0);
    }, 10);
  };

  // When user change search string we scroll to top of the page and set total displayed items to default
  $scope.$watch('searchText', function() {
    resetView();
  });

  // On tag click we set search text
  $scope.selectTag = function(tag) {
    _gaq.push(['_trackEvent', 'Navigation', 'selectTag']);
    $scope.searchText = 'tag:' + tag;
  };

  // Change sorting order
  $scope.changeOrder = function(order) {
    _gaq.push(['_trackEvent', 'Navigation', 'changeOrder',  'Change order to ' + order]);
    $scope.currentOrder = order;
    resetView();
  };

  // Show modal dialog for adding tags
  $scope.editBookmark = function(bookmark) {
    _gaq.push(['_trackEvent', 'Navigation', 'editBookmark']);
     var modalInstance = $modal.open({
      scope: $scope.$new(true /* isolate */),
      templateUrl: 'partials/editBookmark.tpl.html',
      controller: 'editBookmarkController',
      resolve: {
        bookmark: function() {
          return bookmark;
        }
      },
      keyboard: true,
      backdrop: 'static'
    });

    modalInstance.result.then(function (updatedBookmark) {
      if (!updatedBookmark) {
        // Bookmark was deleted
        $scope.bookmarks.splice(_.indexOf($scope.bookmarks, bookmark), 1);
      }
    });

    return false;
  };

  $scope.selectBookmark = function(index) {
    $scope.selectedIndex = index;
  };

  $scope.setHideTopLevelFolders = function() {
    _gaq.push(['_trackEvent', 'ChangeSettings', 'HideTopLevelFolders changed to ' + !$scope.hideTopLevelFolders]);
    bookmarksStorage.setHideTopLevelFolders(!$scope.hideTopLevelFolders, loadBookmarks);
  };

  $scope.setShowThumbnails = function() {
    _gaq.push(['_trackEvent', 'ChangeSettings', 'ShowThumbnails changed to ' + !$scope.showThumbnails]);
    bookmarksStorage.setShowThumbnails(!$scope.showThumbnails, loadBookmarks);
  };
};

bookmarksApp.controller('mainController', ['$scope', '$filter', '$modal', 'bookmarksStorage', MainController]);

});

<|endoftext|>


<commit_before>window.onload = function() {
  'use strict';

  var insultButton = document.getElementById('insultButton'),
  insultString;

  // Returns a random number between min and max
  function getRandomArbitrary(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
  }

  // Returns string with first letter capitalized
  function capitaliseFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
  }

  function generateInsult() {
    var insult = document.getElementById('insult'),

    nouns = [
      'ass',
      'fuck',
      'dick',
      'vagina',
      'twat',
      'cunt',
      'sleaze',
      'gas',
      'butt',
      'puss',
      'piss',
      'belch',
      'crack',
      'glass',
      'douche',
      'scrote',
      'dong',
      'skank',
      'crust',
      'train',
      'shit',
      'scrotum',
      'mucus',
      'snot',
      'rasp',
      'bunion',
      'noodle',
      'whistle',
      'paster',
      'poster',
      'chicken',
      'rectum',
      'cock',
      'fart',
      'pipe',
      'wax',
      'bolt',
      'blanket',
      'lunch',
      'continent',
      'grass',
      'squelch',
      'candle',
      'pants',
      'taint',
      'wad',
      'rag',
      'tampon',
      'condom',
      'barf',
      'pillow',
      'book',
      'fashion',
      'razor',
      'sex',
      'pogo stick',
      'lamp post',
      'weasle',
      'toe',
      'eye lid',
      'intestine',
      'ear hole',
      'nose',
      'cheese',
      'urine',
      'finger',
      'blood',
      'keyboard',
      'phone',
      'internet',
      'grab bag',
      'Santa Claus',
      'antique',
      'gravity',
      'lawn',
      'road house',
      'house party',
      'Sasquatch',
      'white house',
      'forest',
      'under water',
      'Kim Jun Un',
      'Rodney Dangerfield',
      'Power Rangers',
      'Batman',
      'Pink Panther',
      'Looney Tunes',
      'Robin Williams',
      'Vanna White',
      'Huckleberry Fin',
      'Bob Dylan',
      'Dungeons and Dragons',
      'Sherlock Holmes',
      'Barbara Walters',
      'Peter O\'Toole',
      'Mole People',
      'Hobbit',
      'Kim Kardashian',
      'zeitgeist',
      'teleporter',
      'William Shatner',
      'Elizebeth Hasselbeck',
      'turd',
      'bloat',
      'monkey',
      'laser',
      'swamp',
    ],

    ings = [
      'fucking',
      'shitting',
      'coasting',
      'farting',
      'pissing',
      'smelting',
      'munching',
      'clapping',
      'rinsing',
      'juicing',
      'plumbing',
      'churning',
      'belching',
      'burping',
      'barfing',
      'screwing',
      'plotting',
      'touching',
      'passing',
      'gassing',
      'coughing',
      'staining',
      'climbing',
      'blasting',
      'eating',
      'chewing',
      'smelling',
      'squirting',
      'blaming',
      'stealing',
      'moving',
      'swiping',
      'peeling',
      'pleading',
      'bleeding',
      'boiling',
      'placating',
      'porking',
      'gunking',
      'parting',
      'placing',
      'pulling',
      'yanking',
      'pushing',
      'carrying',
      'lifting',
      'flexing',
      'trimming',
      'trashing',
      'smashing',
      'crashing',
      'flashing',
      'reading',
      'purchasing',
    ],

    ers = [
      'fucker',
      'shitter',
      'farter',
      'eater',
      'pleaser',
      'consultant',
      'putz',
      'packer',
      'server',
      'chopper',
      'worker',
      'bagger',
      'stuffer',
      'owner',
      'cleaner',
      'watcher',
      'wiper',
      'tamer',
      'pisser',
      'planner',
      'manager',
      'officer',
      'surgeon',
      'carpenter',
      'waiter',
      'chef',
      'waxer',
      'fluffer',
      'placater',
      'tour guide',
      'fondler',
      'pickler',
      'caster',
      'dictator',
      'believer',
      'slammer',
      'grammarian',
      'politician',
      'teacher',
      'artist',
      'actor',
      'story teller',
      'stainer',
      'pilot',
      'sniffer',
    ],

    num1 = getRandomArbitrary(0, nouns.length - 1),
    num2 = getRandomArbitrary(0, ings.length - 1),
    num3 = getRandomArbitrary(0, nouns.length - 1),
    num4 = getRandomArbitrary(0, ers.length - 1),

    insultee = document.getElementById('insultee').value,

    insultString = nouns[num1] + ' ' + ings[num2] + ' ' + nouns[num3] + ' ' + ers[num4];

    if (insultee !== '') {
      insultString = insultee + ', you\'re a ' + insultString;
    }

    insult.innerHTML = capitaliseFirstLetter(insultString);

    return insultString;
  }
  insultString = generateInsult();

  insultButton.addEventListener('click', generateInsult, false);
};
<commit_msg>Add chief.<commit_after>window.onload = function() {
  'use strict';

  var insultButton = document.getElementById('insultButton'),
  insultString;

  // Returns a random number between min and max
  function getRandomArbitrary(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
  }

  // Returns string with first letter capitalized
  function capitaliseFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
  }

  function generateInsult() {
    var insult = document.getElementById('insult'),

    nouns = [
      'ass',
      'fuck',
      'dick',
      'vagina',
      'twat',
      'cunt',
      'sleaze',
      'gas',
      'butt',
      'puss',
      'piss',
      'belch',
      'crack',
      'glass',
      'douche',
      'scrote',
      'dong',
      'skank',
      'crust',
      'train',
      'shit',
      'scrotum',
      'mucus',
      'snot',
      'rasp',
      'bunion',
      'noodle',
      'whistle',
      'paster',
      'poster',
      'chicken',
      'rectum',
      'cock',
      'fart',
      'pipe',
      'wax',
      'bolt',
      'blanket',
      'lunch',
      'continent',
      'grass',
      'squelch',
      'candle',
      'pants',
      'taint',
      'wad',
      'rag',
      'tampon',
      'condom',
      'barf',
      'pillow',
      'book',
      'fashion',
      'razor',
      'sex',
      'pogo stick',
      'lamp post',
      'weasle',
      'toe',
      'eye lid',
      'intestine',
      'ear hole',
      'nose',
      'cheese',
      'urine',
      'finger',
      'blood',
      'keyboard',
      'phone',
      'internet',
      'grab bag',
      'Santa Claus',
      'antique',
      'gravity',
      'lawn',
      'road house',
      'house party',
      'Sasquatch',
      'white house',
      'forest',
      'under water',
      'Kim Jun Un',
      'Rodney Dangerfield',
      'Power Rangers',
      'Batman',
      'Pink Panther',
      'Looney Tunes',
      'Robin Williams',
      'Vanna White',
      'Huckleberry Fin',
      'Bob Dylan',
      'Dungeons and Dragons',
      'Sherlock Holmes',
      'Barbara Walters',
      'Peter O\'Toole',
      'Mole People',
      'Hobbit',
      'Kim Kardashian',
      'zeitgeist',
      'teleporter',
      'William Shatner',
      'Elizebeth Hasselbeck',
      'turd',
      'bloat',
      'monkey',
      'laser',
      'swamp',
    ],

    ings = [
      'fucking',
      'shitting',
      'coasting',
      'farting',
      'pissing',
      'smelting',
      'munching',
      'clapping',
      'rinsing',
      'juicing',
      'plumbing',
      'churning',
      'belching',
      'burping',
      'barfing',
      'screwing',
      'plotting',
      'touching',
      'passing',
      'gassing',
      'coughing',
      'staining',
      'climbing',
      'blasting',
      'eating',
      'chewing',
      'smelling',
      'squirting',
      'blaming',
      'stealing',
      'moving',
      'swiping',
      'peeling',
      'pleading',
      'bleeding',
      'boiling',
      'placating',
      'porking',
      'gunking',
      'parting',
      'placing',
      'pulling',
      'yanking',
      'pushing',
      'carrying',
      'lifting',
      'flexing',
      'trimming',
      'trashing',
      'smashing',
      'crashing',
      'flashing',
      'reading',
      'purchasing',
    ],

    ers = [
      'fucker',
      'shitter',
      'farter',
      'eater',
      'pleaser',
      'consultant',
      'putz',
      'packer',
      'server',
      'chopper',
      'worker',
      'bagger',
      'stuffer',
      'owner',
      'cleaner',
      'watcher',
      'wiper',
      'tamer',
      'pisser',
      'planner',
      'manager',
      'officer',
      'surgeon',
      'carpenter',
      'waiter',
      'chef',
      'chief',
      'waxer',
      'fluffer',
      'placater',
      'tour guide',
      'fondler',
      'pickler',
      'caster',
      'dictator',
      'believer',
      'slammer',
      'grammarian',
      'politician',
      'teacher',
      'artist',
      'actor',
      'story teller',
      'stainer',
      'pilot',
      'sniffer',
    ],

    num1 = getRandomArbitrary(0, nouns.length - 1),
    num2 = getRandomArbitrary(0, ings.length - 1),
    num3 = getRandomArbitrary(0, nouns.length - 1),
    num4 = getRandomArbitrary(0, ers.length - 1),

    insultee = document.getElementById('insultee').value,

    insultString = nouns[num1] + ' ' + ings[num2] + ' ' + nouns[num3] + ' ' + ers[num4];

    if (insultee !== '') {
      insultString = insultee + ', you\'re a ' + insultString;
    }

    insult.innerHTML = capitaliseFirstLetter(insultString);

    return insultString;
  }
  insultString = generateInsult();

  insultButton.addEventListener('click', generateInsult, false);
};
<|endoftext|>