<commit_before>// Amara, universalsubtitles.org
//
// Copyright (C) 2012 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

var AmaraDFXPParser = function(AmaraDFXPParser) {
    /*
     * A utility for working with DFXP subs.
     */

    var that = this;
    var $ = window.AmarajQuery.noConflict();

    this.init = function(xml) {

        if (typeof xml === 'string') {
            xml = $.parseXML(xml);
        }

        // Store the original XML for comparison later.
        this.$originalXml = $(xml.documentElement).clone();

        // Store the working XML for local edits.
        this.$xml = $(xml.documentElement).clone();

        // Cache the query for the containing div.
        this.$div = $('div', this.$xml);

    };

    // Helper methods.
    this.utils = {

        xmlToString: function(xml) {
            /*
             * Convert an XML document to a string.
             * Accepts: document object (XML tree)
             * Returns: string
             */

            var xmlString;

            // For Internet Explorer.
            if (window.ActiveXObject) {
                xmlString = xml.xml;

            // Everyone else.
            } else {
                xmlString = (new XMLSerializer()).serializeToString(xml);
            }

            return xmlString;
        }

    };

    this.addSubtitle = function(after, newAttrs, content) {
        /*
         * For adding a new subtitle to this set.
         *
         * If `after` is provided, we'll place the new subtitle directly
         * after that one. Otherwise, we'll place the new subtitle at the
         * beginning or end depending on subtitle count.
         *
         * `newAttrs` is an optional JSON object specifying the attributes to
         * be applied to the new element.
         *
         * `content` is an optional string to set the initial content.
         *
         * Returns: new subtitle element
         */

        if (typeof after != 'number') {

            // If we have subtitles, default placement should be at the end.
            if (this.subtitlesCount()) {
                after = this.getLastSubtitle();

            // Otherwise, place the first subtitle at the beginning.
            } else {
                after = -1;
            }
        }

        // Create the new element manually. If you create with jQuery, it'll use
        // the document's namespace as the default namespace, which is ugly.
        var newSubtitle = document.createElementNS('', 'p');

        // Init the default attrs and combine them with the defined newAttrs if
        // required.
        newAttrs = $.extend({
            'begin': '',
            'end': ''
        }, newAttrs);

        var $newSubtitle = $(newSubtitle).attr(newAttrs);

        if (typeof content !== 'undefined') {
            if (content === null) {
                this.content($newSubtitle, '');
            } else {
                this.content($newSubtitle, content);
            }
        }

        // Finally, place the new subtitle.
        //
        // If after is -1, we need to place the subtitle at the beginning.
        if (after === -1) {

            // Prepend this new subtitle directly inside of the containing div.
            this.$div.prepend($newSubtitle);

        // Otherwise, place it after the designated subtitle.
        } else {

            // First just make sure that the previous subtitle exists.
            var $previousSubtitle = this.getSubtitle(after);

            // Then place it.
            $previousSubtitle.after($newSubtitle);
        }

        return $newSubtitle.get(0);
    };
    this.changesMade = function() {
        /*
         * Check to see if any changes have been made to the working XML.
         *
         * Returns: true || false
         */

        var originalString = that.utils.xmlToString(that.$originalXml.get(0));
        var xmlString = that.utils.xmlToString(that.$xml.get(0));

        return originalString != xmlString;
    };
    this.clearAllContent = function() {
        /*
         * Clear all of the content data for every subtitle.
         *
         * Returns: true
         */

        this.getSubtitles().text('');
    };
    this.clearAllTimes = function() {
        /*
         * Clear all of the timing data for every subtitle.
         *
         * Returns: true
         */

        this.getSubtitles().attr({
            'begin': '',
            'end': ''
        });

        return true;
    };
    this.content = function(indexOrElement, content) {
        /*
         * Either get or set the HTML content for the subtitle.
         *
         * Returns: current content (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof content !== 'undefined') {
            $subtitle.text(content);
        }

        // OK. So, when parsing an XML node, you can't just get the HTML content.
        // We need to retrieve the total contents of the node by using "contents()",
        // but that returns an array of objects, like ['', 'hi', '', '
'],
        // etc. So we create a temporary div, and append the array to it, and retrieve
        // the rendered HTML that way. Then remove the temporary div.
        //
        // Reference: http://bit.ly/SwbPeR
        return $('
').append($subtitle.contents().clone()).remove().html();

    };
    this.endTime = function(indexOrElement, endTime) {
        /*
         * Either get or set the end time for the subtitle.
         *
         * Returns: current end time (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof endTime !== 'undefined') {
            if (parseFloat(endTime)) {
                $subtitle.attr('end', endTime);
            } else {
                $subtitle.attr('end', '');
            }
        }

        return $subtitle.attr('end');
    };
    this.getFirstSubtitle = function() {
        /*
         * Retrieve the first subtitle in this set.
         *
         * Returns: first subtitle element
         */

        return this.getSubtitle(0).get(0);
    };
    this.getLastSubtitle = function() {
        /*
         * Retrieve the last subtitle in this set.
         *
         * Returns: subtitle element or empty array
         */

        // Cache the selection.
        var $subtitles = this.getSubtitles();

        return this.getSubtitle($subtitles.length - 1).get(0);
    };
    this.getNextSubtitle = function(indexOrElement) {
        /*
         * Retrieve the subtitle that follows the given subtitle.
         *
         * Returns: subtitle element or empty array
         */

        return this.getSubtitle(indexOrElement).next();
    };
    this.getNonBlankSubtitles = function() {
        /*
         * Retrieve all of the subtitles that have content.
         *
         * Returns: jQuery selection of elements
         */

        return this.getSubtitles().filter(function() {
            return $(this).text() !== '';
        });
    };
    this.getPreviousSubtitle = function(indexOrElement) {
        /*
         * Retrieve the subtitle that precedes the given subtitle.
         *
         * Returns: subtitle element
         */

        return this.getSubtitle(indexOrElement).prev();
    };
    this.getSubtitle = function(indexOrElement) {
        /*
         * Retrieve the subtitle based on the index given.
         *
         * If the given argument is an object, assume it's a DOM node
         * and check to make sure it exists in the subtitles tree.
         *
         * Returns: jQuery selection of element.
         *
         */

        // If an index or an object is not provided, throw an error.
        if (typeof indexOrElement !== 'number' && typeof indexOrElement !== 'object') {
            throw new Error('DFXP: You must supply either an index or an element.');
        }

        var subtitle;

        // If indexOrElement is a number, we'll need to query the DOM to
        // get the element.
        //
        // Note: you should only use this approach for checking one-off
        // subtitles. If you're checking more than one subtitle, it's much
        // faster to pass along pre-selected elements instead.
        //
        // Note also: if you're performing actions on a larger number of
        // subtitles, please consider using getSubtitles() and manually
        // iterating, to avoid performing DOM selection for lots of nodes.
        if (typeof indexOrElement === 'number') {
            subtitle = this.getSubtitles().get(indexOrElement);

        // Otherwise, make sure the element exists.
        } else {

            var $subtitles = this.getSubtitles();

            for (var i= 0; i < $subtitles.length; i++){
                if ($subtitles.get(i) == indexOrElement){
                    subtitle = $subtitles.get(i);
                    break;
                }
            }

        }

        if (!subtitle) {
            return null;
        }

        return $(subtitle);
    };
    this.getSubtitles = function() {
        /*
         * Retrieve the current set of subtitles.
         *
         * Returns: jQuery selection of nodes
         */

        return $('div > *', this.$xml);
    };
    this.isShownAt = function(indexOrElement, time) {
        /*
         * Determine whether the given subtitle should be displayed
         * at the given time.
         *
         * Returns: true || false
         */

        var subtitle = this.getSubtitle(indexOrElement).get(0);

        return (time >= this.startTime(subtitle) &&
                time <= this.endTime(subtitle));
    };
    this.needsAnySynced = function() {
        /*
         * Determine whether any of the subtitles in the set need
         * to be synced.
         *
         * Returns: true || false
         */

        var $subtitles = this.getSubtitles();

        for (var i = 0; i < $subtitles.length; i++) {
            var $subtitle = $subtitles.eq(i);

            var startTime = $subtitle.attr('begin');
            var endTime = $subtitle.attr('end');

            // If start time is empty, it always needs to be synced.
            if (startTime === '') {
                return true;
            }

            // If the end time is empty and this is not the last subtitle,
            // it needs to be synced.
            if (endTime === '' && (subtitle !== this.getLastSubtitle())) {
                return true;
            }

        }

        // Otherwise, we're good.
        return false;
    };
    this.needsAnyTranscribed = function() {
        /*
         * Check all of the subtitles for empty content.
         *
         * Returns: true || false
         */

        var $subtitles = this.getSubtitles();

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

            var $subtitle = $subtitles.eq(i);
            var content = $('
').append($subtitle.contents().clone()).remove().html();

            if (content === '') {
                return true;
            }
        }

        return false;
    };
    this.needsSyncing = function(indexOrElement) {
        /*
         * Given the zero-index or the element of the subtitle to be
         * checked, determine whether the subtitle needs to be synced.
         *
         * In most cases, if a subtitle has either no start time,
         * or no end time, it needs to be synced. However, if the
         * subtitle is the last in the list, the end time may be
         * omitted.
         *
         * Returns: true || false
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        var startTime = $subtitle.attr('begin');
        var endTime = $subtitle.attr('end');

        // If start time is empty, it always needs to be synced.
        if (startTime === '') {
            return true;
        }

        // If the end time is empty and this is not the last subtitle,
        // it needs to be synced.
        if (endTime === '' && (subtitle !== this.getLastSubtitle())) {
            return true;
        }

        // Otherwise, we're good.
        return false;
    };
    this.originalContent = function(indexOrElement, content) {
        /*
         * Either get or set the original HTML content for the subtitle.
         *
         * Returns: current original content (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof content !== 'undefined') {
            $subtitle.attr('originalcontent', content);
        }

        return $subtitle.attr('originalcontent');

    };
    this.originalEndTime = function(indexOrElement, originalEndTime) {
        /*
         * Either get or set the original end time for the subtitle.
         *
         * Returns: current original end time (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof originalEndTime !== 'undefined') {
            if (parseFloat(originalEndTime)) {
                $subtitle.attr('originalend', originalEndTime);
            } else {
                $subtitle.attr('originalend', '');
            }
        }

        return $subtitle.attr('originalend');
    };
    this.originalStartTime = function(indexOrElement, originalStartTime) {
        /*
         * Either get or set the original start time for the subtitle.
         *
         * Returns: current original start time (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof originalStartTime !== 'undefined') {
            if (parseFloat(originalStartTime)) {
                $subtitle.attr('originalbegin', originalStartTime);
            } else {
                $subtitle.attr('originalbegin', '');
            }
        }

        return $subtitle.attr('originalbegin');
    };
    this.originalXmlToString = function() {
        return this.utils.xmlToString(this.$originalXml.get(0));
    };
    this.removeSubtitle = function(indexOrElement) {
        /*
         * Given the zero-index of the subtitle to be removed,
         * remove it from the node tree.
         *
         * Returns: true
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        $subtitle.remove();

        return true;
    };
    this.removeSubtitles = function() {
        /*
         * Remove all subtitles from the working set.
         *
         * Returns: true
         */

        this.getSubtitles().remove();

        return true;
    };
    this.resetSubtitles = function() {
        /*
         * For each subtitle, if the content is empty, delete the subtitle.
         * Otherwise, just reset the text and the start/end times.
         *
         * Returns: true
         */

        var $subtitles = this.getSubtitles();

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

            var $subtitle = $subtitles.eq(i);
            var content = $('
').append($subtitle.contents().clone()).remove().html();

            if (content === '') {
                $subtitle.remove();
            } else {
                $subtitle.text('').attr({
                    'begin': '',
                    'end': ''
                });
            }
        }
    };
    this.startOfParagraph = function(indexOrElement, startOfParagraph) {
        /*
         * Either get or set the startofparagraph attr for the subtitle.
         *
         * Returns: current state of startofparagraph (boolean)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof startOfParagraph !== 'undefined') {
            $subtitle.attr('startofparagraph', startOfParagraph);
        }

        // We return a string for 'false' because this is an attr on the actual
        // XML node. It'll always be a string.
        return $subtitle.attr('startofparagraph') || 'false';
    };
    this.startTime = function(indexOrElement, startTime) {
        /*
         * Either get or set the start time for the subtitle.
         *
         * Returns: current start time (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof startTime !== 'undefined') {
            if (parseFloat(startTime)) {
                $subtitle.attr('begin', startTime);
            } else {
                $subtitle.attr('begin', '');
            }
        }

        return $subtitle.attr('begin');
    };
    this.subtitlesCount = function() {
        /*
         * Retrieve the current number of subtitles.
         *
         * Returns: integer
         */

        return this.getSubtitles().length;
    };
    this.xmlToString = function() {
        return this.utils.xmlToString(this.$xml.get(0));
    };
};
<commit_msg>More specific equality checking. Caught by sjl's eagle-eye via https://github.com/pculture/unisubs/commit/9a57db5a89d2be5e7c6ce6074a7553465fe5f7e0#commitcomment-2177212<commit_after>// Amara, universalsubtitles.org
//
// Copyright (C) 2012 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

