<commit_before>/* global kjua jQuery introJs*/
import diffDom from 'diff-dom';
import { setupFirebasePush } from './notificationService/indexFirebase';
import { sendShownCallback, sendReadCallback} from './notificationService/callback';

if (window.opener && window.opener !== window) {
    window.isInline = true;
}

const diffDOM = new diffDom();

export function softNavigate(newurl, selector = 'html', listener, callback) {
    $.ajax({
        type: "GET",
        url: newurl
    }).done(function (r) {
        // render new page
        const parser = new DOMParser();
        const newPage = parser.parseFromString(r, "text/html");
        // apply new page
        try {
            const newPagePart = newPage.querySelector(selector);
            const oldPagePart = document.querySelector(selector);
            const diff = diffDOM.diff(oldPagePart, newPagePart);
            const result = diffDOM.apply(oldPagePart, diff);
            document.querySelectorAll((listener || selector) + " a").forEach(link => {
                const linkClone = link.cloneNode(true);
                linkClone.addEventListener("click", function (e) {
                    softNavigate($(this).attr('href'), selector, listener);
                    e.preventDefault();
                    e.stopPropagation();
                    return false;
                });
                link.parentNode.replaceChild(linkClone, link);
            });
            // scroll to top
            document.body.scrollTop = 0; // For Safari
            document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
            jQuery(document).trigger('pageload');
            if (callback) {
                callback();
            }
        } catch (e) {
            console.error(e);
            $.showNotification("Fehler bei AJAX-Navigation", "danger", true);
        }
    });
}

function toggleMobileNav() {
    document.querySelector('aside.nav-sidebar nav:first-child').classList.toggle('active');
    this.classList.toggle('active');
}

function toggleMobileSearch() {
    document.querySelector('.search-wrapper .input-group').classList.toggle('active');
    document.querySelector('.search-wrapper .mobile-search-toggle .fa').classList.toggle('fa-search');
    document.querySelector('.search-wrapper .mobile-search-toggle .fa').classList.toggle('fa-times');
}

function togglePresentationMode() {
    const contentArea = $('#main-content');
    const toggleButton = $('.btn-fullscreen');
    $('body').toggleClass('fullscreen');
    toggleButton.children('i').toggleClass('fa-compress');
    toggleButton.children('i').toggleClass('fa-expand');
}

var fullscreen = false;

function fullscreenBtnClicked() {
    togglePresentationMode();
    fullscreen = !fullscreen;
    sessionStorage.setItem("fullscreen", JSON.stringify(fullscreen));
}

$(document).ready(function () {
    // Init mobile nav
    var mobileNavToggle = document.querySelector('.mobile-nav-toggle');
    var mobileSearchToggle = document.querySelector('.mobile-search-toggle');
    if (mobileNavToggle) {
        mobileNavToggle.addEventListener('click', toggleMobileNav);
    }
    if (mobileSearchToggle) {
        mobileSearchToggle.addEventListener('click', toggleMobileSearch);
    }

    // Init modals
    var $modals = $('.modal');
    var $feedbackModal = $('.feedback-modal');
    var $featureModal = $('.feature-modal');
    var $problemModal = $('.problem-modal');
    var $modalForm = $('.modal-form');

    function showAJAXError(req, textStatus, errorThrown) {
        $feedbackModal.modal('hide');
        $problemModal.modal('hide');
        if (textStatus === "timeout") {
            $.showNotification("Zeitüberschreitung der Anfrage", "warn", true);
        } else {
            $.showNotification(errorThrown, "danger", true);
        }
    }

    function showAJAXSuccess(message, modal) {
        modal.modal('hide');
        $.showNotification(message, "success", true);
    }

    /**
     * creates the feedback-message which will be sent to the Schul-Cloud helpdesk
     * @param modal {object} - modal containing content from feedback-form
     */
    const createFeedbackMessage = function (modal) {
        return "Als " + modal.find('#role').val() + "\n" +
            "möchte ich " + modal.find('#desire').val() + ",\n" +
            "um " + modal.find("#benefit").val() + ".\n" +
            "Akzeptanzkriterien: " + modal.find("#acceptance_criteria").val();
    };

    const sendFeedback = function (modal, e) {
        e.preventDefault();

        let type = (modal[0].className.includes('feedback-modal')) ? 'feedback' : 'problem';

        let email = 'ticketsystem@schul-cloud.org';
        let subject = (type === 'feedback') ? 'Feedback' : 'Problem ' + modal.find('#title').val();
        let text = createFeedbackMessage(modal);
        let content = {text: text};
        let category = modal.find('#category').val();
        let currentState = modal.find('#hasHappened').val();
        let targetState = modal.find('#supposedToHappen').val();

        $.ajax({
            url: '/helpdesk',
            type: 'POST',
            data: {
                email: email,
                modalEmail: modal.find('#email').val(),
                subject: subject,
                content: content,
                type: type,
                category: category,
                currentState: currentState,
                targetState: targetState
            },
            success: function (result) {
                showAJAXSuccess("Feedback erfolgreich versendet!", modal);
            },
            error: showAJAXError
        });

        $('.feedback-modal').find('.btn-submit').prop("disabled", true);
    };

    $('.submit-helpdesk').on('click', function (e) {
        e.preventDefault();

        $('.feedback-modal').find('.btn-submit').prop("disabled", false);
        var title = $(document).find("title").text();
        var area = title.slice(0, title.indexOf('- Schul-Cloud') === -1 ? title.length : title.indexOf('- Schul-Cloud'));
        populateModalForm($feedbackModal, {
            title: 'User Story eingeben',
            closeLabel: 'Abbrechen',
            submitLabel: 'Senden'
        });

        $feedbackModal.find('.modal-form').on('submit', sendFeedback.bind(this, $feedbackModal));
        $feedbackModal.appendTo('body').modal('show');
        $feedbackModal.find('#title-area').html(area);
    });

    $('.submit-problem').on('click', function (e) {
        e.preventDefault();

        $('.problem-modal').find('.btn-submit').prop("disabled", false);
        populateModalForm($problemModal, {
            title: 'Problem melden',
            closeLabel: 'Abbrechen',
            submitLabel: 'Senden'
        });

        $problemModal.find('.modal-form').on('submit', sendFeedback.bind(this, $problemModal));
        $problemModal.appendTo('body').modal('show');
    });

    $modals.find('.close, .btn-close').on('click', function () {
        $modals.modal('hide');
    });

    $('.notification-dropdown-toggle').on('click', function () {
        $(this).removeClass('recent');

        $('.notification-dropdown .notification-item.unread').each(function () {
            if ($(this).data('read') == true) return;

            sendShownCallback({notificationId: $(this).data('notification-id')});
            sendReadCallback($(this).data('notification-id'));
            $(this).data('read', true);
        });
    });

    $('.btn-create-qr').on('click', function () {
        // create qr code for current page
        let image = kjua({text: window.location.href, render: 'image'});
        let $qrbox = $('.qr-show');
        $qrbox.empty();
        $qrbox.append(image);
    });

    // Init mobile nav
    if (document.getElementById('searchBar') instanceof Object) {
        document.querySelector('.mobile-nav-toggle').addEventListener('click', toggleMobileNav);
        document.querySelector('.mobile-search-toggle').addEventListener('click', toggleMobileSearch);
    }

    if (!fullscreen) {
        fullscreen = JSON.parse(sessionStorage.getItem("fullscreen")) || false;
        if (fullscreen) {
            togglePresentationMode();
        }
    }
    if(document.querySelector('.btn-fullscreen')){
        document.querySelector('.btn-fullscreen').addEventListener('click', fullscreenBtnClicked);
    }

    $('.btn-cancel').on('click', function (e) {
        e.stopPropagation();
        e.preventDefault();
        let $cancelModal = $('.cancel-modal');
        populateModalForm($cancelModal, {
            title: 'Bist du dir sicher, dass du die Änderungen verwerfen möchtest?',
        });
        let $modalForm = $cancelModal.find(".modal-form");
        $cancelModal.appendTo('body').modal('show');
    });

    populateModalForm($featureModal, {
        title: 'Neue Features sind verfügbar',
        closeLabel: 'Abbrechen'
    });

    // loading animation
    window.addEventListener("beforeunload", function (e) {
        const loaderClassList = document.querySelector(".preload-screen").classList;
        loaderClassList.remove("hidden");
    });
    window.addEventListener("pageshow", function (e) {
        const loaderClassList = document.querySelector(".preload-screen").classList;
        loaderClassList.add("hidden");
    });

    // from: https://stackoverflow.com/a/187557
    jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function (arg) {
        return function (elem) {
            return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
        };
    });
    // js course search/filter
    $("input.js-search").on("keyup", e => {
        if (e.key === "Escape") $(e.target).val("");
        if (e.key === "Unidentified") {
            return false;
        }
        $(".sc-card-title").find('.title:not(:Contains("' + $(e.target).val() + '"))').parents(".sc-card-wrapper").fadeOut(400);
        $(".sc-card-title").find('.title:Contains("' + $(e.target).val() + '")').parents(".sc-card-wrapper").fadeIn(400);

        return !(e.key === "Unidentified");
    });
});

window.addEventListener('DOMContentLoaded', function() {
    if (!/^((?!chrome).)*safari/i.test(navigator.userAgent)) {
        setupFirebasePush();
    }
});
window.addEventListener("resize", function () {
    $('.sidebar-list').css({"height": window.innerHeight});
});

function startIntro() {
    introJs()
    .setOptions({
        nextLabel: "Weiter",
        prevLabel: "Zurück",
        doneLabel: "Fertig",
        skipLabel: "Überspringen"
    })
    .start();
}
<commit_msg>Revert "disable sw"<commit_after>/* global kjua jQuery introJs*/
import diffDom from 'diff-dom';
import { setupFirebasePush } from './notificationService/indexFirebase';
import { sendShownCallback, sendReadCallback} from './notificationService/callback';

if (window.opener && window.opener !== window) {
    window.isInline = true;
}

const diffDOM = new diffDom();

export function softNavigate(newurl, selector = 'html', listener, callback) {
    $.ajax({
        type: "GET",
        url: newurl
    }).done(function (r) {
        // render new page
        const parser = new DOMParser();
        const newPage = parser.parseFromString(r, "text/html");
        // apply new page
        try {
            const newPagePart = newPage.querySelector(selector);
            const oldPagePart = document.querySelector(selector);
            const diff = diffDOM.diff(oldPagePart, newPagePart);
            const result = diffDOM.apply(oldPagePart, diff);
            document.querySelectorAll((listener || selector) + " a").forEach(link => {
                const linkClone = link.cloneNode(true);
                linkClone.addEventListener("click", function (e) {
                    softNavigate($(this).attr('href'), selector, listener);
                    e.preventDefault();
                    e.stopPropagation();
                    return false;
                });
                link.parentNode.replaceChild(linkClone, link);
            });
            // scroll to top
            document.body.scrollTop = 0; // For Safari
            document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
            jQuery(document).trigger('pageload');
            if (callback) {
                callback();
            }
        } catch (e) {
            console.error(e);
            $.showNotification("Fehler bei AJAX-Navigation", "danger", true);
        }
    });
}

function toggleMobileNav() {
    document.querySelector('aside.nav-sidebar nav:first-child').classList.toggle('active');
    this.classList.toggle('active');
}

function toggleMobileSearch() {
    document.querySelector('.search-wrapper .input-group').classList.toggle('active');
    document.querySelector('.search-wrapper .mobile-search-toggle .fa').classList.toggle('fa-search');
    document.querySelector('.search-wrapper .mobile-search-toggle .fa').classList.toggle('fa-times');
}

function togglePresentationMode() {
    const contentArea = $('#main-content');
    const toggleButton = $('.btn-fullscreen');
    $('body').toggleClass('fullscreen');
    toggleButton.children('i').toggleClass('fa-compress');
    toggleButton.children('i').toggleClass('fa-expand');
}

var fullscreen = false;

function fullscreenBtnClicked() {
    togglePresentationMode();
    fullscreen = !fullscreen;
    sessionStorage.setItem("fullscreen", JSON.stringify(fullscreen));
}

$(document).ready(function () {
    // Init mobile nav
    var mobileNavToggle = document.querySelector('.mobile-nav-toggle');
    var mobileSearchToggle = document.querySelector('.mobile-search-toggle');
    if (mobileNavToggle) {
        mobileNavToggle.addEventListener('click', toggleMobileNav);
    }
    if (mobileSearchToggle) {
        mobileSearchToggle.addEventListener('click', toggleMobileSearch);
    }

    // Init modals
    var $modals = $('.modal');
    var $feedbackModal = $('.feedback-modal');
    var $featureModal = $('.feature-modal');
    var $problemModal = $('.problem-modal');
    var $modalForm = $('.modal-form');

    function showAJAXError(req, textStatus, errorThrown) {
        $feedbackModal.modal('hide');
        $problemModal.modal('hide');
        if (textStatus === "timeout") {
            $.showNotification("Zeitüberschreitung der Anfrage", "warn", true);
        } else {
            $.showNotification(errorThrown, "danger", true);
        }
    }

    function showAJAXSuccess(message, modal) {
        modal.modal('hide');
        $.showNotification(message, "success", true);
    }

    /**
     * creates the feedback-message which will be sent to the Schul-Cloud helpdesk
     * @param modal {object} - modal containing content from feedback-form
     */
    const createFeedbackMessage = function (modal) {
        return "Als " + modal.find('#role').val() + "\n" +
            "möchte ich " + modal.find('#desire').val() + ",\n" +
            "um " + modal.find("#benefit").val() + ".\n" +
            "Akzeptanzkriterien: " + modal.find("#acceptance_criteria").val();
    };

    const sendFeedback = function (modal, e) {
        e.preventDefault();

        let type = (modal[0].className.includes('feedback-modal')) ? 'feedback' : 'problem';

        let email = 'ticketsystem@schul-cloud.org';
        let subject = (type === 'feedback') ? 'Feedback' : 'Problem ' + modal.find('#title').val();
        let text = createFeedbackMessage(modal);
        let content = {text: text};
        let category = modal.find('#category').val();
        let currentState = modal.find('#hasHappened').val();
        let targetState = modal.find('#supposedToHappen').val();

        $.ajax({
            url: '/helpdesk',
            type: 'POST',
            data: {
                email: email,
                modalEmail: modal.find('#email').val(),
                subject: subject,
                content: content,
                type: type,
                category: category,
                currentState: currentState,
                targetState: targetState
            },
            success: function (result) {
                showAJAXSuccess("Feedback erfolgreich versendet!", modal);
            },
            error: showAJAXError
        });

        $('.feedback-modal').find('.btn-submit').prop("disabled", true);
    };

    $('.submit-helpdesk').on('click', function (e) {
        e.preventDefault();

        $('.feedback-modal').find('.btn-submit').prop("disabled", false);
        var title = $(document).find("title").text();
        var area = title.slice(0, title.indexOf('- Schul-Cloud') === -1 ? title.length : title.indexOf('- Schul-Cloud'));
        populateModalForm($feedbackModal, {
            title: 'User Story eingeben',
            closeLabel: 'Abbrechen',
            submitLabel: 'Senden'
        });

        $feedbackModal.find('.modal-form').on('submit', sendFeedback.bind(this, $feedbackModal));
        $feedbackModal.appendTo('body').modal('show');
        $feedbackModal.find('#title-area').html(area);
    });

    $('.submit-problem').on('click', function (e) {
        e.preventDefault();

        $('.problem-modal').find('.btn-submit').prop("disabled", false);
        populateModalForm($problemModal, {
            title: 'Problem melden',
            closeLabel: 'Abbrechen',
            submitLabel: 'Senden'
        });

        $problemModal.find('.modal-form').on('submit', sendFeedback.bind(this, $problemModal));
        $problemModal.appendTo('body').modal('show');
    });

    $modals.find('.close, .btn-close').on('click', function () {
        $modals.modal('hide');
    });

    $('.notification-dropdown-toggle').on('click', function () {
        $(this).removeClass('recent');

        $('.notification-dropdown .notification-item.unread').each(function () {
            if ($(this).data('read') == true) return;

            sendShownCallback({notificationId: $(this).data('notification-id')});
            sendReadCallback($(this).data('notification-id'));
            $(this).data('read', true);
        });
    });

    $('.btn-create-qr').on('click', function () {
        // create qr code for current page
        let image = kjua({text: window.location.href, render: 'image'});
        let $qrbox = $('.qr-show');
        $qrbox.empty();
        $qrbox.append(image);
    });

    // Init mobile nav
    if (document.getElementById('searchBar') instanceof Object) {
        document.querySelector('.mobile-nav-toggle').addEventListener('click', toggleMobileNav);
        document.querySelector('.mobile-search-toggle').addEventListener('click', toggleMobileSearch);
    }

    if (!fullscreen) {
        fullscreen = JSON.parse(sessionStorage.getItem("fullscreen")) || false;
        if (fullscreen) {
            togglePresentationMode();
        }
    }
    if(document.querySelector('.btn-fullscreen')){
        document.querySelector('.btn-fullscreen').addEventListener('click', fullscreenBtnClicked);
    }

    $('.btn-cancel').on('click', function (e) {
        e.stopPropagation();
        e.preventDefault();
        let $cancelModal = $('.cancel-modal');
        populateModalForm($cancelModal, {
            title: 'Bist du dir sicher, dass du die Änderungen verwerfen möchtest?',
        });
        let $modalForm = $cancelModal.find(".modal-form");
        $cancelModal.appendTo('body').modal('show');
    });

    populateModalForm($featureModal, {
        title: 'Neue Features sind verfügbar',
        closeLabel: 'Abbrechen'
    });

    // loading animation
    window.addEventListener("beforeunload", function (e) {
        const loaderClassList = document.querySelector(".preload-screen").classList;
        loaderClassList.remove("hidden");
    });
    window.addEventListener("pageshow", function (e) {
        const loaderClassList = document.querySelector(".preload-screen").classList;
        loaderClassList.add("hidden");
    });

    // from: https://stackoverflow.com/a/187557
    jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function (arg) {
        return function (elem) {
            return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
        };
    });
    // js course search/filter
    $("input.js-search").on("keyup", e => {
        if (e.key === "Escape") $(e.target).val("");
        if (e.key === "Unidentified") {
            return false;
        }
        $(".sc-card-title").find('.title:not(:Contains("' + $(e.target).val() + '"))').parents(".sc-card-wrapper").fadeOut(400);
        $(".sc-card-title").find('.title:Contains("' + $(e.target).val() + '")').parents(".sc-card-wrapper").fadeIn(400);

        return !(e.key === "Unidentified");
    });
});