var AmaraDFXPParser = function(AmaraDFXPParser) {
    /*
     * A utility for working with DFXP subs.
     */

    var that = this;
    var $ = window.AmarajQuery.noConflict();

    this.init = function(xml) {

        if (typeof xml === 'string') {
            xml = $.parseXML(xml);
        }

        // Store the original XML for comparison later.
        this.$originalXml = $(xml.documentElement).clone();

        // Store the working XML for local edits.
        this.$xml = $(xml.documentElement).clone();

        // Cache the query for the containing div.
        this.$div = $('div', this.$xml);

    };

    // Helper methods.
    this.utils = {

        xmlToString: function(xml) {
            /*
             * Convert an XML document to a string.
             * Accepts: document object (XML tree)
             * Returns: string
             */

            var xmlString;

            // For Internet Explorer.
            if (window.ActiveXObject) {
                xmlString = xml.xml;

            // Everyone else.
            } else {
                xmlString = (new XMLSerializer()).serializeToString(xml);
            }

            return xmlString;
        }

    };

    this.addSubtitle = function(after, newAttrs, content) {
        /*
         * For adding a new subtitle to this set.
         *
         * If `after` is provided, we'll place the new subtitle directly
         * after that one. Otherwise, we'll place the new subtitle at the
         * beginning or end depending on subtitle count.
         *
         * `newAttrs` is an optional JSON object specifying the attributes to
         * be applied to the new element.
         *
         * `content` is an optional string to set the initial content.
         *
         * Returns: new subtitle element
         */

        if (typeof after != 'number') {

            // If we have subtitles, default placement should be at the end.
            if (this.subtitlesCount()) {
                after = this.getLastSubtitle();

            // Otherwise, place the first subtitle at the beginning.
            } else {
                after = -1;
            }
        }

        // Create the new element manually. If you create with jQuery, it'll use
        // the document's namespace as the default namespace, which is ugly.
        var newSubtitle = document.createElementNS('', 'p');

        // Init the default attrs and combine them with the defined newAttrs if
        // required.
        newAttrs = $.extend({
            'begin': '',
            'end': ''
        }, newAttrs);

        var $newSubtitle = $(newSubtitle).attr(newAttrs);

        if (typeof content !== 'undefined') {
            if (content === null) {
                this.content($newSubtitle, '');
            } else {
                this.content($newSubtitle, content);
            }
        }

        // Finally, place the new subtitle.
        //
        // If after is -1, we need to place the subtitle at the beginning.
        if (after === -1) {

            // Prepend this new subtitle directly inside of the containing div.
            this.$div.prepend($newSubtitle);

        // Otherwise, place it after the designated subtitle.
        } else {

            // First just make sure that the previous subtitle exists.
            var $previousSubtitle = this.getSubtitle(after);

            // Then place it.
            $previousSubtitle.after($newSubtitle);
        }

        return $newSubtitle.get(0);
    };
    this.changesMade = function() {
        /*
         * Check to see if any changes have been made to the working XML.
         *
         * Returns: true || false
         */

        var originalString = that.utils.xmlToString(that.$originalXml.get(0));
        var xmlString = that.utils.xmlToString(that.$xml.get(0));

        return originalString != xmlString;
    };
    this.clearAllContent = function() {
        /*
         * Clear all of the content data for every subtitle.
         *
         * Returns: true
         */

        this.getSubtitles().text('');
    };
    this.clearAllTimes = function() {
        /*
         * Clear all of the timing data for every subtitle.
         *
         * Returns: true
         */

        this.getSubtitles().attr({
            'begin': '',
            'end': ''
        });

        return true;
    };
    this.content = function(indexOrElement, content) {
        /*
         * Either get or set the HTML content for the subtitle.
         *
         * Returns: current content (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof content !== 'undefined') {
            $subtitle.text(content);
        }

        // OK. So, when parsing an XML node, you can't just get the HTML content.
        // We need to retrieve the total contents of the node by using "contents()",
        // but that returns an array of objects, like ['', 'hi', '', '
'],
        // etc. So we create a temporary div, and append the array to it, and retrieve
        // the rendered HTML that way. Then remove the temporary div.
        //
        // Reference: http://bit.ly/SwbPeR
        return $('
').append($subtitle.contents().clone()).remove().html();

    };
    this.endTime = function(indexOrElement, endTime) {
        /*
         * Either get or set the end time for the subtitle.
         *
         * Returns: current end time (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof endTime !== 'undefined') {
            if (parseFloat(endTime)) {
                $subtitle.attr('end', endTime);
            } else {
                $subtitle.attr('end', '');
            }
        }

        return $subtitle.attr('end');
    };
    this.getFirstSubtitle = function() {
        /*
         * Retrieve the first subtitle in this set.
         *
         * Returns: first subtitle element
         */

        return this.getSubtitle(0).get(0);
    };
    this.getLastSubtitle = function() {
        /*
         * Retrieve the last subtitle in this set.
         *
         * Returns: subtitle element or empty array
         */

        // Cache the selection.
        var $subtitles = this.getSubtitles();

        return this.getSubtitle($subtitles.length - 1).get(0);
    };
    this.getNextSubtitle = function(indexOrElement) {
        /*
         * Retrieve the subtitle that follows the given subtitle.
         *
         * Returns: subtitle element or empty array
         */

        return this.getSubtitle(indexOrElement).next();
    };
    this.getNonBlankSubtitles = function() {
        /*
         * Retrieve all of the subtitles that have content.
         *
         * Returns: jQuery selection of elements
         */

        return this.getSubtitles().filter(function() {
            return $(this).text() !== '';
        });
    };
    this.getPreviousSubtitle = function(indexOrElement) {
        /*
         * Retrieve the subtitle that precedes the given subtitle.
         *
         * Returns: subtitle element
         */

        return this.getSubtitle(indexOrElement).prev();
    };
    this.getSubtitle = function(indexOrElement) {
        /*
         * Retrieve the subtitle based on the index given.
         *
         * If the given argument is an object, assume it's a DOM node
         * and check to make sure it exists in the subtitles tree.
         *
         * Returns: jQuery selection of element.
         *
         */

        // If an index or an object is not provided, throw an error.
        if (typeof indexOrElement !== 'number' && typeof indexOrElement !== 'object') {
            throw new Error('DFXP: You must supply either an index or an element.');
        }

        var subtitle;

        // If indexOrElement is a number, we'll need to query the DOM to
        // get the element.
        //
        // Note: you should only use this approach for checking one-off
        // subtitles. If you're checking more than one subtitle, it's much
        // faster to pass along pre-selected elements instead.
        //
        // Note also: if you're performing actions on a larger number of
        // subtitles, please consider using getSubtitles() and manually
        // iterating, to avoid performing DOM selection for lots of nodes.
        if (typeof indexOrElement === 'number') {
            subtitle = this.getSubtitles().get(indexOrElement);

        // Otherwise, make sure the element exists.
        } else {

            var $subtitles = this.getSubtitles();

            for (var i= 0; i < $subtitles.length; i++){
                if ($subtitles.get(i) === indexOrElement){
                    subtitle = $subtitles.get(i);
                    break;
                }
            }

        }

        if (!subtitle) {
            return null;
        }

        return $(subtitle);
    };
    this.getSubtitles = function() {
        /*
         * Retrieve the current set of subtitles.
         *
         * Returns: jQuery selection of nodes
         */

        return $('div > *', this.$xml);
    };
    this.isShownAt = function(indexOrElement, time) {
        /*
         * Determine whether the given subtitle should be displayed
         * at the given time.
         *
         * Returns: true || false
         */

        var subtitle = this.getSubtitle(indexOrElement).get(0);

        return (time >= this.startTime(subtitle) &&
                time <= this.endTime(subtitle));
    };
    this.needsAnySynced = function() {
        /*
         * Determine whether any of the subtitles in the set need
         * to be synced.
         *
         * Returns: true || false
         */

        var $subtitles = this.getSubtitles();

        for (var i = 0; i < $subtitles.length; i++) {
            var $subtitle = $subtitles.eq(i);

            var startTime = $subtitle.attr('begin');
            var endTime = $subtitle.attr('end');

            // If start time is empty, it always needs to be synced.
            if (startTime === '') {
                return true;
            }

            // If the end time is empty and this is not the last subtitle,
            // it needs to be synced.
            if (endTime === '' && (subtitle !== this.getLastSubtitle())) {
                return true;
            }

        }

        // Otherwise, we're good.
        return false;
    };
    this.needsAnyTranscribed = function() {
        /*
         * Check all of the subtitles for empty content.
         *
         * Returns: true || false
         */

        var $subtitles = this.getSubtitles();

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

            var $subtitle = $subtitles.eq(i);
            var content = $('
').append($subtitle.contents().clone()).remove().html();

            if (content === '') {
                return true;
            }
        }

        return false;
    };
    this.needsSyncing = function(indexOrElement) {
        /*
         * Given the zero-index or the element of the subtitle to be
         * checked, determine whether the subtitle needs to be synced.
         *
         * In most cases, if a subtitle has either no start time,
         * or no end time, it needs to be synced. However, if the
         * subtitle is the last in the list, the end time may be
         * omitted.
         *
         * Returns: true || false
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        var startTime = $subtitle.attr('begin');
        var endTime = $subtitle.attr('end');

        // If start time is empty, it always needs to be synced.
        if (startTime === '') {
            return true;
        }

        // If the end time is empty and this is not the last subtitle,
        // it needs to be synced.
        if (endTime === '' && (subtitle !== this.getLastSubtitle())) {
            return true;
        }

        // Otherwise, we're good.
        return false;
    };
    this.originalContent = function(indexOrElement, content) {
        /*
         * Either get or set the original HTML content for the subtitle.
         *
         * Returns: current original content (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof content !== 'undefined') {
            $subtitle.attr('originalcontent', content);
        }

        return $subtitle.attr('originalcontent');

    };
    this.originalEndTime = function(indexOrElement, originalEndTime) {
        /*
         * Either get or set the original end time for the subtitle.
         *
         * Returns: current original end time (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof originalEndTime !== 'undefined') {
            if (parseFloat(originalEndTime)) {
                $subtitle.attr('originalend', originalEndTime);
            } else {
                $subtitle.attr('originalend', '');
            }
        }

        return $subtitle.attr('originalend');
    };
    this.originalStartTime = function(indexOrElement, originalStartTime) {
        /*
         * Either get or set the original start time for the subtitle.
         *
         * Returns: current original start time (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof originalStartTime !== 'undefined') {
            if (parseFloat(originalStartTime)) {
                $subtitle.attr('originalbegin', originalStartTime);
            } else {
                $subtitle.attr('originalbegin', '');
            }
        }

        return $subtitle.attr('originalbegin');
    };
    this.originalXmlToString = function() {
        return this.utils.xmlToString(this.$originalXml.get(0));
    };
    this.removeSubtitle = function(indexOrElement) {
        /*
         * Given the zero-index of the subtitle to be removed,
         * remove it from the node tree.
         *
         * Returns: true
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        $subtitle.remove();

        return true;
    };
    this.removeSubtitles = function() {
        /*
         * Remove all subtitles from the working set.
         *
         * Returns: true
         */

        this.getSubtitles().remove();

        return true;
    };
    this.resetSubtitles = function() {
        /*
         * For each subtitle, if the content is empty, delete the subtitle.
         * Otherwise, just reset the text and the start/end times.
         *
         * Returns: true
         */

        var $subtitles = this.getSubtitles();

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

            var $subtitle = $subtitles.eq(i);
            var content = $('
').append($subtitle.contents().clone()).remove().html();

            if (content === '') {
                $subtitle.remove();
            } else {
                $subtitle.text('').attr({
                    'begin': '',
                    'end': ''
                });
            }
        }
    };
    this.startOfParagraph = function(indexOrElement, startOfParagraph) {
        /*
         * Either get or set the startofparagraph attr for the subtitle.
         *
         * Returns: current state of startofparagraph (boolean)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof startOfParagraph !== 'undefined') {
            $subtitle.attr('startofparagraph', startOfParagraph);
        }

        // We return a string for 'false' because this is an attr on the actual
        // XML node. It'll always be a string.
        return $subtitle.attr('startofparagraph') || 'false';
    };
    this.startTime = function(indexOrElement, startTime) {
        /*
         * Either get or set the start time for the subtitle.
         *
         * Returns: current start time (string)
         */

        var $subtitle = this.getSubtitle(indexOrElement);

        if (typeof startTime !== 'undefined') {
            if (parseFloat(startTime)) {
                $subtitle.attr('begin', startTime);
            } else {
                $subtitle.attr('begin', '');
            }
        }

        return $subtitle.attr('begin');
    };
    this.subtitlesCount = function() {
        /*
         * Retrieve the current number of subtitles.
         *
         * Returns: integer
         */

        return this.getSubtitles().length;
    };
    this.xmlToString = function() {
        return this.utils.xmlToString(this.$xml.get(0));
    };
};
<|endoftext|>