window.addEventListener('DOMContentLoaded', function() {
    if (!/^((?!chrome).)*safari/i.test(navigator.userAgent)) {
        setupFirebasePush();
    }
});
window.addEventListener("resize", function () {
    $('.sidebar-list').css({"height": window.innerHeight});
});

function startIntro() {
    introJs()
    .setOptions({
        nextLabel: "Weiter",
        prevLabel: "Zurück",
        doneLabel: "Fertig",
        skipLabel: "Überspringen"
    })
    .start();
}

if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('/sw.js').then(registration => {
            console.log('SW registered: ', registration);
        }).catch(registrationError => {
            console.log('SW registration failed: ', registrationError);
        });
    });
}
<|endoftext|>


<commit_before>import _ from 'lodash';

const isEqualsUndefinedAssignment = _.matches({
  type: 'ExpressionStatement',
  expression: {
    type: 'AssignmentExpression',
    left: {
      type: 'Identifier',
      // name: <name>
    },
    operator: '=',
    right: {
      type: 'ConditionalExpression',
      test: {
        type: 'BinaryExpression',
        left: {
          type: 'Identifier'
          // name: <name>
        },
        // operator: <operator>
        right: {
          type: 'Identifier',
          name: 'undefined'
        }
      },
      consequent: {
        // <consequent>
      },
      alternate: {
        // <alternate>
      }
    }
  }
});

/**
 * Matches: <name> = <name> === undefined ? <value> : <name>;
 *
 * When node matches returns the extracted fields:
 *
 * - name
 * - value
 * - node (the entire node)
 *
 * @param {Object} node
 * @return {Object}
 */
export default function (node) {
  if (isEqualsUndefinedAssignment(node)) {
    const {
      expression: {
        left: {name},
        right: {
          test: {
            left: {name: name2},
            operator
          },
          consequent,
          alternate
        }
      }
    } = node;

    if (name === name2) {
      if ((operator === '===' || operator === '==') && alternate.type === 'Identifier') {
        return {name, value: consequent, node};
      }
      if ((operator === '!==' || operator === '!=') && consequent.type === 'Identifier') {
        return {name, value: alternate, node};
      }
    }
  }
}
<commit_msg>Improve matchEqualsUndefinedAssignment() with matchesAst()<commit_after>import {matchesAst, extract} from '../../utils/matches-ast';

const matchEqualsUndefinedAssignment = matchesAst({
  type: 'ExpressionStatement',
  expression: {
    type: 'AssignmentExpression',
    left: {
      type: 'Identifier',
      name: extract('name')
    },
    operator: '=',
    right: {
      type: 'ConditionalExpression',
      test: {
        type: 'BinaryExpression',
        left: {
          type: 'Identifier',
          name: extract('name2')
        },
        operator: extract('operator'),
        right: {
          type: 'Identifier',
          name: 'undefined'
        }
      },
      consequent: extract('consequent'),
      alternate: extract('alternate')
    }
  }
});

/**
 * Matches: <name> = <name> === undefined ? <value> : <name>;
 *
 * When node matches returns the extracted fields:
 *
 * - name
 * - value
 * - node (the entire node)
 *
 * @param {Object} node
 * @return {Object}
 */
export default function (node) {
  const {name, name2, operator, consequent, alternate} = matchEqualsUndefinedAssignment(node) || {};

  if (name === name2) {
    if ((operator === '===' || operator === '==') && alternate.type === 'Identifier' && alternate.name === name) {
      return {name, value: consequent, node};
    }
    if ((operator === '!==' || operator === '!=') && consequent.type === 'Identifier' && consequent.name === name) {
      return {name, value: alternate, node};
    }
  }
}
<|endoftext|>


<commit_before>describe('CollapsibleCollection', function(){
  var collectionsFromHRMCHTML, collectionsFromBlobHTML, collectionBlob, collection;

  beforeEach(function(){

    // Content comes as a single blob of markdown from the GDSAPI in the "body" field.
    collectionsFromBlobString =
      '
'+
        '
'+
          '
'+
            // Some content not in a collapsible subsection
            '
Where the tax gets paid to the man about the thing for the other thing
'+
            '
Where the tax gets paid to the man about the thing for the other thing
'+
            '
Where the tax gets paid to the man about the thing for the other thing
'+
            // Three collapsible subsections
            '
A section title!
'+
            '
A subsection title!
'+
            '
Where an employer meets the tax payable on a non-cash incentive award given to a direct
'+
            '
A second section title!
'+
            '
Where an employer meets the tax payable on a non-cash incentive award given to a direct
'+
            '
Where an employer meets the tax payable on a non-cash incentive award given to a direct
'+
            '
A third section title!
'+
            '
Where an employer meets the tax payable on a non-cash incentive award given to a direct
'+
            '
Follow this Link to subsection in first section
'+
          '
'+
        '
'+
      '
';

      collectionsFromBlobHTML = $(collectionsFromBlobString);

      $('body').append(collectionsFromBlobHTML);
      collection = new GOVUK.CollapsibleCollection({$el: collectionsFromBlobHTML});
  });

  afterEach(function(){
    collectionsFromBlobHTML.remove();
  });

  var resetBody = function resetBody() {
    collectionsFromBlobHTML.remove();
    collectionsFromBlobHTML = $(collectionsFromBlobString);
    $('body').append(collectionsFromBlobHTML);
  };

  describe('CollapsibleCollection', function(){
    beforeEach(function(){
      resetBody();
    });

    it('should close all sections by default', function(){
      spyOn(GOVUK, 'getCurrentLocation').and.returnValue({
        hash: ''
      });

      var collection = new GOVUK.CollapsibleCollection({$el: collectionsFromBlobHTML});

      var sections = $.map(
        collection.collapsibles,
        function(section, index) { return section; }
      );

      var openSections = sections.filter(
        function(section) { return !section.isClosed(); }
      );

      expect(openSections.length).toBe(0);
    });

    it('should open the section linked to by the anchor in the URL', function(){
      spyOn(GOVUK, 'getCurrentLocation').and.returnValue({
        hash: '#a-second-section-title'
      });

      var collection = new GOVUK.CollapsibleCollection({$el: collectionsFromBlobHTML});

      var sections = $.map(
        collection.collapsibles,
        function(section, index) { return section; }
      );

      var openSections = sections.filter(
        function(section) { return !section.isClosed(); }
      );

      expect(openSections.length).toBe(1);
      expect(openSections[0]).toBe(collection.collapsibles['a-second-section-title']);
    });

    it('should open the section linked to by the anchor on the same page', function(){
      var collection = new GOVUK.CollapsibleCollection({$el: collectionsFromBlobHTML});

      $('#internal-link').click();

      var sections = $.map(
        collection.collapsibles,
        function(section, index) { return section; }
      );

      var openSections = sections.filter(
        function(section) { return !section.isClosed(); }
      );

      expect(openSections.length).toBe(1);
      expect(openSections[0]).toBe(collection.collapsibles['a-section-title']);
    });
  });

  describe('initCollapsible', function(){
    it ('should add control links to HTML generated from a blob', function(){
      var html = $(collectionsFromBlobString);
      expect(html.find('a.collection-control').length).toBe(0);
      var collection = new GOVUK.CollapsibleCollection({$el:html});
      expect(collection.$container.find('.js-collection-controls a').length).toBe(2);
    });

    it('should add a new object to collapsibles hash with the id from the section for blobs', function(){
      var collectionSize = Object.keys(collection.collapsibles).length;
      collection.initCollapsible(collection.$sections[0]);
      expect(Object.keys(collection.collapsibles).length).toBe(collectionSize+1);
    });
  });

  describe('markupSections', function(){
    it('should add the a js-subsection-title class to any h2s that are not excluded by js-exclude-h2s or have the link-out class for blobs', function(){
      var html = $(collectionsFromBlobString);
      var h2Count = html.find('h2').length;
      var excludedH2Count = html.find('.js-ignore-h2s h2, h2.linked-title').length
      var sectionHeaderCount = h2Count - excludedH2Count;

      expect(html.find('h2.js-subsection-title').length).toBe(0);
      var newCollection = new GOVUK.CollapsibleCollection({$el:html});
      expect(html.find('h2.js-subsection-title').length).toBe(sectionHeaderCount);
    });

    it('should wrap h2 and section in a div with the classes js-openable for blobs', function(){
      collectionsFromBlobHTML.find('h2.js-subsection-title').each(function(index){
        expect($(this).parents('.js-openable').length).toBe(1);
      });
    });

    it('should wrap all tags following a js-subsection-title h2 up to the next js-subsection-title h2 in a div with the class js-subsection-body', function(){
      collectionsFromBlobHTML.find('h2.js-subsection-title').each(function(index){
        expect($(this).next().hasClass('js-subsection-body')).toBe(true);
      });
    });
  });

  describe('calculateSuperiorsSelector', function(){
    it ('should return all the headings of that depth level or of greater priority', function(){
     expect(collection.calculateSuperiorsSelector(1)).toBe('h2,h1');
     expect(collection.calculateSuperiorsSelector(2)).toBe('h3,h2,h1');
   });
  });

  describe('closeAll', function(){
    it('should close all collapsibles in this collection', function(){
      collection.openAll();

      for (var collapsible_id in this.collapsibles) {
        expect($(this.collapsible[collapsible.id]).isClosed()).toBe(false)
      }
      collection.closeAll();

      for (var collapsible_id in this.collapsibles) {
        expect($(this.collapsible[collapsible.id]).isClosed()).toBe(true)
      }
    });

    it('should call disable with the closeAll control', function(){
      spyOn(collection, "disableControl");
      collection.closeAll();
      expect(collection.disableControl).toHaveBeenCalledWith(collection.$closeAll);
    });

    it('should call enable with the openAll control', function(){
      spyOn(collection, "enableControl");
      collection.closeAll();
      expect(collection.enableControl).toHaveBeenCalledWith(collection.$openAll);
    });
  });

  describe('openAll', function(){
    it('should open all collapsibles in this collection', function(){
      collection.closeAll();

      for (var collapsible_id in this.collapsibles) {
        expect($(this.collapsible[collapsible.id]).isOpen()).toBe(false)
      }
      collection.openAll();

      for (var collapsible_id in this.collapsibles) {
        expect($(this.collapsible[collapsible.id]).isClosed()).toBe(true)
      }
    });

    it('should call disable with the openAll control', function(){
      spyOn(collection, "disableControl");
      collection.openAll();
      expect(collection.disableControl).toHaveBeenCalledWith(collection.$openAll);
    });

    it('should call enable with the closeAll control', function(){
      spyOn(collection, "enableControl");
      collection.openAll();
      expect(collection.enableControl).toHaveBeenCalledWith(collection.$closeAll);
    });
  });

  describe('updateControls', function(){
    it ('should call enableControl with openAll if any of the collapsible sections are closed', function(){
      spyOn(collection, "enableControl");
      collection.collapsibles[Object.keys(collection.collapsibles)[0]].close();
      collection.collapsibles[Object.keys(collection.collapsibles)[1]].open();
      collection.updateControls();

      expect(collection.enableControl).toHaveBeenCalledWith(collection.$openAll);
    });

    it ('should call enableControl with closeAll if any of the collapsible sections are open', function(){
      spyOn(collection, "enableControl");
      collection.collapsibles[Object.keys(collection.collapsibles)[0]].close();
      collection.collapsibles[Object.keys(collection.collapsibles)[1]].open();
      collection.updateControls();

      expect(collection.enableControl).toHaveBeenCalledWith(collection.$closeAll);
    });

    it ('should call disableControl with closeAll if all of the collapsible sections are closed', function(){
      spyOn(collection, "disableControl");
      collection.closeAll();

      collection.updateControls();

      expect(collection.disableControl).toHaveBeenCalledWith(collection.$closeAll);
    });

    it ('should call disableControl with openAll if all of the collapsible sections are open', function(){
      spyOn(collection, "disableControl");
      collection.openAll();

      collection.updateControls();

      expect(collection.disableControl).toHaveBeenCalledWith(collection.$openAll);
    });
  });
});