<commit_before>/* client side javascript functions */
var socket = io();

/* on document ready */
$(document).ready(function() {
  // fill all fields from database
  getParams();

  // listen for checkbox changes and POST
  $('#tabs :input[type="checkbox"]').change(function () {
    if(this.checked) {
      var param = JSON.stringify({name: $(this).attr('id'), value: "1"})
    } else {
      var param = JSON.stringify({name: $(this).attr('id'), value: "0"})
    }
    // POST data, GET parameters as callback
    $.post("/params/", param, getParams)
  });

  // listen for form field changes and POST
  $('#tabs :input[type="number"]').change(function () {
    if ($(this).attr('id') == 'PGAIN' | $(this).attr('id') == 'IGAIN' | $(this).attr('id') == 'DGAIN' | $(this).attr('id') == 'PGAIN2') {
      var param = JSON.stringify({name: $(this).attr('id'), value: ($(this).val()*32768).toFixed(0)})
    } else {
      var param = JSON.stringify({name: $(this).attr('id'), value: $(this).val()})
    }
    // POST data, GET parameters as callback
    $.post("/params/", param, getParams)
  });

  // listen for dropdown changes and POST
  $('select').change(function() {
    var param = JSON.stringify({name: $(this).attr('id'), value: $(this).val()})
    // POST data, GET parameters as callback
    $.post("/control/mode/", param, getParams)
  });
});

/* Plot data with flot */
socket.on('data', function (data) {
  $.plot("#oscilloscope", [data.data], {
    series: {
      shadowSize: 0     // Drawing is faster without shadows
    },
    grid: {
      clickable: true	// Allow mouse click to determine lock point
    }
  });
});

/* handle oscilloscope mouse click */
$(function() {
  $("#oscilloscope").bind("plotclick", function (event, pos, item) {
    document.getElementById('XLOCK').value = pos.x.toFixed(0);
    $('#XLOCK').trigger('change')
    document.getElementById('YLOCK').value = pos.y.toFixed(0);
    $('#YLOCK').trigger('change')
  });
});

/* only to test for now */
function openClose()  {
  if($('#OPENCLOSE').attr("value") == "1"){
    // toggle to closed loop
    var param = JSON.stringify({name: "OPENCLOSE", value: "0"})
  } else {
    // toggle to open loop
    var param = JSON.stringify({name: "OPENCLOSE", value: "1"})
  }
  $.post("/params/", param, getParams)
}

/* GET parameters and insert into form */
function getParams() {
  $.get("/params/", function(data) {
    for (var obj in data) {
      try {
        // insert values into form
	var element = document.getElementById(data[obj].name)
	if(element.type == 'checkbox') {
	  element.checked = data[obj].value	// checkbox
	} else if (data[obj].name == 'PGAIN' | data[obj].name == 'IGAIN' | data[obj].name == 'DGAIN' | data[obj].name == 'PGAIN2'){
      	  element.value = (data[obj].value/16384).toFixed(4)/2	// represent number in decimal notation
	} else if (data[obj].name == 'OPENCLOSE'){
	  if(data[obj].value){
	    $('#OPENCLOSE').text('OPEN LOOP').addClass("btn-success").removeClass("btn-danger").val("1");
	  } else {
	    $('#OPENCLOSE').text('CLOSED LOOP').addClass("btn-danger").removeClass("btn-success").val("0");
	  }
	} else {
      	  element.value = data[obj].value	// number field
	}
      } catch (err) {
        // getElementById(...) is null
      }
    }
  });
}
<commit_msg>only update xlock and ylock depending on oscilloscope mode<commit_after>/* client side javascript functions */
var socket = io();

/* on document ready */
$(document).ready(function() {
  // fill all fields from database
  getParams();

  // listen for checkbox changes and POST
  $('#tabs :input[type="checkbox"]').change(function () {
    if(this.checked) {
      var param = JSON.stringify({name: $(this).attr('id'), value: "1"})
    } else {
      var param = JSON.stringify({name: $(this).attr('id'), value: "0"})
    }
    // POST data, GET parameters as callback
    $.post("/params/", param, getParams)
  });

  // listen for form field changes and POST
  $('#tabs :input[type="number"]').change(function () {
    if ($(this).attr('id') == 'PGAIN' | $(this).attr('id') == 'IGAIN' | $(this).attr('id') == 'DGAIN' | $(this).attr('id') == 'PGAIN2') {
      var param = JSON.stringify({name: $(this).attr('id'), value: ($(this).val()*32768).toFixed(0)})
    } else {
      var param = JSON.stringify({name: $(this).attr('id'), value: $(this).val()})
    }
    // POST data, GET parameters as callback
    $.post("/params/", param, getParams)
  });

  // listen for dropdown changes and POST
  $('select').change(function() {
    var param = JSON.stringify({name: $(this).attr('id'), value: $(this).val()})
    // POST data, GET parameters as callback
    $.post("/control/mode/", param, getParams)
  });
});

/* Plot data with flot */
socket.on('data', function (data) {
  $.plot("#oscilloscope", [data.data], {
    series: {
      shadowSize: 0     // Drawing is faster without shadows
    },
    grid: {
      clickable: true	// Allow mouse click to determine lock point
    }
  });
});

/* handle oscilloscope mouse click */
$(function() {
  $("#oscilloscope").bind("plotclick", function (event, pos, item) {
    if($('#MODE').val() == "2"){
      // only update XLOCK when X-Y mode
      document.getElementById('XLOCK').value = pos.x.toFixed(0);
      $('#XLOCK').trigger('change')
    }
    if($('#MODE').val() == "1" | $('#MODE').val() == "2"){
      // only update YLOCK on TIME-Y or X-Y mode
      document.getElementById('YLOCK').value = pos.y.toFixed(0);
      $('#YLOCK').trigger('change')
    }
  });
});

/* only to test for now */
function openClose()  {
  if($('#OPENCLOSE').attr("value") == "1"){
    // toggle to closed loop
    var param = JSON.stringify({name: "OPENCLOSE", value: "0"})
  } else {
    // toggle to open loop
    var param = JSON.stringify({name: "OPENCLOSE", value: "1"})
  }
  $.post("/params/", param, getParams)
}

/* GET parameters and insert into form */
function getParams() {
  $.get("/params/", function(data) {
    for (var obj in data) {
      try {
        // insert values into form
	var element = document.getElementById(data[obj].name)
	if(element.type == 'checkbox') {
	  element.checked = data[obj].value	// checkbox
	} else if (data[obj].name == 'PGAIN' | data[obj].name == 'IGAIN' | data[obj].name == 'DGAIN' | data[obj].name == 'PGAIN2'){
      	  element.value = (data[obj].value/16384).toFixed(4)/2	// represent number in decimal notation
	} else if (data[obj].name == 'OPENCLOSE'){
	  if(data[obj].value){
	    $('#OPENCLOSE').text('OPEN LOOP').addClass("btn-success").removeClass("btn-danger").val("1");
	  } else {
	    $('#OPENCLOSE').text('CLOSED LOOP').addClass("btn-danger").removeClass("btn-success").val("0");
	  }
	} else {
      	  element.value = data[obj].value	// number field
	}
      } catch (err) {
        // getElementById(...) is null
      }
    }
  });
}
<|endoftext|>


<commit_before>var npm = require("npm/lib/npm.js");
var readInstalled = require("npm/lib/utils/read-installed.js");
var relativize = require("npm/lib/utils/relativize.js");
var readJson = require("npm/lib/utils/read-json.js");
var path = require("path");
var async = require("async");
var fs = require("fs");
var tsort = require("./tsort");

exports.prefix = 'pluginomatic_';
exports.loaded = false;
exports.plugins = {};
exports.parts = [];
exports.hooks = {};

exports.ensure = function (cb) {
  if (!exports.loaded)
    exports.update(cb);
  else
    cb();
}

exports.update = function (cb) {
  exports.getPlugins(function (er, plugins, parts, hooks) {
    exports.plugins = plugins;
    exports.parts = parts;
    exports.hooks = hooks;
    exports.loaded = true;
    cb(er);
  });
}

exports.formatPlugins = function () {
  return Object.keys(exports.plugins).join(", ");
}

exports.formatParts = function () {
  return exports.parts.map(function (part) { return part.full_name; }).join("\n");
}

exports.formatHooks = function () {
  var res = [];
  Object.keys(exports.hooks).forEach(function (hook_name) {
    exports.hooks[hook_name].forEach(function (hook) {
      res.push(hook.hook_name + ": " + hook.hook_fn_name + " from " + hook.part.full_name);
    });
  });
  return res.join("\n");
}

exports.getPlugins = function (cb) {
  exports.getPackages(function (er, packages) {
    packages.__builtin__ = {
      "path": path.resolve(npm.dir, "../..")
    };

    var parts = {};
    var plugins = {};
    // Load plugin metadata pluginomatic.json
    async.forEach(
      Object.keys(packages),
      function (plugin_name, cb) {
        exports.loadPlugin(packages, plugin_name, plugins, parts, cb);
      },
      function (err) {
        parts = exports.sortParts(parts);
        var hooks = exports.extractHooks(parts);
        cb(err, plugins, parts, hooks);
      }
    );
  });
}

exports.getPackages = function (cb) {
  // Load list of installed NPM packages, flatten it to a list, and filter out only packages with names that
  // ../.. and not just .. because current dir is like ETHERPAD_ROOT/node/node_modules (!!!!)
  var dir = path.resolve(npm.dir, "../..")
  readInstalled(dir, function (er, data) {
    if (er) cb(er, null);
    var packages = {};
    function flatten(deps) {
      Object.keys(deps).forEach(function (name) {
        if (name.indexOf(exports.prefix) == 0) {
          packages[name] = deps[name];
	}
	if (deps[name].dependencies !== undefined)
	  flatten(deps[name].dependencies);
      });
    }
    flatten([data]);
    cb(null, packages);
  });
}

exports.extractHooks = function (parts) {
  var hooks = {};
  parts.forEach(function (part) {
    Object.keys(part.hooks || {}).forEach(function (hook_name) {
      if (hooks[hook_name] === undefined) hooks[hook_name] = [];
	var hook_fn_name = part.hooks[hook_name];
      var hook_fn = exports.loadFn(part.hooks[hook_name]);
      if (hook_fn) {
        hooks[hook_name].push({"hook_name": hook_name, "hook_fn": hook_fn, "hook_fn_name": hook_fn_name, "part": part});
      } else {
	console.error("Unable to load hook function for " + part.full_name + " for hook " + hook_name + ": " + part.hooks[hook_name]);
      }
    });
  });
  return hooks;
}

exports.loadPlugin = function (packages, plugin_name, plugins, parts, cb) {
  var plugin_path = path.resolve(packages[plugin_name].path, "pluginomatic.json");
  fs.readFile(
    plugin_path,
    function (er, data) {
      var plugin = JSON.parse(data);
      plugin.package = packages[plugin_name];
      plugins[plugin_name] = plugin;
      plugin.parts.forEach(function (part) {
	part.plugin = plugin;
	part.full_name = plugin_name + "/" + part.name;
	parts[part.full_name] = part;
      });
      cb();
    }
  );
}

exports.partsToParentChildList = function (parts) {
  var res = [];
  Object.keys(parts).forEach(function (name) {
    (parts[name].post || []).forEach(function (child_name)  {
      res.push([name, child_name]);
    });
    (parts[name].pre || []).forEach(function (parent_name)  {
      res.push([parent_name, name]);
    });
    if (!parts[name].pre && !parts[name].post) {
      res.push([name, ":" + name]); // Include apps with no dependency info
    }
  });
  return res;
}

exports.sortParts = function(parts) {
  return tsort(
    exports.partsToParentChildList(parts)
  ).filter(
    function (name) { return parts[name] !== undefined; }
  ).map(
    function (name) { return parts[name]; }
  );
};

exports.loadFn = function (path) {
  var x = path.split(":");
  var fn = require(x[0]);
  x[1].split(".").forEach(function (name) {
    fn = fn[name];
  });
  return fn;
}
<commit_msg>Make plugins dict non-recursive<commit_after>var npm = require("npm/lib/npm.js");
var readInstalled = require("npm/lib/utils/read-installed.js");
var relativize = require("npm/lib/utils/relativize.js");
var readJson = require("npm/lib/utils/read-json.js");
var path = require("path");
var async = require("async");
var fs = require("fs");
var tsort = require("./tsort");
var util = require("util");

exports.prefix = 'pluginomatic_';
exports.loaded = false;
exports.plugins = {};
exports.parts = [];
exports.hooks = {};

exports.ensure = function (cb) {
  if (!exports.loaded)
    exports.update(cb);
  else
    cb();
}

exports.update = function (cb) {
  exports.getPlugins(function (er, plugins, parts, hooks) {
    exports.plugins = plugins;
    exports.parts = parts;
    exports.hooks = hooks;
    exports.loaded = true;
    cb(er);
  });
}

exports.formatPlugins = function () {
  return Object.keys(exports.plugins).join(", ");
}

exports.formatParts = function () {
  return exports.parts.map(function (part) { return part.full_name; }).join("\n");
}

exports.formatHooks = function () {
  var res = [];
  Object.keys(exports.hooks).forEach(function (hook_name) {
    exports.hooks[hook_name].forEach(function (hook) {
      res.push(hook.hook_name + ": " + hook.hook_fn_name + " from " + hook.part.full_name);
    });
  });
  return res.join("\n");
}

exports.getPlugins = function (cb) {
  exports.getPackages(function (er, packages) {
    packages.__builtin__ = {
      "path": path.resolve(npm.dir, "../..")
    };

    var parts = {};
    var plugins = {};
    // Load plugin metadata pluginomatic.json
    async.forEach(
      Object.keys(packages),
      function (plugin_name, cb) {
        exports.loadPlugin(packages, plugin_name, plugins, parts, cb);
      },
      function (err) {
        parts = exports.sortParts(parts);
        var hooks = exports.extractHooks(parts);
//        console.log(util.inspect(plugins, undefined, null));
        cb(err, plugins, parts, hooks);
      }
    );
  });
}