<commit_msg>Extract finding open sections to helper method<commit_after>describe('CollapsibleCollection', function(){
  var collectionsFromHRMCHTML, collectionsFromBlobHTML, collectionBlob, collection;

  beforeEach(function(){

    // Content comes as a single blob of markdown from the GDSAPI in the "body" field.
    collectionsFromBlobString =
      '
'+
        '
'+
          '
'+
            // Some content not in a collapsible subsection
            '
Where the tax gets paid to the man about the thing for the other thing
'+
            '
Where the tax gets paid to the man about the thing for the other thing
'+
            '
Where the tax gets paid to the man about the thing for the other thing
'+
            // Three collapsible subsections
            '
A section title!
'+
            '
A subsection title!
'+
            '
Where an employer meets the tax payable on a non-cash incentive award given to a direct
'+
            '
A second section title!
'+
            '
Where an employer meets the tax payable on a non-cash incentive award given to a direct
'+
            '
Where an employer meets the tax payable on a non-cash incentive award given to a direct
'+
            '
A third section title!
'+
            '
Where an employer meets the tax payable on a non-cash incentive award given to a direct
'+
            '
Follow this Link to subsection in first section
'+
          '
'+
        '
'+
      '
';

      collectionsFromBlobHTML = $(collectionsFromBlobString);

      $('body').append(collectionsFromBlobHTML);
      collection = new GOVUK.CollapsibleCollection({$el: collectionsFromBlobHTML});
  });

  afterEach(function(){
    collectionsFromBlobHTML.remove();
  });

  var findOpenSections = function openSections(collapsibles) {
    return $.map(
      collapsibles,
      function(section, index) { return section.isClosed() ? null : section; }
    );
  };

  var resetBody = function resetBody() {
    collectionsFromBlobHTML.remove();
    collectionsFromBlobHTML = $(collectionsFromBlobString);
    $('body').append(collectionsFromBlobHTML);
  };

  describe('CollapsibleCollection', function(){
    beforeEach(function(){
      resetBody();
    });

    it('should close all sections by default', function(){
      spyOn(GOVUK, 'getCurrentLocation').and.returnValue({
        hash: ''
      });

      var collection = new GOVUK.CollapsibleCollection({$el: collectionsFromBlobHTML});

      var openSections = findOpenSections(collection.collapsibles);

      expect(openSections.length).toBe(0);
    });

    it('should open the section linked to by the anchor in the URL', function(){
      spyOn(GOVUK, 'getCurrentLocation').and.returnValue({
        hash: '#a-second-section-title'
      });

      var collection = new GOVUK.CollapsibleCollection({$el: collectionsFromBlobHTML});

      var openSections = findOpenSections(collection.collapsibles);

      expect(openSections.length).toBe(1);
      expect(openSections[0]).toBe(collection.collapsibles['a-second-section-title']);
    });

    it('should open the closed section linked to by the anchor on the same page', function(){
      var collection = new GOVUK.CollapsibleCollection({$el: collectionsFromBlobHTML});

      $('#internal-link').click();

      var openSections = findOpenSections(collection.collapsibles);

      expect(openSections.length).toBe(1);
      expect(openSections[0]).toBe(collection.collapsibles['a-section-title']);
    });
  });

  describe('initCollapsible', function(){
    it ('should add control links to HTML generated from a blob', function(){
      var html = $(collectionsFromBlobString);
      expect(html.find('a.collection-control').length).toBe(0);
      var collection = new GOVUK.CollapsibleCollection({$el:html});
      expect(collection.$container.find('.js-collection-controls a').length).toBe(2);
    });

    it('should add a new object to collapsibles hash with the id from the section for blobs', function(){
      var collectionSize = Object.keys(collection.collapsibles).length;
      collection.initCollapsible(collection.$sections[0]);
      expect(Object.keys(collection.collapsibles).length).toBe(collectionSize+1);
    });
  });

  describe('markupSections', function(){
    it('should add the a js-subsection-title class to any h2s that are not excluded by js-exclude-h2s or have the link-out class for blobs', function(){
      var html = $(collectionsFromBlobString);
      var h2Count = html.find('h2').length;
      var excludedH2Count = html.find('.js-ignore-h2s h2, h2.linked-title').length
      var sectionHeaderCount = h2Count - excludedH2Count;

      expect(html.find('h2.js-subsection-title').length).toBe(0);
      var newCollection = new GOVUK.CollapsibleCollection({$el:html});
      expect(html.find('h2.js-subsection-title').length).toBe(sectionHeaderCount);
    });

    it('should wrap h2 and section in a div with the classes js-openable for blobs', function(){
      collectionsFromBlobHTML.find('h2.js-subsection-title').each(function(index){
        expect($(this).parents('.js-openable').length).toBe(1);
      });
    });

    it('should wrap all tags following a js-subsection-title h2 up to the next js-subsection-title h2 in a div with the class js-subsection-body', function(){
      collectionsFromBlobHTML.find('h2.js-subsection-title').each(function(index){
        expect($(this).next().hasClass('js-subsection-body')).toBe(true);
      });
    });
  });

  describe('calculateSuperiorsSelector', function(){
    it ('should return all the headings of that depth level or of greater priority', function(){
     expect(collection.calculateSuperiorsSelector(1)).toBe('h2,h1');
     expect(collection.calculateSuperiorsSelector(2)).toBe('h3,h2,h1');
   });
  });

  describe('closeAll', function(){
    it('should close all collapsibles in this collection', function(){
      collection.openAll();

      for (var collapsible_id in this.collapsibles) {
        expect($(this.collapsible[collapsible.id]).isClosed()).toBe(false)
      }
      collection.closeAll();

      for (var collapsible_id in this.collapsibles) {
        expect($(this.collapsible[collapsible.id]).isClosed()).toBe(true)
      }
    });

    it('should call disable with the closeAll control', function(){
      spyOn(collection, "disableControl");
      collection.closeAll();
      expect(collection.disableControl).toHaveBeenCalledWith(collection.$closeAll);
    });

    it('should call enable with the openAll control', function(){
      spyOn(collection, "enableControl");
      collection.closeAll();
      expect(collection.enableControl).toHaveBeenCalledWith(collection.$openAll);
    });
  });

  describe('openAll', function(){
    it('should open all collapsibles in this collection', function(){
      collection.closeAll();

      for (var collapsible_id in this.collapsibles) {
        expect($(this.collapsible[collapsible.id]).isOpen()).toBe(false)
      }
      collection.openAll();

      for (var collapsible_id in this.collapsibles) {
        expect($(this.collapsible[collapsible.id]).isClosed()).toBe(true)
      }
    });

    it('should call disable with the openAll control', function(){
      spyOn(collection, "disableControl");
      collection.openAll();
      expect(collection.disableControl).toHaveBeenCalledWith(collection.$openAll);
    });

    it('should call enable with the closeAll control', function(){
      spyOn(collection, "enableControl");
      collection.openAll();
      expect(collection.enableControl).toHaveBeenCalledWith(collection.$closeAll);
    });
  });

  describe('updateControls', function(){
    it ('should call enableControl with openAll if any of the collapsible sections are closed', function(){
      spyOn(collection, "enableControl");
      collection.collapsibles[Object.keys(collection.collapsibles)[0]].close();
      collection.collapsibles[Object.keys(collection.collapsibles)[1]].open();
      collection.updateControls();

      expect(collection.enableControl).toHaveBeenCalledWith(collection.$openAll);
    });

    it ('should call enableControl with closeAll if any of the collapsible sections are open', function(){
      spyOn(collection, "enableControl");
      collection.collapsibles[Object.keys(collection.collapsibles)[0]].close();
      collection.collapsibles[Object.keys(collection.collapsibles)[1]].open();
      collection.updateControls();

      expect(collection.enableControl).toHaveBeenCalledWith(collection.$closeAll);
    });

    it ('should call disableControl with closeAll if all of the collapsible sections are closed', function(){
      spyOn(collection, "disableControl");
      collection.closeAll();

      collection.updateControls();

      expect(collection.disableControl).toHaveBeenCalledWith(collection.$closeAll);
    });

    it ('should call disableControl with openAll if all of the collapsible sections are open', function(){
      spyOn(collection, "disableControl");
      collection.openAll();

      collection.updateControls();

      expect(collection.disableControl).toHaveBeenCalledWith(collection.$openAll);
    });
  });
});

<|endoftext|>


<commit_before>(function() {
  'use strict';

  angular.module('dimApp')
    .directive('dimStoreItems', StoreItems)
    .filter('equipped', function() {
      return function(items, isEquipped) {
        return _.select(items || [], function (item) {
          return item.equipped === isEquipped;
        });
      };
    })
    .filter('sortItems', function() {
      return function(items, sort) {
        return _(items || [])
              .chain()
              .sortBy('name')
              .sortBy(function(item) {
                if (sort === 'rarity') {
                  switch (item.tier) {
                  case 'Exotic':
                    return 0;
                  case 'Legendary':
                    return 1;
                  case 'Rare':
                    return 2;
                  case 'Uncommon':
                    return 3;
                  case 'Common':
                    return 4;
                  default:
                    return 5;
                  }
                } else {
                  return (item.primStat) ? (-1 * item.primStat.value) : 1000;
                }
              })
              .value();
      };
    });

  StoreItems.$inject = ['dimStoreService', '$window'];

  function StoreItems(dimStoreService, $window) {
    return {
      controller: StoreItemsCtrl,
      controllerAs: 'vm',
      bindToController: true,
      link: Link,
      replace: true,
      scope: {
        'store': '=storeData'
      },
      template: [
        '
',
        '
',
        '
',
        '
',
        '        {{ ::key }}',
        '        {{ vm.sortSize[key] ? vm.sortSize[key] : 0 }}/{{:: (key === \'Weapons\' || key === \'Armor\') ? 72 : 36 }}  ',
        '
',
        '
',
        '
',
        '

',
        '
',
        '
',
        '

',
        '

',
        '
',
        '
',
        '
',
        '
',
        '
'
      ].join('')
    };

    function Link(scope, element, attrs) {
      scope.vm.onDrop = function(id, $event, equip) {
        var vm = scope.vm;

        var srcElement = $('#' + id);

        vm.moveDroppedItem(angular.element(srcElement[0])
          .scope()
          .item, equip);
      };
    }
  }

  StoreItemsCtrl.$inject = ['$scope', '$rootScope', 'dimStoreService', 'dimItemService', '$q', '$timeout', 'toaster', 'dimSettingsService'];

  function StoreItemsCtrl($scope, $rootScope, dimStoreService, dimItemService, $q, $timeout, toaster, dimSettingsService) {
    var vm = this;

    var types = [ // Order of types in the rows.
      'Class',
      'Primary',
      'Special',
      'Heavy',
      'Helmet',
      'Gauntlets',
      'Chest',
      'Leg',
      'ClassItem',
      'Artifact',
      'Ghost',
      'Emblem',
      'Armor',
      'Ship',
      'Vehicle',
      'Horn',
      'Emote',
      'Consumable',
      'Material',
      'Missions',
      'Bounties',
      'Messages',
      'Special Orders',
      'Lost Items'
    ];
    vm.orderedTypes = {};

    _.each(types, function(value, index) {
      vm.orderedTypes[value] = index;
    });

    vm.sortSize = _.countBy(vm.store.items, 'sort');

    vm.categories = { // Grouping of the types in the rows.
      Weapons: [
        'Class',
        'Primary',
        'Special',
        'Heavy',
      ],
      Armor: [
        'Helmet',
        'Gauntlets',
        'Chest',
        'Leg',
        'ClassItem'
      ],
      General: [
        'Artifact',
        'Ghost',
        'Consumable',
        'Material',
        'Emblem',
        'Armor',
        'Emote',
        'Ship',
        'Vehicle',
        'Horn',
        'Missions',
        'Bounties'
      ],
      Postmaster: [
        'Messages',
        'Special Orders',
        'Lost Items'
      ]
    };

    vm.keys = _.keys(vm.categories);

    vm.styles = { // Styles of the types in the rows.
      Class: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Primary: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Special: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Heavy: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Helmet: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Gauntlets: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Chest: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Leg: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      ClassItem: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Artifact: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Emblem: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Armor: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Emote: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Ghost: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Ship: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Vehicle: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Horn: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Consumable: {
        equipped: '',
        unequipped: 'unequipped equippable',
      },
      Material: {
        equipped: '',
        unequipped: 'unequipped equippable',
      },
      Messages: {
        equipped: '',
        unequipped: 'unequipped equippable',
      },
      'Special-Orders': {
        equipped: '',
        unequipped: 'unequipped equippable',
      },
      'Lost-Items': {
        equipped: '',
        unequipped: 'unequipped equippable',
      }
    };

    vm.moveDroppedItem = function(item, equip) {
      var promise = null;
      var target = vm.store;

      if (item.owner === vm.store.id) {
        if ((item.equipped && equip) || (!item.equipped) && (!equip)) {
          return;
        }

        promise = $q.when(vm.store);

      } else {
        promise = dimStoreService.getStore(item.owner);
      }

      var source;

      if (item.notransfer && item.owner !== target.id) {
        return $q.reject(new Error('Cannot move class to different store.'));
      }

      var dimStores = null;

      promise = promise
        .then(function(s) {
          source = s;
        })
        .then(dimItemService.moveTo.bind(null, item, target, equip))
        .then(dimStoreService.getStores)
        .then(function(stores) {
          dimStores = stores;
          return dimStoreService.updateStores();
        })
        .then(function(bungieStores) {
          _.each(dimStores, function(dStore) {
            if (dStore.id !== 'vault') {
              var bStore = _.find(bungieStores, function(bStore) {
                return dStore.id === bStore.id;
              });

              dStore.level = bStore.base.characterLevel;
              dStore.percentToNextLevel = bStore.base.percentToNextLevel;
              dStore.powerLevel = bStore.base.characterBase.powerLevel;
              dStore.background = bStore.base.backgroundPath;
              dStore.icon = bStore.base.emblemPath;
            }
          });
        })
        .catch(function(a) {
          toaster.pop('error', item.name, a.message);
        });

      $rootScope.loadingTracker.addPromise(promise);
    };

    function resetData() {
      if (vm.store.id === 'vault') {
        vm.sortSize = _.countBy(vm.store.items, 'sort');
      }

      vm.data = _.groupBy(vm.store.items, function(item) {
        return vm.orderedTypes[item.type];
      });
    }

    dimSettingsService.getSetting('itemSort').then(function(sort) {
      vm.itemSort = sort;
    });

    $rootScope.$on('dim-settings-updated', function(event, arg) {
      if (_.has(settings, 'itemSort')) {
        vm.itemSort = settings.itemSort;
      }
    });

    $scope.$watchCollection('vm.store.items', resetData);
  }
})();
<commit_msg>Fix changing sort not being applied.<commit_after>(function() {
  'use strict';

  angular.module('dimApp')
    .directive('dimStoreItems', StoreItems)
    .filter('equipped', function() {
      return function(items, isEquipped) {
        return _.select(items || [], function (item) {
          return item.equipped === isEquipped;
        });
      };
    })
    .filter('sortItems', function() {
      return function(items, sort) {
        return _(items || [])
              .chain()
              .sortBy('name')
              .sortBy(function(item) {
                if (sort === 'rarity') {
                  switch (item.tier) {
                  case 'Exotic':
                    return 0;
                  case 'Legendary':
                    return 1;
                  case 'Rare':
                    return 2;
                  case 'Uncommon':
                    return 3;
                  case 'Common':
                    return 4;
                  default:
                    return 5;
                  }
                } else {
                  return (item.primStat) ? (-1 * item.primStat.value) : 1000;
                }
              })
              .value();
      };
    });

  StoreItems.$inject = ['dimStoreService', '$window'];

  function StoreItems(dimStoreService, $window) {
    return {
      controller: StoreItemsCtrl,
      controllerAs: 'vm',
      bindToController: true,
      link: Link,
      replace: true,
      scope: {
        'store': '=storeData'
      },
      template: [
        '
',
        '
',
        '
',
        '
',
        '        {{ ::key }}',
        '        {{ vm.sortSize[key] ? vm.sortSize[key] : 0 }}/{{:: (key === \'Weapons\' || key === \'Armor\') ? 72 : 36 }}  ',
        '
',
        '
',
        '
',
        '

',
        '
',
        '
',
        '

',
        '

',
        '
',
        '
',
        '
',
        '
',
        '
'
      ].join('')
    };

    function Link(scope, element, attrs) {
      scope.vm.onDrop = function(id, $event, equip) {
        var vm = scope.vm;

        var srcElement = $('#' + id);

        vm.moveDroppedItem(angular.element(srcElement[0])
          .scope()
          .item, equip);
      };
    }
  }

  StoreItemsCtrl.$inject = ['$scope', '$rootScope', 'dimStoreService', 'dimItemService', '$q', '$timeout', 'toaster', 'dimSettingsService'];

  function StoreItemsCtrl($scope, $rootScope, dimStoreService, dimItemService, $q, $timeout, toaster, dimSettingsService) {
    var vm = this;

    var types = [ // Order of types in the rows.
      'Class',
      'Primary',
      'Special',
      'Heavy',
      'Helmet',
      'Gauntlets',
      'Chest',
      'Leg',
      'ClassItem',
      'Artifact',
      'Ghost',
      'Emblem',
      'Armor',
      'Ship',
      'Vehicle',
      'Horn',
      'Emote',
      'Consumable',
      'Material',
      'Missions',
      'Bounties',
      'Messages',
      'Special Orders',
      'Lost Items'
    ];
    vm.orderedTypes = {};

    _.each(types, function(value, index) {
      vm.orderedTypes[value] = index;
    });

    vm.sortSize = _.countBy(vm.store.items, 'sort');

    vm.categories = { // Grouping of the types in the rows.
      Weapons: [
        'Class',
        'Primary',
        'Special',
        'Heavy',
      ],
      Armor: [
        'Helmet',
        'Gauntlets',
        'Chest',
        'Leg',
        'ClassItem'
      ],
      General: [
        'Artifact',
        'Ghost',
        'Consumable',
        'Material',
        'Emblem',
        'Armor',
        'Emote',
        'Ship',
        'Vehicle',
        'Horn',
        'Missions',
        'Bounties'
      ],
      Postmaster: [
        'Messages',
        'Special Orders',
        'Lost Items'
      ]
    };

    vm.keys = _.keys(vm.categories);

    vm.styles = { // Styles of the types in the rows.
      Class: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Primary: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Special: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Heavy: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Helmet: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Gauntlets: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Chest: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Leg: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      ClassItem: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Artifact: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Emblem: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Armor: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Emote: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Ghost: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Ship: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Vehicle: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Horn: {
        equipped: 'equipped equippable',
        unequipped: 'unequipped equippable',
      },
      Consumable: {
        equipped: '',
        unequipped: 'unequipped equippable',
      },
      Material: {
        equipped: '',
        unequipped: 'unequipped equippable',
      },
      Messages: {
        equipped: '',
        unequipped: 'unequipped equippable',
      },
      'Special-Orders': {
        equipped: '',
        unequipped: 'unequipped equippable',
      },
      'Lost-Items': {
        equipped: '',
        unequipped: 'unequipped equippable',
      }
    };

    vm.moveDroppedItem = function(item, equip) {
      var promise = null;
      var target = vm.store;

      if (item.owner === vm.store.id) {
        if ((item.equipped && equip) || (!item.equipped) && (!equip)) {
          return;
        }

        promise = $q.when(vm.store);

      } else {
        promise = dimStoreService.getStore(item.owner);
      }

      var source;

      if (item.notransfer && item.owner !== target.id) {
        return $q.reject(new Error('Cannot move class to different store.'));
      }

      var dimStores = null;

      promise = promise
        .then(function(s) {
          source = s;
        })
        .then(dimItemService.moveTo.bind(null, item, target, equip))
        .then(dimStoreService.getStores)
        .then(function(stores) {
          dimStores = stores;
          return dimStoreService.updateStores();
        })
        .then(function(bungieStores) {
          _.each(dimStores, function(dStore) {
            if (dStore.id !== 'vault') {
              var bStore = _.find(bungieStores, function(bStore) {
                return dStore.id === bStore.id;
              });

              dStore.level = bStore.base.characterLevel;
              dStore.percentToNextLevel = bStore.base.percentToNextLevel;
              dStore.powerLevel = bStore.base.characterBase.powerLevel;
              dStore.background = bStore.base.backgroundPath;
              dStore.icon = bStore.base.emblemPath;
            }
          });
        })
        .catch(function(a) {
          toaster.pop('error', item.name, a.message);
        });

      $rootScope.loadingTracker.addPromise(promise);
    };

    function resetData() {
      if (vm.store.id === 'vault') {
        vm.sortSize = _.countBy(vm.store.items, 'sort');
      }

      vm.data = _.groupBy(vm.store.items, function(item) {
        return vm.orderedTypes[item.type];
      });
    }

    dimSettingsService.getSetting('itemSort').then(function(sort) {
      vm.itemSort = sort;
    });

    $scope.$on('dim-settings-updated', function(event, settings) {
      if (_.has(settings, 'itemSort')) {
        vm.itemSort = settings.itemSort;
      }
    });

    $scope.$watchCollection('vm.store.items', resetData);
  }
})();
<|endoftext|>