exports.getPackages = function (cb) {
  // Load list of installed NPM packages, flatten it to a list, and filter out only packages with names that
  // ../.. and not just .. because current dir is like ETHERPAD_ROOT/node/node_modules (!!!!)
  var dir = path.resolve(npm.dir, "../..")
  readInstalled(dir, function (er, data) {
    if (er) cb(er, null);
    var packages = {};
    function flatten(deps) {
      Object.keys(deps).forEach(function (name) {
        if (name.indexOf(exports.prefix) == 0) {
          packages[name] = deps[name];
	}
	if (deps[name].dependencies !== undefined)
	  flatten(deps[name].dependencies);
      });
    }
    flatten([data]);
    cb(null, packages);
  });
}

exports.extractHooks = function (parts) {
  var hooks = {};
  parts.forEach(function (part) {
    Object.keys(part.hooks || {}).forEach(function (hook_name) {
      if (hooks[hook_name] === undefined) hooks[hook_name] = [];
	var hook_fn_name = part.hooks[hook_name];
      var hook_fn = exports.loadFn(part.hooks[hook_name]);
      if (hook_fn) {
        hooks[hook_name].push({"hook_name": hook_name, "hook_fn": hook_fn, "hook_fn_name": hook_fn_name, "part": part});
      } else {
	console.error("Unable to load hook function for " + part.full_name + " for hook " + hook_name + ": " + part.hooks[hook_name]);
      }
    });
  });
  return hooks;
}

exports.loadPlugin = function (packages, plugin_name, plugins, parts, cb) {
  var plugin_path = path.resolve(packages[plugin_name].path, "pluginomatic.json");
  fs.readFile(
    plugin_path,
    function (er, data) {
      var plugin = JSON.parse(data);
      plugin.package = packages[plugin_name];
      plugins[plugin_name] = plugin;
      plugin.parts.forEach(function (part) {
	part.plugin = plugin_name;
	part.full_name = plugin_name + "/" + part.name;
	parts[part.full_name] = part;
      });
      cb();
    }
  );
}

exports.partsToParentChildList = function (parts) {
  var res = [];
  Object.keys(parts).forEach(function (name) {
    (parts[name].post || []).forEach(function (child_name)  {
      res.push([name, child_name]);
    });
    (parts[name].pre || []).forEach(function (parent_name)  {
      res.push([parent_name, name]);
    });
    if (!parts[name].pre && !parts[name].post) {
      res.push([name, ":" + name]); // Include apps with no dependency info
    }
  });
  return res;
}

exports.sortParts = function(parts) {
  return tsort(
    exports.partsToParentChildList(parts)
  ).filter(
    function (name) { return parts[name] !== undefined; }
  ).map(
    function (name) { return parts[name]; }
  );
};

exports.loadFn = function (path) {
  var x = path.split(":");
  var fn = require(x[0]);
  x[1].split(".").forEach(function (name) {
    fn = fn[name];
  });
  return fn;
}
<|endoftext|>


<commit_before>// To support using nodejs modules that do module.exports = ...
var module = {};

var Util = {
Text: (function Text(){
	var http_matcher = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
	var www_matcher = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
	var mail_to_matcher = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;
	var my_name = (window.Me && Me.name && Me.name.toLowerCase()) || '';
	var filter = (function(){
		var f = window.UiOptions && UiOptions.wordFilter;
		return f && new RegExp(f.join('|'), 'gi');
	})();

	return {
		newlineMatcher: /\n|\r\n/gim,

		linkify: function(text){
			text = text.replace(http_matcher, '$1');
			text = text.replace(www_matcher, '$1$2');
			text = text.replace(mail_to_matcher, '$1');
			return text
		},

		linkifyTest: function(text){
			return http_matcher.test(text) ||
			       www_matcher.test(text) ||
			       mail_to_matcher.test(text);
		},

		mentionMatcher: function(text){
			var matches = text.match(/@([^\s]+)/g);
			return matches && matches.some(function(match){
				match = match.substr(1);
				return match.length >= 2 && my_name.indexOf(match.toLowerCase()) >= 0;
			});
		},

		wordFilter: function(text){
			if(!filter) return text;
			return text.replace(filter, function(m) {
				return Array(m.length+1).join("*");
			});
		}
	};
})(),

nextTick: function(f){
	return setTimeout(f,0);
},

ts:function(){
	return new Date().getTime();
},

time:function time(t){
	var H = t.getHours(),
			h = H % 12,
			m = t.getMinutes();

	return (h==0 ? 12 : h) + ":" + (m < 10 ? '0' + m : m) + (H > 11 ? 'pm' : 'am');
},

date:function date(t){
	var d = t.getDate(),
	    m = t.getMonth() + 1;

	return m + "/" + (d < 10 ? '0' + d : d);
},

url:function(){
	return window.location.href.split('?')[0];
},

inherits:function(to, from){
	$.extend(to.prototype, from.prototype, {_super: from});
}
};

if(window.EventEmitter){
	EventEmitter.prototype.muteEvents = function(fn){
		this.emit = function(){}
		fn();
		delete this.emit;
	}
}
<commit_msg>Notify all the users with certain keywords<commit_after>// To support using nodejs modules that do module.exports = ...
var module = {};

var Util = {
Text: (function Text(){
	var http_matcher = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
	var www_matcher = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
	var mail_to_matcher = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;
	var my_name = (window.Me && Me.name && Me.name.toLowerCase()) || '';
	var filter = (function(){
		var f = window.UiOptions && UiOptions.wordFilter;
		return f && new RegExp(f.join('|'), 'gi');
	})();

	return {
		newlineMatcher: /\n|\r\n/gim,

		linkify: function(text){
			text = text.replace(http_matcher, '$1');
			text = text.replace(www_matcher, '$1$2');
			text = text.replace(mail_to_matcher, '$1');
			return text
		},

		linkifyTest: function(text){
			return http_matcher.test(text) ||
			       www_matcher.test(text) ||
			       mail_to_matcher.test(text);
		},

		mentionMatcher: function(text){
			var matches = text.match(/@([^\s]+)/g);
			return matches && matches.some(function(match){
				match = match.substr(1);
            // does it match @<name> or @all or @everyone or @everybody?
            matches_name_or_all =
             (my_name.indexOf(match.toLowerCase()) >= 0 ||
             "all".indexOf(match.toLowerCase()) >= 0 ||
             "everybody".indexOf(match.toLowerCase()) >= 0 ||
             "everyone".indexOf(match.toLowerCase()) >= 0);
				return match.length >= 2 && matches_name_or_all;
			});
		},

		wordFilter: function(text){
			if(!filter) return text;
			return text.replace(filter, function(m) {
				return Array(m.length+1).join("*");
			});
		}
	};
})(),

nextTick: function(f){
	return setTimeout(f,0);
},

ts:function(){
	return new Date().getTime();
},

time:function time(t){
	var H = t.getHours(),
			h = H % 12,
			m = t.getMinutes();

	return (h==0 ? 12 : h) + ":" + (m < 10 ? '0' + m : m) + (H > 11 ? 'pm' : 'am');
},

date:function date(t){
	var d = t.getDate(),
	    m = t.getMonth() + 1;

	return m + "/" + (d < 10 ? '0' + d : d);
},

url:function(){
	return window.location.href.split('?')[0];
},

inherits:function(to, from){
	$.extend(to.prototype, from.prototype, {_super: from});
}
};

if(window.EventEmitter){
	EventEmitter.prototype.muteEvents = function(fn){
		this.emit = function(){}
		fn();
		delete this.emit;
	}
}
<|endoftext|>


<commit_before>// To support using nodejs modules that do module.exports = ...
var module = {};