<commit_before>// A constructor for defining new cars
function Car(options) {
  // some defaults
  this.doors = options.doors || 4;
  this.state = options.state || "brand new";
  this.color = options.color || "silver";
}

// A constructor for defining new trucks
function Truck(options) {
  this.state = options.state || "used";
  this.wheelSize = options.wheelSize || "large";
  this.color = options.color || "blue";
}

// Define a skeleton vehicle factory
function VehicleFactory() {}

// Define the prototypes and utilities for this factory

// Our default vehicleClass is Car
VehicleFactory.prototype.vehicleClass = Car;

// Our Factory method for creating new Vehicle instances
VehicleFactory.prototype.createVehicle = function(options) {

  switch(options.vehicleType) {
    case "car":
      this.vehicleClass = Car;
      break;
    case "truck":
      this.vehicleClass = Truck;
      break;
    //defaults to VehicleFactory.prototype.vehicleClass (Car)
  }

  return new this.vehicleClass(options);
};

// Create an instance of our factory that makes cars
var carFactory = new VehicleFactory();
var car = carFactory.createVehicle({
            vehicleType: "car",
            color: "yellow",
            doors: 6 });

// Test to confirm our car was created using the vehicleClass/prototype Car

// Outputs: true
console.log(car instanceof Car);

// Outputs: Car object of color "yellow", doors: 6 in a "brand new" state
console.log(car);

var movingTruck = carFactory.createVehicle( {
                      vehicleType: "truck",
                      state: "like new",
                      color: "red",
                      wheelSize: "small" } );

// Test to confirm our truck was created with the vehicleClass/prototype Truck

// Outputs: true
console.log(movingTruck instanceof Truck);

// Outputs: Truck object of color "red", a "like new" state
// and a "small" wheelSize
console.log(movingTruck);

function TruckFactory () {}
TruckFactory.prototype = new VehicleFactory();
TruckFactory.prototype.vehicleClass = Truck;

var truckFactory = new TruckFactory();
var myBigTruck = truckFactory.createVehicle( {
                    state: "omg..so bad.",
                    color: "pink",
                    wheelSize: "so big" } );

// Confirms that myBigTruck was created with the prototype Truck
// Outputs: true
console.log(myBigTruck instanceof Truck);

// Outputs: Truck object with the color "pink", wheelSize "so big"
// and state "omg. so bad"
console.log(myBigTruck);
<commit_msg>Update factory pattern<commit_after>function CarDoor(options) {
  this.color = options.color || 'red';
  this.side = options.side || 'right';
  this.hasPowerWindows = options.hasPowerWindows || true;
}

function CarSeat(options) {
  this.color = options.color || 'gray';
  this.material = options.material || 'leather';
  this.isReclinable = options.isReclinable || true;
}

function CarPartFactory() {}
CarPartFactory.prototype.createPart = function createCarPart(options) {
  var parentClass = null;

  if (options.partType === 'door') {
    parentClass = CarDoor;
  } else if (options.partType === 'seat') {
    parentClass = CarSeat;
  }

  if (parentClass === null) {
    return false;
  }

  return new parentClass( options );
}

// example usage
var myPartFactory = new CarPartFactory();
var seat = myPartFactory.createPart({
  partType: 'seat',
  material: 'leather',
  color: 'blue',
  isReclinable: false
});

// outputs: true
console.log(seat instanceof CarSeat);

// outputs a CarSeat object with material "leather", color "blue", isReclinable "false"
console.log(seat);
<|endoftext|>


<commit_before>define([ 'ractive' ], function ( Ractive ) {

	'use strict';

	return function () {

		var fixture = document.getElementById( 'qunit-fixture' );

		module( 'elements' );

		test( 'option elements set select parent before initializing parent', function ( t ) {
			var ractive = new Ractive({
				el: fixture,
				template:
					'<select>' +
					'{{#options}}' +
					'<option value="{{.}}" selected="{{.===selected}}">{{.}}</option>' +
					'{{/options}}' +
					'</select>',
				data: {
					selected: 2,
					options: [1,2,3]
				}
			});

			t.equal( ractive.find('select').value , 2 );
		});

	};

});<commit_msg>clarify test title<commit_after>define([ 'ractive' ], function ( Ractive ) {

	'use strict';

	return function () {

		var fixture = document.getElementById( 'qunit-fixture' );

		module( 'elements' );

		test( 'option element with custom selected logic works without error and correctly', function ( t ) {
			var ractive = new Ractive({
				el: fixture,
				template:
					'<select>' +
					'{{#options}}' +
					'<option value="{{.}}" selected="{{.===selected}}">{{.}}</option>' +
					'{{/options}}' +
					'</select>',
				data: {
					selected: 2,
					options: [1,2,3]
				}
			});

			t.equal( ractive.find('select').value , 2 );
		});

	};

});<|endoftext|>


<commit_before>/*
 * Client connection
 */
var io = require('socket.io-client')
  , db = require('./db');

var url = process.argv[2]
  , channel = process.argv[3]
  , listen = url;

if(!url) {
  console.log('Please specify a url to listen to!');
  process.exit(1);
}

/*
 * Add logging to socket by overriding $emit function
 */
var addlog = function(socket) {
  var $emit = socket.$emit;
  socket.$emit = function() {
    if(arguments['0'] == 'message') {
      console.log(socket.name, Array.prototype.slice.call(arguments));
    }
    $emit.apply(socket, arguments);
  };
}

if(channel) { //if a channel is specified
  console.log('Listening to channel '+channel);
  listen += '/'+channel;
  addlog(io.connect(listen));
} else { // else listen to all channels
  console.log('No channel, listening to all channels');
  db.channels.on('load', function() { // when db is ready
    db.channels.forEach(function(key, val) { // loop through each channel
      addlog(io.connect(listen+'/'+key));
    });
  });
}

<commit_msg>Log all events not just 'message' ones<commit_after>/*
 * Client connection
 */
var io = require('socket.io-client')
  , db = require('./db');

var url = process.argv[2]
  , channel = process.argv[3]
  , listen = url;

if(!url) {
  console.log('Please specify a url to listen to!');
  process.exit(1);
}

/*
 * Add logging to socket by overriding $emit function
 */
var addlog = function(socket) {
  var $emit = socket.$emit;
  socket.$emit = function() {
    console.log(socket.name, Array.prototype.slice.call(arguments));
    $emit.apply(socket, arguments);
  };
}

if(channel) { //if a channel is specified
  console.log('Listening to channel '+channel);
  listen += '/'+channel;
  addlog(io.connect(listen));
} else { // else listen to all channels
  console.log('No channel, listening to all channels');
  db.channels.on('load', function() { // when db is ready
    db.channels.forEach(function(key, val) { // loop through each channel
      addlog(io.connect(listen+'/'+key));
    });
  });
}

<|endoftext|>


<commit_before>class ModularWidgets {
    static widgetTypes = [
        {
            type: 'etherpad',
            icon: 'http://localhost:8000/static/etherpad.svg',
            name: 'Etherpad',
            description: 'Collaborative text editor',
        },
        {
            type: 'grafana',
            icon: 'http://localhost:8000/static/grafana.svg',
            name: 'Grafana',
            description: 'Graph and monitor all the things!',
        },
        {
            type: 'jitsi',
            icon: 'http://localhost:8000/static/jitsi.png',
            name: 'jitsi',
            description: 'Jitsi video conference',
        },
        {
            type: 'vrdemo',
            icon: 'http://localhost:8000/static/jitsi.png',
            name: 'vrdemo',
            description: 'Matrix VR Demo',
        },
        {
            type: 'custom',
            icon: 'http://localhost:8000/static/blocks.png',
            name: 'Custom Widget',
            description: 'Add your own custom widget',
        },
    ];
}
export default ModularWidgets;
<commit_msg>Fix jitsi logo references<commit_after>class ModularWidgets {
    static widgetTypes = [
        {
            type: 'etherpad',
            icon: 'http://localhost:8000/static/etherpad.svg',
            name: 'Etherpad',
            description: 'Collaborative text editor',
        },
        {
            type: 'grafana',
            icon: 'http://localhost:8000/static/grafana.svg',
            name: 'Grafana',
            description: 'Graph and monitor all the things!',
        },
        {
            type: 'jitsi',
            icon: 'http://localhost:8000/static/jitsi.svg',
            name: 'jitsi',
            description: 'Jitsi video conference',
        },
        {
            type: 'vrdemo',
            icon: 'http://localhost:8000/static/jitsi.svg',
            name: 'vrdemo',
            description: 'Matrix VR Demo',
        },
        {
            type: 'custom',
            icon: 'http://localhost:8000/static/blocks.png',
            name: 'Custom Widget',
            description: 'Add your own custom widget',
        },
    ];
}
export default ModularWidgets;
<|endoftext|>


<commit_before>// some dependencies
var puts = require('sys').puts,
    connect = require('connect'),
    express = require('express');

// initialize the app and fire it up
var app = express.createServer()

// configuration
app.configure(function() {
    app.set("root", __dirname)
    app.use('/static', connect.staticProvider(__dirname + '/static'))
    app.use('/solvers', connect.staticProvider(__dirname + '/solvers'))
  })
app.configure('development', function(){
    app.enable('reload views', 5000)
  })

// routing
app.get('/', function(req, res){
    res.render('index.html.ejs')
  })
app.get('/problem', function(req,res){
    res.send(problem())
  })
app.post('/solution', function(req,res){
    res.send('got your solution for problem. thanks for helping out')
  })

// helper functions. TODO: move this out of here
function requiredParam(name, req) {
  var param = req.param(name)
  if(!param) throw 'Required parameter ' + name + ' is missing.'
  return param
}

// retreive a problem
function problem() {
  // TODO: load a problem from a persistent source
  return {id: '1', description: 'Add two numbers and send back the result', solver: 'adder', args: {a: 2, b: 3}}
}

var port = parseInt(process.env.PORT || 8000)
app.listen(port)
// log some useful details. TODO: replace this with real logging
puts('Workhorse running at http://<your_domain>:' + port)
puts('Environment is set to ' + app.set('env'))
<commit_msg>Removed unnecessary call to set the server's root directory.<commit_after>// some dependencies
var puts = require('sys').puts,
    connect = require('connect'),
    express = require('express');

// initialize the app and fire it up
var app = express.createServer()

// configuration
app.configure(function() {
    app.use('/static', connect.staticProvider(__dirname + '/static'))
    app.use('/solvers', connect.staticProvider(__dirname + '/solvers'))
  })
app.configure('development', function(){
    app.enable('reload views', 5000)
  })

// routing
app.get('/', function(req, res){
    res.render('index.html.ejs')
  })
app.get('/problem', function(req,res){
    res.send(problem())
  })
app.post('/solution', function(req,res){
    res.send('got your solution for problem. thanks for helping out')
  })

// helper functions. TODO: move this out of here
function requiredParam(name, req) {
  var param = req.param(name)
  if(!param) throw 'Required parameter ' + name + ' is missing.'
  return param
}

// retreive a problem
function problem() {
  // TODO: load a problem from a persistent source
  return {id: '1', description: 'Add two numbers and send back the result', solver: 'adder', args: {a: 2, b: 3}}
}

var port = parseInt(process.env.PORT || 8000)
app.listen(port)
// log some useful details. TODO: replace this with real logging
puts('Workhorse running at http://<your_domain>:' + port)
puts('Environment is set to ' + app.set('env'))
<|endoftext|>