var Util = {
Text: (function Text(){
	var http_matcher = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
	var www_matcher = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
	var mail_to_matcher = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;
   var image_suffix_matcher = /.(gif|jpg|jpeg|png|tiff|tif|svg)/gim;
	var my_name = (window.Me && Me.name && Me.name.toLowerCase()) || '';
	var filter = (function(){
		var f = window.UiOptions && UiOptions.wordFilter;
		return f && new RegExp(f.join('|'), 'gi');
	})();

	return {
		newlineMatcher: /\n|\r\n/gim,

		linkify: function(text){
         var tmp = this.imageDisplay(text);
         if (text != tmp) {
            return tmp;
         }
			text = text.replace(http_matcher, '$1');
			text = text.replace(www_matcher, '$1$2');
			text = text.replace(mail_to_matcher, '$1');
			return text
		},

		linkifyTest: function(text){
			return this.httpMatch(text) ||
			       this.wwwMatch(text) ||
			       this.mailtoMatch(text);
		},

      // we need these four match methods because regex.test() is buggy when
      // you reuse a regex object. other regex functions are fine though...
      httpMatch: function(text) {
         return (/(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim).test(text);
      },

      wwwMatch: function(text) {
         return (/(^|[^\/])(www\.[\S]+(\b|$))/gim).test(text);
      },

      mailtoMatch: function(text) {
         return (/(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim).test(text);
      },

      imageSuffixMatch: function(text) {
         return (/.(gif|jpg|jpeg|png|tiff|tif|svg)/gim).test(text);
      },

		mentionMatcher: function(text){
			var matches = text.match(/@([^\s]+)/g);
			return matches && matches.some(function(match){
				match = match.substr(1);
				return match.length >= 2 && my_name.indexOf(match.toLowerCase()) >= 0;
			});
		},

      imageDisplay: function(text){
         if (this.httpMatch(text) || this.wwwMatch(text)) {
            if (this.wwwMatch(text))
               text = 'http://' + text;

            if (this.imageSuffixMatch(text)) {
               text = text.replace(http_matcher, ' ');
            }
         }
         return text;
      },

		wordFilter: function(text){
			if(!filter) return text;
			return text.replace(filter, function(m) {
				return Array(m.length+1).join("*");
			});
		}
	};
})(),

nextTick: function(f){
	return setTimeout(f,0);
},

ts:function(){
	return new Date().getTime();
},

time:function time(t){
	var H = t.getHours(),
			h = H % 12,
			m = t.getMinutes();

	return (h==0 ? 12 : h) + ":" + (m < 10 ? '0' + m : m) + (H > 11 ? 'pm' : 'am');
},

date:function date(t){
	var d = t.getDate(),
	    m = t.getMonth() + 1;

	return m + "/" + (d < 10 ? '0' + d : d);
},

url:function(){
	return window.location.href.split('?')[0];
},

inherits:function(to, from){
	$.extend(to.prototype, from.prototype, {_super: from});
}
};

if(window.EventEmitter){
	EventEmitter.prototype.muteEvents = function(fn){
		this.emit = function(){}
		fn();
		delete this.emit;
	}
}
<commit_msg>Added iFixit image suffixes<commit_after>// To support using nodejs modules that do module.exports = ...
var module = {};

var Util = {
Text: (function Text(){
	var http_matcher = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
	var www_matcher = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
	var mail_to_matcher = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;
   var image_suffix_matcher = /.(gif|jpg|jpeg|png|tiff|tif|svg|mini|thumbnail|standard|medium|large|huge)/gim;
	var my_name = (window.Me && Me.name && Me.name.toLowerCase()) || '';
	var filter = (function(){
		var f = window.UiOptions && UiOptions.wordFilter;
		return f && new RegExp(f.join('|'), 'gi');
	})();

	return {
		newlineMatcher: /\n|\r\n/gim,

		linkify: function(text){
         var tmp = this.imageDisplay(text);
         if (text != tmp) {
            return tmp;
         }
			text = text.replace(http_matcher, '$1');
			text = text.replace(www_matcher, '$1$2');
			text = text.replace(mail_to_matcher, '$1');
			return text
		},

		linkifyTest: function(text){
			return this.httpMatch(text) ||
			       this.wwwMatch(text) ||
			       this.mailtoMatch(text);
		},

      // we need these four match methods because regex.test() is buggy when
      // you reuse a regex object. other regex functions are fine though...
      httpMatch: function(text) {
         return (/(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim).test(text);
      },

      wwwMatch: function(text) {
         return (/(^|[^\/])(www\.[\S]+(\b|$))/gim).test(text);
      },

      mailtoMatch: function(text) {
         return (/(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim).test(text);
      },

      imageSuffixMatch: function(text) {
         return (/.(gif|jpg|jpeg|png|tiff|tif|svg)/gim).test(text);
      },

		mentionMatcher: function(text){
			var matches = text.match(/@([^\s]+)/g);
			return matches && matches.some(function(match){
				match = match.substr(1);
				return match.length >= 2 && my_name.indexOf(match.toLowerCase()) >= 0;
			});
		},

      imageDisplay: function(text){
         if (this.httpMatch(text) || this.wwwMatch(text)) {
            if (this.wwwMatch(text))
               text = 'http://' + text;

            if (this.imageSuffixMatch(text)) {
               text = text.replace(http_matcher, ' ');
            }
         }
         return text;
      },

		wordFilter: function(text){
			if(!filter) return text;
			return text.replace(filter, function(m) {
				return Array(m.length+1).join("*");
			});
		}
	};
})(),

nextTick: function(f){
	return setTimeout(f,0);
},

ts:function(){
	return new Date().getTime();
},

time:function time(t){
	var H = t.getHours(),
			h = H % 12,
			m = t.getMinutes();

	return (h==0 ? 12 : h) + ":" + (m < 10 ? '0' + m : m) + (H > 11 ? 'pm' : 'am');
},

date:function date(t){
	var d = t.getDate(),
	    m = t.getMonth() + 1;

	return m + "/" + (d < 10 ? '0' + d : d);
},

url:function(){
	return window.location.href.split('?')[0];
},

inherits:function(to, from){
	$.extend(to.prototype, from.prototype, {_super: from});
}
};

if(window.EventEmitter){
	EventEmitter.prototype.muteEvents = function(fn){
		this.emit = function(){}
		fn();
		delete this.emit;
	}
}
<|endoftext|>


<commit_before>// System imports
var FS               = require('fs'),
    PATH             = require('path');

// Local imports
var LOGGER           = require(__dirname + '/../utils/logger.js'),
    SANITY_TESTS     = require(__dirname + '/../utils/sanity.js'),
    TAGS_MANAGER     = require(__dirname + '/tagsManager.js'),
    UTILS            = require(__dirname + '/../utils/utils.js'),
    VALIDATION_UTILS = require(__dirname + '/../utils/validation.js');

/**
 * This is entry point of process
 *
 * It first parses the CLI arguments and
 * start finding the tags frequency
 *
 */
(function startProcess() {
    //
    // Read the command line parameters
    //
    var myArgs          = process.argv.slice(2),
        configPath      = __dirname + '/../conf/appConf.js'
        isIgnoreCase    = false;

    // If path to configuration is specified
    if (myArgs && myArgs.indexOf("-c") != -1) {
        configPath = myArgs[myArgs.indexOf("-c") + 1];
    }

    // If case-insensitive check flag added
    if (myArgs && myArgs.indexOf("-i") != -1) {
        isIgnoreCase = true;
    }

    // Get the tags
    getTags(myArgs, function(err, tags) {
        if (err) {
            console.error('Error while fetching the tags : ' + err);
            console.log('Usage: node source/driver.js -t <tag1>,<tag2>,...,<tagN> -c <path_to_configuration file>');

            // Let us exit
            process.exit(-1);
        } else {
            // We got the tags, Let us do the sanity checks

            // 1. Check if config file exists
            confValidator(configPath, function(err) {
                if (err) {
                    console.error('Error while validating the configuration file path : ' + err);

                    // Let us exit
                    process.exit(-1);
                } else {
                    var CONF = require(configPath);

                    var  logger = LOGGER.createLogger(CONF['LOGGER'], 'NODE_TAGS');

                    logger.debug({
                        msg  : 'Driver:Configuration used',
                        conf : CONF
                    });

                    // 2. Check if data directory exists
                    SANITY_TESTS.directoryExists(CONF['DATA_FILES_DIR'], function(err) {
                        if (err) {
                            self._log('error', {
                                msg     : 'Driver._init::Error occured while checking if data directory exists',
                                error   : err
                            });

                            // Let us exit
                            process.exit(-1);
                        } else {
                            var startProcess;

                            // Let us start tags processing
                            var tagsManager = new TAGS_MANAGER(CONF, {
                                logger          : logger,
                                tags            : tags,
                                isIgnoreCase    : isIgnoreCase
                            });

                            // Add start event listener
                            tagsManager.on('start', function() {
                                logger.debug({
                                    msg : 'Driver::Started tags processing'
                                });

                                startProcess = Date.now();
                            });

                            // Add end event listener
                            tagsManager.on('end',   function(data) {
                                logger.info({
                                    msg         : 'Driver::Finished tags processing',
                                    result      : UTILS.sortObject(data),       // Return the sorted results
                                    timeTakenMS : (Date.now() - startProcess)
                                });

                                // Let us exit the process
                                process.exit(0);
                            });

                            // Add error event listener
                            tagsManager.on('error',   function(err) {
                                logger.error({
                                    msg     : 'Driver::Error happened while processing tags',
                                    error   : err
                                });

                                // Let us exit the process
                                process.exit(-1);
                            });

                            tagsManager.processTags();
                        }
                    });
                }
            })
        }
    });
})();

/**
 *
 * Based on CLI argument it fetches the tags
 * If no tags have been specified then it uses the default tags
 *
 * @param  {[String]} : Array of CLI arguments
 * @param  {Function} : Callback method
 * @return {[string]} : Invoke the callback, either with error or Array of tags
 */
function getTags(args, cb) {
    var tags = null;

    // If tags are specified
    if (args && args.indexOf("-t") != -1)
        tags = args[args.indexOf("-t") + 1];

    // First ensure that tags field contains string
    if (VALIDATION_UTILS.isString(tags))
        tags = tags ? (tags || '').split(',') : []; // Since these are comma separated, hence split it on comma

    // Atleast one tag is be not null/empty
    if (VALIDATION_UTILS.isArray(tags) && tags.length > 0 &&
        VALIDATION_UTILS.containsNotNull(tags)) {
        if (cb) cb(null, tags);
    } else {
        // Read the default tags
        var tagsFile = PATH.join(__dirname, 'tags.txt');
        FS.readFile(tagsFile, 'utf8', function(err, data) {
            if (err) {
                if (cb) cb (err);
            } else {
                // Check the platform and then use line feed character
                tags = (data || '').split(UTILS.getNewLine());

                // If last element is empty string then remove it
                if (tags && VALIDATION_UTILS.isEmpty(tags[tags.length - 1]))
                    tags = tags.slice(0, tags.length - 1);

                if (cb) cb(null, tags);
            }
        });
    }
}

/**
 * Given the path of configuration file, it checks if file exists or not
 *
 * This is part of sanity check
 *
 * @param {String} : Path of configuration file
 * @param {Function} : Callback method which gets invoked with the error
 * if file does not exists at specified path
 */
function confValidator(path, cb) {
    var parsedPath = PATH.parse(path);
    if (parsedPath.dir !== __dirname + '/../conf')
        path = __dirname + '/../conf/' + parsedPath.base;

    SANITY_TESTS.fileExists(path, cb);
}

/**
 * uncaughtException event listener
 */
process.on('uncaughtException', function(e) {
    console.error({ msg:'THIS IS REALLY BAD::Encountered uncaught exception ', stack : e.stack});

    // Let us exit the process
    process.exit(1);
});<commit_msg>Updated result format as per requirement<commit_after>// System imports
var FS               = require('fs'),
    PATH             = require('path');

// Local imports
var LOGGER           = require(__dirname + '/../utils/logger.js'),
    SANITY_TESTS     = require(__dirname + '/../utils/sanity.js'),
    TAGS_MANAGER     = require(__dirname + '/tagsManager.js'),
    UTILS            = require(__dirname + '/../utils/utils.js'),
    VALIDATION_UTILS = require(__dirname + '/../utils/validation.js');

/**
 * This is entry point of process
 *
 * It first parses the CLI arguments and
 * start finding the tags frequency
 *
 */
(function startProcess() {
    //
    // Read the command line parameters
    //
    var myArgs          = process.argv.slice(2),
        configPath      = __dirname + '/../conf/appConf.js'
        isIgnoreCase    = false;

    // If path to configuration is specified
    if (myArgs && myArgs.indexOf("-c") != -1) {
        configPath = myArgs[myArgs.indexOf("-c") + 1];
    }

    // If case-insensitive check flag added
    if (myArgs && myArgs.indexOf("-i") != -1) {
        isIgnoreCase = true;
    }

    // Get the tags
    getTags(myArgs, function(err, tags) {
        if (err) {
            console.error('Error while fetching the tags : ' + err);
            console.log('Usage: node source/driver.js -t <tag1>,<tag2>,...,<tagN> -c <path_to_configuration file>');

            // Let us exit
            process.exit(-1);
        } else {
            // We got the tags, Let us do the sanity checks

            // 1. Check if config file exists
            confValidator(configPath, function(err) {
                if (err) {
                    console.error('Error while validating the configuration file path : ' + err);

                    // Let us exit
                    process.exit(-1);
                } else {
                    var CONF = require(configPath);

                    var  logger = LOGGER.createLogger(CONF['LOGGER'], 'NODE_TAGS');

                    logger.debug({
                        msg  : 'Driver:Configuration used',
                        conf : CONF
                    });

                    // 2. Check if data directory exists
                    SANITY_TESTS.directoryExists(CONF['DATA_FILES_DIR'], function(err) {
                        if (err) {
                            self._log('error', {
                                msg     : 'Driver._init::Error occured while checking if data directory exists',
                                error   : err
                            });

                            // Let us exit
                            process.exit(-1);
                        } else {
                            var startProcess;

                            // Let us start tags processing
                            var tagsManager = new TAGS_MANAGER(CONF, {
                                logger          : logger,
                                tags            : tags,
                                isIgnoreCase    : isIgnoreCase
                            });

                            // Add start event listener
                            tagsManager.on('start', function() {
                                logger.debug({
                                    msg : 'Driver::Started tags processing'
                                });

                                startProcess = Date.now();
                            });

                            // Add end event listener
                            tagsManager.on('end',   function(data) {
                                logger.debug({
                                    msg         : 'Driver::Finished tags processing',
                                    result      : UTILS.sortObject(data),       // Return the sorted results
                                    timeTakenMS : (Date.now() - startProcess)
                                });

                                var result = UTILS.sortObjectToArray(data);

                                // Let us print the result in a format which is expected
                                if (result && VALIDATION_UTILS.isArray(result)) {
                                    (result || []).forEach(function(datum) {
                                        console.log(datum[0] + '\t\t' + datum[1]);
                                    });
                                }

                                // Let us exit the process
                                process.exit(0);
                            });

                            // Add error event listener
                            tagsManager.on('error',   function(err) {
                                logger.error({
                                    msg     : 'Driver::Error happened while processing tags',
                                    error   : err
                                });

                                // Let us exit the process
                                process.exit(-1);
                            });

                            tagsManager.processTags();
                        }
                    });
                }
            })
        }
    });
})();

/**
 *
 * Based on CLI argument it fetches the tags
 * If no tags have been specified then it uses the default tags
 *
 * @param  {[String]} : Array of CLI arguments
 * @param  {Function} : Callback method
 * @return {[string]} : Invoke the callback, either with error or Array of tags
 */
function getTags(args, cb) {
    var tags = null;

    // If tags are specified
    if (args && args.indexOf("-t") != -1)
        tags = args[args.indexOf("-t") + 1];

    // First ensure that tags field contains string
    if (VALIDATION_UTILS.isString(tags))
        tags = tags ? (tags || '').split(',') : []; // Since these are comma separated, hence split it on comma

    // Atleast one tag is be not null/empty
    if (VALIDATION_UTILS.isArray(tags) && tags.length > 0 &&
        VALIDATION_UTILS.containsNotNull(tags)) {
        if (cb) cb(null, tags);
    } else {
        // Read the default tags
        var tagsFile = PATH.join(__dirname, 'tags.txt');
        FS.readFile(tagsFile, 'utf8', function(err, data) {
            if (err) {
                if (cb) cb (err);
            } else {
                // Check the platform and then use line feed character
                tags = (data || '').split(UTILS.getNewLine());

                // If last element is empty string then remove it
                if (tags && VALIDATION_UTILS.isEmpty(tags[tags.length - 1]))
                    tags = tags.slice(0, tags.length - 1);

                if (cb) cb(null, tags);
            }
        });
    }
}

/**
 * Given the path of configuration file, it checks if file exists or not
 *
 * This is part of sanity check
 *
 * @param {String} : Path of configuration file
 * @param {Function} : Callback method which gets invoked with the error
 * if file does not exists at specified path
 */
function confValidator(path, cb) {
    var parsedPath = PATH.parse(path);
    if (parsedPath.dir !== __dirname + '/../conf')
        path = __dirname + '/../conf/' + parsedPath.base;

    SANITY_TESTS.fileExists(path, cb);
}

/**
 * uncaughtException event listener
 */
process.on('uncaughtException', function(e) {
    console.error({ msg:'THIS IS REALLY BAD::Encountered uncaught exception ', stack : e.stack});

    // Let us exit the process
    process.exit(1);
});<|endoftext|>


<commit_before>//----------------------------------------------------------------------
//
// This source file is part of the furipota project.
//
// Licensed under MIT. See LICENCE for full licence information.
// See CONTRIBUTORS for the list of contributors to the project.
//
//----------------------------------------------------------------------

class Stream {
  constructor(producer, name) {
    this.producer = producer;
    this.listeners = [];
    this.running = false;
    this.name = name;
  }

  chain(transformation) {
    return new Stream(async (producer) => {
      let pending = [];
      const maybeClose = (x) => async () => {
        pending = pending.filter(a => a !== x);
        if (!pending.length) {
          await producer.close();
        }
      }

      this.subscribe({
        Value(x) {
          const stream = transformation(x);
          stream.subscribe({
            Value: producer.pushValue,
            Error: producer.pushError,
            Close: maybeClose(stream)
          });
          pending.push(stream);
          stream.run();
        },

        Error: producer.pushError,
        Close: maybeClose(this)
      });

      pending.push(this);
      await this.run();
    }, this.name + ' chain');
  }

  static of(value) {
    return new Stream(async (producer) => {
      await producer.pushValue(value);
      await producer.close();
    }, 'of');
  }

  static empty() {
    return new Stream(async (producer) => {
      await producer.close();
    }, 'empty');
  }

  map(transformation) {
    return this.chain(x => Stream.of(transformation(x)));
  }

  onError(listener) {
    this.listeners.push({
      Value(){ },
      Close(){ },
      Error(reason) {
        listener(reason);
      }
    });
  }

  subscribe(handler) {
    this.listeners.push(handler);
    return handler;
  }

  unsubscribe(handler) {
    this.listeners = this.listeners.filter(x => x !== handler);
    return handler;
  }

  run() {
    if (this.running) {
      return;
    }
    this.running = true;

    return new Promise((resolve, reject) => {
      const listeners = this.listeners;
      let closed = false;

      const broadcast = (operation, args) => {
        if (!closed) {
          return Promise.all(listeners.map(l => l[operation](...args)));
        } else {
          return Promise.resolve();
        }
      };

      const die = (error) => {
        closed = true;
        reject(error);
      };

      const operations = {
        async pushValue(value) {
          await broadcast('Value', [value]).catch(die);
        },

        async pushError(error) {
          await broadcast('Error', [error]).catch(die);
        },

        async close() {
          await broadcast('Close', []).catch(die);
          closed = true;
          resolve();
        }
      };

      this.producer(operations, this);
    });
  }
}

module.exports = Stream;
<commit_msg>Blocks when mapping in chain<commit_after>//----------------------------------------------------------------------
//
// This source file is part of the furipota project.
//
// Licensed under MIT. See LICENCE for full licence information.
// See CONTRIBUTORS for the list of contributors to the project.
//
//----------------------------------------------------------------------

class Stream {
  constructor(producer, name) {
    this.producer = producer;
    this.listeners = [];
    this.running = false;
    this.name = name;
  }

  chain(transformation) {
    return new Stream(async (producer) => {
      let pending = [];
      const maybeClose = (x) => async () => {
        pending = pending.filter(a => a !== x);
        if (!pending.length) {
          await producer.close();
        }
      }

      this.subscribe({
        async Value(x) {
          const stream = transformation(x);
          stream.subscribe({
            Value: producer.pushValue,
            Error: producer.pushError,
            Close: maybeClose(stream)
          });
          pending.push(stream);
          await stream.run();
        },

        Error: producer.pushError,
        Close: maybeClose(this)
      });

      pending.push(this);
      await this.run();
    }, this.name + ' chain');
  }

  static of(value) {
    return new Stream(async (producer) => {
      await producer.pushValue(value);
      await producer.close();
    }, 'of');
  }

  static empty() {
    return new Stream(async (producer) => {
      await producer.close();
    }, 'empty');
  }

  map(transformation) {
    return this.chain(x => Stream.of(transformation(x)));
  }

  onError(listener) {
    this.listeners.push({
      Value(){ },
      Close(){ },
      Error(reason) {
        listener(reason);
      }
    });
  }

  subscribe(handler) {
    this.listeners.push(handler);
    return handler;
  }

  unsubscribe(handler) {
    this.listeners = this.listeners.filter(x => x !== handler);
    return handler;
  }

  run() {
    if (this.running) {
      return;
    }
    this.running = true;

    return new Promise((resolve, reject) => {
      const listeners = this.listeners;
      let closed = false;

      const broadcast = (operation, args) => {
        if (!closed) {
          return Promise.all(listeners.map(l => l[operation](...args)));
        } else {
          return Promise.resolve();
        }
      };

      const die = (error) => {
        closed = true;
        reject(error);
      };

      const operations = {
        async pushValue(value) {
          await broadcast('Value', [value]).catch(die);
        },

        async pushError(error) {
          await broadcast('Error', [error]).catch(die);
        },

        async close() {
          await broadcast('Close', []).catch(die);
          closed = true;
          resolve();
        }
      };

      this.producer(operations, this);
    });
  }
}

module.exports = Stream;
<|endoftext|>


<commit_before>describe("Unit", function() {
  beforeEach(function() {
    var location =
    var unit = new Unit(location)
  })
  describe("move", function() {
    it("moves a unit from one location to target location", function() {
      Unit.move([[1,1], [1,0], [0,0]]
      expect(unit.location.coordinates).toBe([0,0]))
    })
  })
})
<commit_msg>Troubleshoot spec<commit_after>describe("Unit", function() {

  beforeEach(function() {
    var location = new Location(1,1)
    var unit = new Unit(location)
  });

  describe("move", function() {
    it("moves a unit from one location to target location", function() {
      Unit.move([[1,0], [0,0]];
      expect(unit.location.coordinates).toBe([0,0]));
    });
  });
});
<|endoftext|>


<commit_before>define(['app', 'buttons', 'consts', 'components', 'sinon'], function(app, buttons, consts, components, sinon) {
    describe('Test app', function() {
        'use strict';

        beforeEach(function() {
            this.server = sinon.fakeServer.create();
            this.body = consts.DOC.body;
            this.gameMenuClass = new components.GameMenu();
            this.startButtonsClass = new components.StartButtons();
            this.gameContainerClass = new components.GameContainer();
            this.panelGamerClass = new components.PanelGamer();

            // create container
            var container = consts.DOC.createElement('div');
            container.id = 'parentContainer';

            // append to body
            this.body.appendChild(container);
        });

        afterEach(function() {
            this.server.restore();
        });

        // test init method
        it('Test app.init method is called', function() {
            spyOn(app, 'init');

            app.init();
            expect(app.init).toBeDefined();
            expect(app.init).toHaveBeenCalled();
            expect(app.init.calls.count()).toBe(1);
        });

        // test Class GameMenu
        it('Test GameMenu Class [appendMenu] method', function() {
            var container = consts.DOC.getElementById('parentContainer');

            this.gameMenuClass.appendMenu(container);

            expect(container.childNodes.length).toBeGreaterThan(0);
            expect(container.childNodes[0].id).toBe('gameMenu');
        });
        it('Test Gamemenu Class [getGameMenu] method', function() {
            var getGameMenu = this.gameMenuClass.getGameMenu(),
                mockResponse = '

';

            expect(getGameMenu.tagName).toBe('DIV');
            expect(getGameMenu.outerHTML).toBe(mockResponse);
        });
        it('Test GameMenu Class [getParent] method', function() {
            var getParent = this.gameMenuClass.getParent(),
                mockResponse = '

';

            expect(getParent.tagName).toBe('DIV');
            expect(getParent.outerHTML).toBe(mockResponse);
        });

        // test Class StartButtons
        it('Test StartButtons Class [appendStartButton, appendLoadButton, appendOptionsButton, appendExitButton] methods', function() {
            var gameContainer = consts.DOC.getElementById('gameMenu');

            this.startButtonsClass.appendStartButton(gameContainer);
            expect(gameContainer.childNodes.length).toBeGreaterThan(0);
            expect(gameContainer.childNodes[0].id).toBe('startButton');

            this.startButtonsClass.appendLoadButton(gameContainer);
            expect(gameContainer.childNodes.length).toBeGreaterThan(1);
            expect(gameContainer.childNodes[1].id).toBe('loadButton');

            this.startButtonsClass.appendOptionsButton(gameContainer);
            expect(gameContainer.childNodes.length).toBeGreaterThan(2);
            expect(gameContainer.childNodes[2].id).toBe('optionsButton');

            this.startButtonsClass.appendExitButton(gameContainer);
            expect(gameContainer.childNodes.length).toBeGreaterThan(3);
            expect(gameContainer.childNodes[3].id).toBe('exitButton');
        });

        // test Class gameContainer
        it('Test GameContainer Class [getGameContainer] method', function() {
            var getGameContainer = this.gameContainerClass.getGameContainer(),
                mockResponse = '

';

            expect(getGameContainer.tagName).toBe('DIV');
            expect(getGameContainer.outerHTML).toBe(mockResponse);
        });
        it('Test GameContainer Class [appendGameContainer] method', function() {
            var container = consts.DOC.getElementById('parentContainer');

            this.gameContainerClass.appendGameContainer(container);

            expect(container.childNodes.length).toBeGreaterThan(0);
            expect(container.childNodes[0].id).toBe('gameMenu');
        });

        // test Class panelGamer
        it('Test PanelGamer Class [getPanelGamer] method', function() {
            var panelGamer = this.panelGamerClass.getPanelGamer(),
                mockResponseId = 'panelGamer';

            expect(panelGamer.tagName).toBe('DIV');
            expect(panelGamer.id).toBe(mockResponseId);
        });
        it('Test panelGamer Class [appendPanelGamer] method', function() {
            var container = consts.DOC.getElementById('gameContainer');

            this.panelGamerClass.appendPanelGamer(container);

            expect(container.childNodes.length).toBeGreaterThan(0);
            expect(container.childNodes[0].id).toBe('panelGamer');
        });

        // test createTable method
        it('Test createTable method', function() {
            var panelGamer = this.panelGamerClass.getPanelGamer(),
                table = components.createTable(panelGamer);

            panelGamer.appendChild(table);

            expect(panelGamer.childNodes.length).toBeGreaterThan(0);
            expect(panelGamer.childNodes[1].tagName).toBe('TABLE');
        });
    });
});
<commit_msg>Improved testApp<commit_after>define(['$', 'app', 'buttons', 'consts', 'components', 'sinon'], function($, app, buttons, consts, components, sinon) {
    describe('Test app', function() {
        'use strict';

        beforeEach(function() {
            this.server = sinon.fakeServer.create();
            this.body = consts.DOC.body;
            this.gameMenuClass = new components.GameMenu();
            this.startButtonsClass = new components.StartButtons();
            this.gameContainerClass = new components.GameContainer();
            this.panelGamerClass = new components.PanelGamer();

            // create container
            var container = consts.DOC.createElement('div');
            container.id = 'parentContainer';

            // append to body
            this.body.appendChild(container);
        });

        afterEach(function() {
            this.server.restore();
        });

        // test init method
        it('Test app.init method is called', function() {
            spyOn(app, 'init');

            app.init();
            expect(app.init).toBeDefined();
            expect(app.init).toHaveBeenCalled();
            expect(app.init.calls.count()).toBe(1);
        });

        // test Class GameMenu
        it('Test GameMenu Class [appendMenu] method', function() {
            var container = consts.DOC.getElementById('parentContainer');

            this.gameMenuClass.appendMenu(container);

            expect(container.childNodes.length).toBeGreaterThan(0);
            expect(container.childNodes[0].id).toBe('gameMenu');
        });
        it('Test Gamemenu Class [getGameMenu] method', function() {
            var getGameMenu = this.gameMenuClass.getGameMenu(),
                mockResponse = '

';

            expect(getGameMenu.tagName).toBe('DIV');
            expect(getGameMenu.outerHTML).toBe(mockResponse);
        });
        it('Test GameMenu Class [getParent] method', function() {
            var getParent = this.gameMenuClass.getParent(),
                mockResponse = '

';

            expect(getParent.tagName).toBe('DIV');
            expect(getParent.outerHTML).toBe(mockResponse);
        });

        // test Class StartButtons
        it('Test StartButtons Class [appendStartButton, appendLoadButton, appendOptionsButton, appendExitButton] methods', function() {
            var gameContainer = consts.DOC.getElementById('gameMenu');

            this.startButtonsClass.appendStartButton(gameContainer);
            expect(gameContainer.childNodes.length).toBeGreaterThan(0);
            expect(gameContainer.childNodes[0].id).toBe('startButton');

            this.startButtonsClass.appendLoadButton(gameContainer);
            expect(gameContainer.childNodes.length).toBeGreaterThan(1);
            expect(gameContainer.childNodes[1].id).toBe('loadButton');

            this.startButtonsClass.appendOptionsButton(gameContainer);
            expect(gameContainer.childNodes.length).toBeGreaterThan(2);
            expect(gameContainer.childNodes[2].id).toBe('optionsButton');

            this.startButtonsClass.appendExitButton(gameContainer);
            expect(gameContainer.childNodes.length).toBeGreaterThan(3);
            expect(gameContainer.childNodes[3].id).toBe('exitButton');
        });

        // test Class gameContainer
        it('Test GameContainer Class [getGameContainer] method', function() {
            var getGameContainer = this.gameContainerClass.getGameContainer(),
                mockResponse = '

';

            expect(getGameContainer.tagName).toBe('DIV');
            expect(getGameContainer.outerHTML).toBe(mockResponse);
        });
        it('Test GameContainer Class [appendGameContainer] method', function() {
            var container = consts.DOC.getElementById('parentContainer');

            this.gameContainerClass.appendGameContainer(container);

            expect(container.childNodes.length).toBeGreaterThan(0);
            expect(container.childNodes[0].id).toBe('gameMenu');
        });

        // test Class panelGamer
        it('Test PanelGamer Class [getPanelGamer] method', function() {
            var panelGamer = this.panelGamerClass.getPanelGamer(),
                mockResponseId = 'panelGamer';

            expect(panelGamer.tagName).toBe('DIV');
            expect(panelGamer.id).toBe(mockResponseId);
        });
        it('Test panelGamer Class [appendPanelGamer] method', function() {
            var container = consts.DOC.getElementById('gameContainer');

            this.panelGamerClass.appendPanelGamer(container);

            expect(container.childNodes.length).toBeGreaterThan(0);
            expect(container.childNodes[0].id).toBe('panelGamer');
        });

        // test createTable method
        it('Test createTable method', function() {
            var panelGamer = this.panelGamerClass.getPanelGamer(),
                table = components.createTable(panelGamer);

            panelGamer.appendChild(table);

            expect(panelGamer.childNodes.length).toBeGreaterThan(0);
            expect(panelGamer.childNodes[1].tagName).toBe('TABLE');
        });
    });
});
<|endoftext|>