<commit_before>(function() {
     function SongPlayer(Fixtures) {

           /**
          * @desc Hold the Buzz song object
          * @type {Object}
          */

          var SongPlayer = {};

          /**
          * @desc Album object from Fixtures
          * @type {Object}
          */

          var currentAlbum = Fixtures.getAlbum();

          /**
          * @desc Buzz object audio file
          * @type {Object}
          */

          var currentBuzzObject = null;

          /**
          * @function setSong
          * @desc Stops currently playing song and loads new audio file as currentBuzzObject
          * @param {Object} song
          */

          var setSong = function(song) {
            if (currentBuzzObject) {
                currentBuzzObject.stop();
                SongPlayer.currentSong.playing = null;
            }

            currentBuzzObject = new buzz.sound(song.audioUrl, {
                formats: ['mp3'],
                preload: true
            });
            SongPlayer.currentSong = song;
         };

          /**
          * @function playSong
          * @desc Plays loaded currentBuzzObject audio file and sets playing song property to true
          * @param {Object} song
          */

         var playSong = function(song){
            currentBuzzObject.play();
            song.playing = true;
         };

          /**
          * @function playSong
          * @desc Stops loaded currentBuzzObject audio file
          * @param {Object} song
          */

         var stopSong = function(song){
            currentBuzzObject.stop();
            song.playing = null;
         };

         /**
         * @desc Tracks the index of the song currently playing
         * @type {Object} song
         */

         var getSongIndex = function(song) {
            return currentAlbum.songs.indexOf(song);
         };

         /**
         * @desc Active song object from list of songs
         * @type {Object}
         */

          SongPlayer.currentSong = null;

         /**
          * @function .play
          * @desc Decides whether to set and play new song or play a paused song
          * @param {Object} song
          */

          SongPlayer.play = function(song){
            song = song || SongPlayer.currentSong;
            if (SongPlayer.currentSong !== song) {
                setSong(song);
                playSong(song);
            }else if (SongPlayer.currentSong === song) {
                if (currentBuzzObject.isPaused()) {
                    playSong(song);
                    }
             }else if(SongPlayer.currentSong === null){
                 playSong(currentAlbum.songs[0]);
             }
          };

         /**
          * @function .pause
          * @desc Pauses currently playing song
          * @param {Object} song
          */

          SongPlayer.pause = function(song) {
                song = song || SongPlayer.currentSong;
                currentBuzzObject.pause();
                song.playing = false;
          };

          /**
          * @function .previous
          * @desc Navigates to previous song on the album
          * @param {Object}
          */

          SongPlayer.previous = function() {
             var currentSongIndex = getSongIndex(SongPlayer.currentSong);
             currentSongIndex--;
             console.log(currentAlbum.artist);

            if (currentSongIndex < 0) {
                var song = currentAlbum.songs[currentAlbum.songs.length - 1];
                setSong(song);
                playSong(song);
            }else {
                    var song = currentAlbum.songs[currentSongIndex];
                    setSong(song);
                    playSong(song);
             }
          };

          /**
          * @function .next
          * @desc Navigates to next song on the album
          * @param {Object}
          */

          SongPlayer.next = function() {
            var currentSongIndex = getSongIndex(SongPlayer.currentSong);
            currentSongIndex++;

            if (currentSongIndex > (currentAlbum.songs.length -1)) {
                var song = currentAlbum.songs[0];
                setSong(song);
                playSong(song);
            }else{
                var song = currentAlbum.songs[currentSongIndex];
                setSong(song);
                playSong(song);
            }

        };

          return SongPlayer;
     }

     angular
         .module('blocJams')
         .factory('SongPlayer', ['Fixtures', SongPlayer]);
 })();<commit_msg>master edit post servicespt3<commit_after>(function() {
     function SongPlayer(Fixtures) {

           /**
          * @desc Hold the Buzz song object
          * @type {Object}
          */

          var SongPlayer = {};

          /**
          * @desc Album object from Fixtures
          * @type {Object}
          */

          var currentAlbum = Fixtures.getAlbum();

          /**
          * @desc Buzz object audio file
          * @type {Object}
          */

          var currentBuzzObject = null;

          /**
          * @function setSong
          * @desc Stops currently playing song and loads new audio file as currentBuzzObject
          * @param {Object} song
          */

          var setSong = function(song) {
            if (currentBuzzObject) {
                currentBuzzObject.stop();
                SongPlayer.currentSong.playing = null;
            }

            currentBuzzObject = new buzz.sound(song.audioUrl, {
                formats: ['mp3'],
                preload: true
            });
            SongPlayer.currentSong = song;
         };

          /**
          * @function playSong
          * @desc Plays loaded currentBuzzObject audio file and sets playing song property to true
          * @param {Object} song
          */

         var playSong = function(song){
            currentBuzzObject.play();
            song.playing = true;
         };

          /**
          * @function playSong
          * @desc Stops loaded currentBuzzObject audio file
          * @param {Object} song
          */

         var stopSong = function(song){
            currentBuzzObject.stop();
            song.playing = null;
         };

         /**
         * @desc Tracks the index of the song currently playing
         * @type {Object} song
         */

         var getSongIndex = function(song) {
            return currentAlbum.songs.indexOf(song);
         };

         /**
         * @desc Active song object from list of songs
         * @type {Object}
         */

          SongPlayer.currentSong = null;

         /**
          * @function .play
          * @desc Decides whether to set and play new song or play a paused song
          * @param {Object} song
          */

          SongPlayer.play = function(song){
            song = song || SongPlayer.currentSong;
            if (SongPlayer.currentSong !== song) {
                setSong(song);
                playSong(song);
            }else if (SongPlayer.currentSong === song) {
                if (currentBuzzObject.isPaused()) {
                    playSong(song);
                    }
             }
          };

         /**
          * @function .pause
          * @desc Pauses currently playing song
          * @param {Object} song
          */

          SongPlayer.pause = function(song) {
                song = song || SongPlayer.currentSong;
                currentBuzzObject.pause();
                song.playing = false;
          };

          /**
          * @function .previous
          * @desc Navigates to previous song on the album
          * @param {Object}
          */

          SongPlayer.previous = function() {
             var currentSongIndex = getSongIndex(SongPlayer.currentSong);
             currentSongIndex--;

            if (currentSongIndex < 0) {
                var song = currentAlbum.songs[currentAlbum.songs.length - 1];
                setSong(song);
                playSong(song);
            }else {
                    var song = currentAlbum.songs[currentSongIndex];
                    setSong(song);
                    playSong(song);
             }
          };

          /**
          * @function .next
          * @desc Navigates to next song on the album
          * @param {Object}
          */

          SongPlayer.next = function() {
            var currentSongIndex = getSongIndex(SongPlayer.currentSong);
            currentSongIndex++;

            if (currentSongIndex > (currentAlbum.songs.length -1)) {
                var song = currentAlbum.songs[0];
                setSong(song);
                playSong(song);
            }else{
                var song = currentAlbum.songs[currentSongIndex];
                setSong(song);
                playSong(song);
            }

        };

          return SongPlayer;
     }

     angular
         .module('blocJams')
         .factory('SongPlayer', ['Fixtures', SongPlayer]);
 })();<|endoftext|>


<commit_before>/*
 Copyright 2016 OpenMarket Ltd

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

'use strict';

import React from 'react';
import MFileBody from './MFileBody';

import MatrixClientPeg from '../../../MatrixClientPeg';
import sdk from '../../../index';

export default class MAudioBody extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            playing: false
        }
    }

    onPlayToggle() {
        this.setState({
            playing: !this.state.playing
        });
    }

    render() {
        var content = this.props.mxEvent.getContent();
        var cli = MatrixClientPeg.get();

        return (

                <audio src={cli.mxcUrlToHttp(content.url)} controls />
                <MFileBody {...this.props} />

        );
    }
}
<commit_msg>Decrypt m.audio attachments<commit_after>/*
 Copyright 2016 OpenMarket Ltd

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

'use strict';

import React from 'react';
import MFileBody from './MFileBody';

import MatrixClientPeg from '../../../MatrixClientPeg';
import sdk from '../../../index';
import { decryptFile } from '../../../utils/DecryptFile';

export default class MAudioBody extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            playing: false,
            decryptedUrl: null,
        }
    }
    onPlayToggle() {
        this.setState({
            playing: !this.state.playing
        });
    }

    _getContentUrl() {
        var content = this.props.mxEvent.getContent();
        if (content.file !== undefined) {
            return this.state.decryptedUrl;
        } else {
            return MatrixClientPeg.get().mxcUrlToHttp(content.url);
        }
    }

    componentDidMount() {
        var content = this.props.mxEvent.getContent();
        if (content.file !== undefined && this.state.decryptedUrl === null) {
            decryptFile(content.file).then((blob) => {
                if (!this._unmounted) {
                    this.setState({
                        decryptedUrl: window.URL.createObjectURL(blob),
                    });
                }
            }).catch((err) => {
                console.warn("Unable to decrypt attachment: ", err)
                // Set a placeholder image when we can't decrypt the image.
                this.refs.image.src = "img/warning.svg";
            });
        }
    }

    componentWillUnmount() {
        this._unmounted = true;
        if (this.state.decryptedUrl) {
            window.URL.revokeObjectURL(this.state.decryptedUrl);
        }
    }

    render() {
        var content = this.props.mxEvent.getContent();

        if (content.file !== undefined && this.state.decryptedUrl === null) {
            // Need to decrypt the attachment
            // The attachment is decrypted in componentDidMount.
            // For now add an img tag with a spinner.
            return (

            );
        }

        var contentUrl = this._getContentUrl();

        return (

                <audio src={contentUrl} controls />
                <MFileBody {...this.props} />

        );
    }
}
<|endoftext|>