<commit_before>'use strict';

var log = log4javascript.getLogger("credentialController-logger");

angular.module('uluwatuControllers').controller('credentialController', ['$scope', '$rootScope', '$filter', '$base64', 'UserCredential', 'AccountCredential', 'GlobalCredential', 'GlobalCredentialCertificate',
    function($scope, $rootScope, $filter, $base64, UserCredential, AccountCredential, GlobalCredential, GlobalCredentialCertificate) {
        $rootScope.credentials = AccountCredential.query();
        $scope.credentialInCreation = false;
        $scope.credentialAws = {};
        $scope.credentialAzure = {};
        $scope.credentialAzureRm = {};
        $scope.credentialGcp = {};
        $scope.credentialOpenstack = {};
        $scope.azureRmCredential = false;
        $scope.awsCredential = true;
        $scope.gcpCredential = false;
        $scope.openstackCredential = false;
        $scope.awsCredentialForm = {};
        $scope.gcpCredentialForm = {};
        $scope.azureCredentialForm = {};
        $scope.azureRmCredentialForm = {};
        $scope.openstackCredentialForm = {};
        $scope.gcp = {};
        $scope.gcp.p12 = "";
        $scope.showAlert = false;
        $scope.alertMessage = "";

        $scope.createAzureRmCredentialRequest = function() {
            $scope.awsCredential = false;
            $scope.gcpCredential = false;
            $scope.openstackCredential = false;
            $scope.azureRmCredential = true;
        }

        $scope.createAwsCredentialRequest = function() {
            $scope.awsCredential = true;
            $scope.gcpCredential = false;
            $scope.openstackCredential = false;
            $scope.azureRmCredential = false;
        }

        $scope.createGcpCredentialRequest = function() {
            $scope.awsCredential = false;
            $scope.gcpCredential = true;
            $scope.openstackCredential = false;
            $scope.azureRmCredential = false;
        }

        $scope.createOpenstackCredentialRequest = function() {
            $scope.awsCredential = false;
            $scope.gcpCredential = false;
            $scope.openstackCredential = true;
            $scope.azureRmCredential = false;
        }

        $scope.refreshCertificateFile = function(credentialId) {
            GlobalCredentialCertificate.update({
                id: credentialId
            }, {}, function(result) {
                window.location.href = "credentials/certificate/" + credentialId;
            });
        }

        $scope.createAwsCredential = function() {
            $scope.credentialAws.cloudPlatform = "AWS";
            $scope.credentialInCreation = true;

            if ($scope.credentialAws.public) {
                AccountCredential.save($scope.credentialAws, function(result) {
                    handleAwsCredentialSuccess(result);
                }, function(error) {
                    $scope.showError(error, $rootScope.msg.aws_credential_failed);
                    $scope.credentialInCreation = false;
                    $scope.showErrorMessageAlert();
                });
            } else {
                UserCredential.save($scope.credentialAws, function(result) {
                    handleAwsCredentialSuccess(result);
                }, function(error) {
                    $scope.showError(error, $rootScope.msg.aws_credential_failed);
                    $scope.credentialInCreation = false;
                    $scope.showErrorMessageAlert();
                });
            }

            function handleAwsCredentialSuccess(result) {
                $scope.credentialAws.id = result.id;
                $rootScope.credentials.push($scope.credentialAws);
                $scope.credentialAws = {};
                $scope.showSuccess($filter("format")($rootScope.msg.aws_credential_success, String(result.id)));
                $scope.awsCredentialForm.$setPristine();
                collapseCreateCredentialFormPanel();
                $scope.credentialInCreation = false;
                $scope.unShowErrorMessageAlert();
            }
        }

        $scope.createOpenstackCredential = function() {
            $scope.credentialOpenstack.cloudPlatform = "OPENSTACK";
            $scope.credentialInCreation = true;
            if ($scope.credentialOpenstack.parameters.keystoneVersion === "cb-keystone-v2") {
                $scope.credentialOpenstack.parameters.selector = $scope.credentialOpenstack.parameters.keystoneVersion;
            } else {
                $scope.credentialOpenstack.parameters.selector = $scope.credentialOpenstack.parameters.keystoneAuthScope;
            }
            delete $scope.credentialOpenstack.parameters.keystoneVersion;
            delete $scope.credentialOpenstack.parameters.keystoneAuthScope;

            if ($scope.credentialOpenstack.public) {
                AccountCredential.save($scope.credentialOpenstack, handleOpenstackCredentialSuccess, handleOpenstackCredentailCreationError);
            } else {
                UserCredential.save($scope.credentialOpenstack, handleOpenstackCredentialSuccess, handleOpenstackCredentailCreationError);
            }

            function handleOpenstackCredentialSuccess(result) {
                $scope.credentialOpenstack.id = result.id;
                $rootScope.credentials.push($scope.credentialOpenstack);
                $scope.credentialOpenstack = {parameters:{keystoneVersion:"cb-keystone-v2"}};
                $scope.showSuccess($filter("format")($rootScope.msg.openstack_credential_success, String(result.id)));
                $scope.openstackCredentialForm.$setPristine();
                collapseCreateCredentialFormPanel();
                $scope.credentialInCreation = false;
                $scope.unShowErrorMessageAlert();
            }

            function handleOpenstackCredentailCreationError(error) {
                if ($scope.credentialOpenstack.parameters.selector === "cb-keystone-v2") {
                    $scope.credentialOpenstack.parameters.keystoneVersion = $scope.credentialOpenstack.parameters.selector;
                } else {
                    $scope.credentialOpenstack.parameters.keystoneVersion = "cb-keystone-v3";
                    $scope.credentialOpenstack.parameters.keystoneAuthScope = $scope.credentialOpenstack.parameters.selector;
                }
                delete $scope.credentialOpenstack.parameters.selector;
                $scope.showError(error, $rootScope.msg.openstack_credential_failed);
                $scope.credentialInCreation = false;
                $scope.showErrorMessageAlert();
            }
        }

        $scope.createAzureRmCredential = function() {
            $scope.credentialAzureRm.cloudPlatform = "AZURE_RM";
            $scope.credentialInCreation = true;

            if ($scope.credentialAzureRm.public) {
                AccountCredential.save($scope.credentialAzureRm, function(result) {
                    handleAzureRmCredentialSuccess(result)
                }, function(error) {
                    $scope.showError(error, $rootScope.msg.azure_rm_credential_failed);
                    $scope.credentialInCreation = false;
                    $scope.showErrorMessageAlert();
                });
            } else {
                UserCredential.save($scope.credentialAzureRm, function(result) {
                    handleAzureRmCredentialSuccess(result)
                }, function(error) {
                    $scope.showError(error, $rootScope.msg.azure_rm_credential_failed);
                    $scope.credentialInCreation = false;
                    $scope.showErrorMessageAlert();
                });
            }

            function handleAzureRmCredentialSuccess(result) {
                $scope.credentialAzureRm.id = result.id;
                $rootScope.credentials.push($scope.credentialAzureRm);
                $scope.credentialAzureRm = {};
                $scope.showSuccess($filter("format")($rootScope.msg.azure_credential_success, result.id));
                $scope.azureRmCredentialForm.$setPristine();
                collapseCreateCredentialFormPanel();
                $scope.unShowErrorMessageAlert();
            }
        }

        $scope.createGcpCredential = function() {
            $scope.credentialGcp.cloudPlatform = "GCP";
            $scope.credentialInCreation = true

            var p12File = $scope.gcp.p12
            var reader = new FileReader();

            reader.onloadend = function(evt) {
                if (evt.target.readyState == FileReader.DONE) {
                    $scope.credentialGcp.parameters.serviceAccountPrivateKey = $base64.encode(evt.target.result);

                    if ($scope.credentialGcp.public) {
                        AccountCredential.save($scope.credentialGcp, function(result) {
                            handleGcpCredentialSuccess(result)
                        }, function(error) {
                            $scope.showError(error, $rootScope.msg.gcp_credential_failed);
                            $scope.credentialInCreation = false;
                            $scope.showErrorMessageAlert();
                        });
                    } else {
                        UserCredential.save($scope.credentialGcp, function(result) {
                            handleGcpCredentialSuccess(result)
                        }, function(error) {
                            $scope.showError(error, $rootScope.msg.gcp_credential_failed);
                            $scope.credentialInCreation = false;
                            $scope.showErrorMessageAlert();
                        });
                    }
                }
            };

            function handleGcpCredentialSuccess(result) {
                $scope.credentialGcp.id = result.id;
                $rootScope.credentials.push($scope.credentialGcp);
                $scope.credentialGcp = {};
                $scope.showSuccess($filter("format")($rootScope.msg.gcp_credential_success, result.id));
                $scope.credentialInCreation = false;
                $scope.gcpCredentialForm.$setPristine();
                collapseCreateCredentialFormPanel();
                $scope.unShowErrorMessageAlert();
            }

            var blob = p12File.slice(0, p12File.size);
            reader.readAsBinaryString(blob);

        }

        $scope.deleteCredential = function(credential) {
            GlobalCredential.delete({
                id: credential.id
            }, function(success) {
                $rootScope.credentials.splice($rootScope.credentials.indexOf(credential), 1);
                $scope.showSuccess($filter("format")($rootScope.msg.credential_delete_success, credential.id));
            }, function(error) {
                $scope.showError(error);
            });

        }

        $scope.unShowErrorMessageAlert = function() {
            $scope.showAlert = false;
            $scope.alertMessage = "";
        }

        $scope.showErrorMessageAlert = function() {
            $scope.showAlert = true;
            $scope.alertMessage = $scope.statusMessage;
        }

        function collapseCreateCredentialFormPanel() {
            angular.element(document.querySelector('#panel-create-credentials-collapse-btn')).click();
        }
    }
]);<commit_msg>add back keystone version and authscope fields to os credentail creation<commit_after>'use strict';

var log = log4javascript.getLogger("credentialController-logger");

angular.module('uluwatuControllers').controller('credentialController', ['$scope', '$rootScope', '$filter', '$base64', 'UserCredential', 'AccountCredential', 'GlobalCredential', 'GlobalCredentialCertificate',
    function($scope, $rootScope, $filter, $base64, UserCredential, AccountCredential, GlobalCredential, GlobalCredentialCertificate) {
        $rootScope.credentials = AccountCredential.query();
        $scope.credentialInCreation = false;
        $scope.credentialAws = {};
        $scope.credentialAzure = {};
        $scope.credentialAzureRm = {};
        $scope.credentialGcp = {};
        $scope.credentialOpenstack = {};
        $scope.azureRmCredential = false;
        $scope.awsCredential = true;
        $scope.gcpCredential = false;
        $scope.openstackCredential = false;
        $scope.awsCredentialForm = {};
        $scope.gcpCredentialForm = {};
        $scope.azureCredentialForm = {};
        $scope.azureRmCredentialForm = {};
        $scope.openstackCredentialForm = {};
        $scope.gcp = {};
        $scope.gcp.p12 = "";
        $scope.showAlert = false;
        $scope.alertMessage = "";

        $scope.createAzureRmCredentialRequest = function() {
            $scope.awsCredential = false;
            $scope.gcpCredential = false;
            $scope.openstackCredential = false;
            $scope.azureRmCredential = true;
        }

        $scope.createAwsCredentialRequest = function() {
            $scope.awsCredential = true;
            $scope.gcpCredential = false;
            $scope.openstackCredential = false;
            $scope.azureRmCredential = false;
        }

        $scope.createGcpCredentialRequest = function() {
            $scope.awsCredential = false;
            $scope.gcpCredential = true;
            $scope.openstackCredential = false;
            $scope.azureRmCredential = false;
        }

        $scope.createOpenstackCredentialRequest = function() {
            $scope.awsCredential = false;
            $scope.gcpCredential = false;
            $scope.openstackCredential = true;
            $scope.azureRmCredential = false;
        }

        $scope.refreshCertificateFile = function(credentialId) {
            GlobalCredentialCertificate.update({
                id: credentialId
            }, {}, function(result) {
                window.location.href = "credentials/certificate/" + credentialId;
            });
        }

        $scope.createAwsCredential = function() {
            $scope.credentialAws.cloudPlatform = "AWS";
            $scope.credentialInCreation = true;

            if ($scope.credentialAws.public) {
                AccountCredential.save($scope.credentialAws, function(result) {
                    handleAwsCredentialSuccess(result);
                }, function(error) {
                    $scope.showError(error, $rootScope.msg.aws_credential_failed);
                    $scope.credentialInCreation = false;
                    $scope.showErrorMessageAlert();
                });
            } else {
                UserCredential.save($scope.credentialAws, function(result) {
                    handleAwsCredentialSuccess(result);
                }, function(error) {
                    $scope.showError(error, $rootScope.msg.aws_credential_failed);
                    $scope.credentialInCreation = false;
                    $scope.showErrorMessageAlert();
                });
            }

            function handleAwsCredentialSuccess(result) {
                $scope.credentialAws.id = result.id;
                $rootScope.credentials.push($scope.credentialAws);
                $scope.credentialAws = {};
                $scope.showSuccess($filter("format")($rootScope.msg.aws_credential_success, String(result.id)));
                $scope.awsCredentialForm.$setPristine();
                collapseCreateCredentialFormPanel();
                $scope.credentialInCreation = false;
                $scope.unShowErrorMessageAlert();
            }
        }

        $scope.createOpenstackCredential = function() {
            $scope.credentialOpenstack.cloudPlatform = "OPENSTACK";
            $scope.credentialInCreation = true;
            if ($scope.credentialOpenstack.parameters.keystoneVersion === "cb-keystone-v2") {
                $scope.credentialOpenstack.parameters.selector = $scope.credentialOpenstack.parameters.keystoneVersion;
            } else {
                $scope.credentialOpenstack.parameters.selector = $scope.credentialOpenstack.parameters.keystoneAuthScope;
            }

            if ($scope.credentialOpenstack.public) {
                AccountCredential.save($scope.credentialOpenstack, handleOpenstackCredentialSuccess, handleOpenstackCredentailCreationError);
            } else {
                UserCredential.save($scope.credentialOpenstack, handleOpenstackCredentialSuccess, handleOpenstackCredentailCreationError);
            }

            function handleOpenstackCredentialSuccess(result) {
                $scope.credentialOpenstack.id = result.id;
                $rootScope.credentials.push($scope.credentialOpenstack);
                $scope.credentialOpenstack = {parameters:{keystoneVersion:"cb-keystone-v2"}};
                $scope.showSuccess($filter("format")($rootScope.msg.openstack_credential_success, String(result.id)));
                $scope.openstackCredentialForm.$setPristine();
                collapseCreateCredentialFormPanel();
                $scope.credentialInCreation = false;
                $scope.unShowErrorMessageAlert();
            }

            function handleOpenstackCredentailCreationError(error) {
                $scope.showError(error, $rootScope.msg.openstack_credential_failed);
                $scope.credentialInCreation = false;
                $scope.showErrorMessageAlert();
            }
        }

        $scope.createAzureRmCredential = function() {
            $scope.credentialAzureRm.cloudPlatform = "AZURE_RM";
            $scope.credentialInCreation = true;

            if ($scope.credentialAzureRm.public) {
                AccountCredential.save($scope.credentialAzureRm, function(result) {
                    handleAzureRmCredentialSuccess(result)
                }, function(error) {
                    $scope.showError(error, $rootScope.msg.azure_rm_credential_failed);
                    $scope.credentialInCreation = false;
                    $scope.showErrorMessageAlert();
                });
            } else {
                UserCredential.save($scope.credentialAzureRm, function(result) {
                    handleAzureRmCredentialSuccess(result)
                }, function(error) {
                    $scope.showError(error, $rootScope.msg.azure_rm_credential_failed);
                    $scope.credentialInCreation = false;
                    $scope.showErrorMessageAlert();
                });
            }

            function handleAzureRmCredentialSuccess(result) {
                $scope.credentialAzureRm.id = result.id;
                $rootScope.credentials.push($scope.credentialAzureRm);
                $scope.credentialAzureRm = {};
                $scope.showSuccess($filter("format")($rootScope.msg.azure_credential_success, result.id));
                $scope.azureRmCredentialForm.$setPristine();
                collapseCreateCredentialFormPanel();
                $scope.unShowErrorMessageAlert();
            }
        }

        $scope.createGcpCredential = function() {
            $scope.credentialGcp.cloudPlatform = "GCP";
            $scope.credentialInCreation = true

            var p12File = $scope.gcp.p12
            var reader = new FileReader();

            reader.onloadend = function(evt) {
                if (evt.target.readyState == FileReader.DONE) {
                    $scope.credentialGcp.parameters.serviceAccountPrivateKey = $base64.encode(evt.target.result);

                    if ($scope.credentialGcp.public) {
                        AccountCredential.save($scope.credentialGcp, function(result) {
                            handleGcpCredentialSuccess(result)
                        }, function(error) {
                            $scope.showError(error, $rootScope.msg.gcp_credential_failed);
                            $scope.credentialInCreation = false;
                            $scope.showErrorMessageAlert();
                        });
                    } else {
                        UserCredential.save($scope.credentialGcp, function(result) {
                            handleGcpCredentialSuccess(result)
                        }, function(error) {
                            $scope.showError(error, $rootScope.msg.gcp_credential_failed);
                            $scope.credentialInCreation = false;
                            $scope.showErrorMessageAlert();
                        });
                    }
                }
            };

            function handleGcpCredentialSuccess(result) {
                $scope.credentialGcp.id = result.id;
                $rootScope.credentials.push($scope.credentialGcp);
                $scope.credentialGcp = {};
                $scope.showSuccess($filter("format")($rootScope.msg.gcp_credential_success, result.id));
                $scope.credentialInCreation = false;
                $scope.gcpCredentialForm.$setPristine();
                collapseCreateCredentialFormPanel();
                $scope.unShowErrorMessageAlert();
            }

            var blob = p12File.slice(0, p12File.size);
            reader.readAsBinaryString(blob);

        }

        $scope.deleteCredential = function(credential) {
            GlobalCredential.delete({
                id: credential.id
            }, function(success) {
                $rootScope.credentials.splice($rootScope.credentials.indexOf(credential), 1);
                $scope.showSuccess($filter("format")($rootScope.msg.credential_delete_success, credential.id));
            }, function(error) {
                $scope.showError(error);
            });

        }

        $scope.unShowErrorMessageAlert = function() {
            $scope.showAlert = false;
            $scope.alertMessage = "";
        }

        $scope.showErrorMessageAlert = function() {
            $scope.showAlert = true;
            $scope.alertMessage = $scope.statusMessage;
        }

        function collapseCreateCredentialFormPanel() {
            angular.element(document.querySelector('#panel-create-credentials-collapse-btn')).click();
        }
    }
]);<|endoftext|>


<commit_before>var public_spreadsheet_url =                'https://docs.google.com/spreadsheets/d/1QXOUoGM93i4dt5XH8QbfS7TG8A00v5b-18Few3KcqfM/pubhtml';

function InitializeTabletop()
{
    Tabletop.init( { key: public_spreadsheet_url,
                     callback: showInfo,
                     simpleSheet: true } );
}

function showInfo(data, tabletop)
{
    //alert("Successfully processed!")
    console.log(data);
    AddSkillsToPage(data);
    //alert(data[0].Skill);
}

function CreateModule (skill, iconLink, blurb)
{
    	return "

"+skill+"

"
}

function AddSkillsToPage(data)
{
    for(i = 0; i < data.length; i++)
    {
        var module = CreateModule(data[i].Skill, data[i].IconLink, data[i].Blurb);
        $('.autowide').append(module);
    }

    //Add clicks to all modules
    $('.module').click(ModuleClick);

}

function ModuleClick(event)
{
        console.log('click');
        swal({
            title: "

"+$(this).attr('data-skill')+"
",
            text: $(this).attr('data-blurb'),
            html: true,
            allowOutsideClick: true
        });
}
HIGHLIGHTS BY 0
<commit_msg>Update wp-skills.js<commit_after>var public_spreadsheet_url =                'https://docs.google.com/spreadsheets/d/1QXOUoGM93i4dt5XH8QbfS7TG8A00v5b-18Few3KcqfM/pubhtml';

function InitializeTabletop()
{
    Tabletop.init( { key: public_spreadsheet_url,
                     callback: showInfo,
                     simpleSheet: true } );
}

function showInfo(data, tabletop)
{
    //alert("Successfully processed!")
    console.log(data);
    AddSkillsToPage(data);
    //alert(data[0].Skill);
}

function CreateModule (skill, iconLink, blurb)
{
    	return "

"+skill+"

"
}

function AddSkillsToPage(data)
{
    for(i = 0; i < data.length; i++)
    {
        var module = CreateModule(data[i].Skill, data[i].IconLink, data[i].Blurb);
        $('.autowide').append(module);
    }

    //Add clicks to all modules
    $('.module').click(ModuleClick);

}

function ModuleClick(event)
{
        console.log('click');
        swal({
            title: "

"+$(this).attr('data-skill')+"
",
            text: $(this).attr('data-blurb'),
            html: true,
            allowOutsideClick: true
        });
}
<|endoftext|>


<commit_before>/*!
 * jQuery Accordion 0.0.1
 * (c) 2014 Victor Fernandez <victor@vctrfrnndz.com>
 * MIT Licensed.
 */

;(function ( $, window, document, undefined ) {

    var pluginName = 'accordion',
        defaults = {
            transitionSpeed: 300,
            transitionEasing: 'ease',
            controlElement: '[data-control]',
            contentElement: '[data-content]',
            groupElement: '[data-accordion-group]',
            singleOpen: true
        };

    function Accordion(element, options) {
        this.element = element;
        this.options = $.extend({}, defaults, options);
        this._defaults = defaults;
        this._name = pluginName;
        this.init();
    }

    Accordion.prototype.init = function () {
        var self = this,
            opts = self.options;

        var $accordion = $(self.element),
            $content =  $accordion.find('> ' + opts.contentElement);

        function toggleTransition($el, remove) {
            if(!remove) {
                $content.css({
                    '-webkit-transition': 'max-height ' + opts.transitionSpeed + 'ms ' + opts.transitionEasing,
                    'transition': 'max-height ' + opts.transitionSpeed + 'ms ' + opts.transitionEasing
                });
            } else {
                $content.css({
                    '-webkit-transition': '',
                    'transition': ''
                });
            }
        }

        function calculateHeight($el) {
            var height = 0;

            $el.children().each(function() {
                height = height + $(this).outerHeight();
            });

            $el.data('oHeight', height);
        }

        function toggleAccordion() {
            var isAccordionGroup = (opts.singleOpen) ? $accordion.parents(opts.groupElement).length > 0 : false;

            var closedCSS = { 'max-height': 0, 'overflow': 'hidden' };

            calculateHeight($content);

            toggleTransition($content);

            if(isAccordionGroup) {
                var $accordionGroup = $accordion.closest('[data-accordion-group]'),
                    $siblings = $accordion.siblings('[data-accordion]'),
                    $siblingsChildren = $siblings.find('[data-accordion]'),
                    $otherAccordions = $siblings.add($siblingsChildren);

                $otherAccordions.each(function() {
                    var $content = $(this).find('[data-content]');

                    $content.css('max-height', $content.data('oHeight'));

                    setTimeout(function() {
                        $content.css(closedCSS);
                    }, 0.1);
                });

                $otherAccordions.removeClass('open');
            }

            $content.promise().done(function() {
                if($accordion.hasClass('open')) {
                    if($content.css('max-height') === 'none') {
                        $content.css('max-height', $content.data('oHeight'));

                        setTimeout(function() {
                            $content.css(closedCSS);
                        }, 0.1);
                    } else {
                        $content.css(closedCSS);
                    }

                    $accordion.removeClass('open');
                } else {
                    $content.css('max-height', $content.data('oHeight'));
                    $accordion.addClass('open');

                    setTimeout(function() {
                        $content.css('max-height', 'none');
                    }, opts.transitionSpeed);
                }
            });
        }

        function addEventListeners() {
            $accordion.on('click', '> '+ opts.controlElement, toggleAccordion);
        }

        function checkIfCSSisSetup() {
            if($content.css('max-height') != 0) {
                $(opts.contentElement).css({ 'max-height': 0, 'overflow': 'hidden' });

                return false;
            }

            return true;
        }

        checkIfCSSisSetup();
        addEventListeners();
    };

    $.fn[pluginName] = function ( options ) {
        return this.each(function () {
            if (!$.data(this, 'plugin_' + pluginName)) {
                $.data(this, 'plugin_' + pluginName,
                new Accordion( this, options ));
            }
        });
    }

})( jQuery, window, document );<commit_msg>Fix references to options<commit_after>/*!
 * jQuery Accordion 0.0.1
 * (c) 2014 Victor Fernandez <victor@vctrfrnndz.com>
 * MIT Licensed.
 */

;(function ( $, window, document, undefined ) {

    var pluginName = 'accordion',
        defaults = {
            transitionSpeed: 300,
            transitionEasing: 'ease',
            controlElement: '[data-control]',
            contentElement: '[data-content]',
            groupElement: '[data-accordion-group]',
            singleOpen: true
        };

    function Accordion(element, options) {
        this.element = element;
        this.options = $.extend({}, defaults, options);
        this._defaults = defaults;
        this._name = pluginName;
        this.init();
    }

    Accordion.prototype.init = function () {
        var self = this,
            opts = self.options;

        var $accordion = $(self.element),
            $content =  $accordion.find('> ' + opts.contentElement);

        function toggleTransition($el, remove) {
            if(!remove) {
                $content.css({
                    '-webkit-transition': 'max-height ' + opts.transitionSpeed + 'ms ' + opts.transitionEasing,
                    'transition': 'max-height ' + opts.transitionSpeed + 'ms ' + opts.transitionEasing
                });
            } else {
                $content.css({
                    '-webkit-transition': '',
                    'transition': ''
                });
            }
        }

        function calculateHeight($el) {
            var height = 0;

            $el.children().each(function() {
                height = height + $(this).outerHeight();
            });

            $el.data('oHeight', height);
        }

        function toggleAccordion() {
            var isAccordionGroup = (opts.singleOpen) ? $accordion.parents(opts.groupElement).length > 0 : false;

            var closedCSS = { 'max-height': 0, 'overflow': 'hidden' };

            calculateHeight($content);

            toggleTransition($content);

            if(isAccordionGroup) {
                var $accordionGroup = $accordion.closest(opts.groupElement),
                    $siblings = $accordion.siblings(self),
                    $siblingsChildren = $siblings.find(self),
                    $otherAccordions = $siblings.add($siblingsChildren);

                $otherAccordions.each(function() {
                    var $content = $(this).find(opts.contentElement);

                    $content.css('max-height', $content.data('oHeight'));

                    setTimeout(function() {
                        $content.css(closedCSS);
                    }, 0.1);
                });

                $otherAccordions.removeClass('open');
            }

            $content.promise().done(function() {
                if($accordion.hasClass('open')) {
                    if($content.css('max-height') === 'none') {
                        $content.css('max-height', $content.data('oHeight'));

                        setTimeout(function() {
                            $content.css(closedCSS);
                        }, 0.1);
                    } else {
                        $content.css(closedCSS);
                    }

                    $accordion.removeClass('open');
                } else {
                    $content.css('max-height', $content.data('oHeight'));
                    $accordion.addClass('open');

                    setTimeout(function() {
                        $content.css('max-height', 'none');
                    }, opts.transitionSpeed);
                }
            });
        }

        function addEventListeners() {
            $accordion.on('click', '> '+ opts.controlElement, toggleAccordion);
        }

        function checkIfCSSisSetup() {
            if($content.css('max-height') != 0) {
                $(opts.contentElement).css({ 'max-height': 0, 'overflow': 'hidden' });

                return false;
            }

            return true;
        }

        checkIfCSSisSetup();
        addEventListeners();
    };

    $.fn[pluginName] = function ( options ) {
        return this.each(function () {
            if (!$.data(this, 'plugin_' + pluginName)) {
                $.data(this, 'plugin_' + pluginName,
                new Accordion( this, options ));
            }
        });
    }

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


<commit_before>!function($, Foundation, window){
  /**
   * Compares the dimensions of an element to a container and determines collision events with container.
   * @function
   * @param {jQuery} element - jQuery object to test for collisions.
   * @param {jQuery} parent - jQuery object to use as bounding container.
   * @param {Boolean} lrOnly - set to true to check left and right values only.
   * @param {Boolean} tbOnly - set to true to check top and bottom values only.
   * @default if no parent object passed, detects collisions with `window`.
   * @returns {Boolean} - true if collision free, false if a collision in any direction.
   */
  var ImNotTouchingYou = function(element, parent, lrOnly, tbOnly){
    var eleDims = GetDimensions(element),
    // var eleDims = GetDimensions(element),
        top, bottom, left, right;

    if(parent){
      // var parDims = GetDimensions(parent);
      var parDims = GetDimensions(parent);

      bottom = (eleDims.offset.top + eleDims.height <= parDims.height + parDims.offset.top);
      top    = (eleDims.offset.top >= parDims.offset.top);
      left   = (eleDims.offset.left >= parDims.offset.left);
      right  = (eleDims.offset.left + eleDims.width <= parDims.width);
    }else{
      bottom = (eleDims.offset.top + eleDims.height <= eleDims.windowDims.height + eleDims.windowDims.offset.top);
      top    = (eleDims.offset.top >= eleDims.windowDims.offset.top);
      left   = (eleDims.offset.left >= eleDims.windowDims.offset.left);
      right  = (eleDims.offset.left + eleDims.width <= eleDims.windowDims.width);
    }
    var allDirs = [bottom, top, left, right];

    if(lrOnly){ return left === right === true; }
    if(tbOnly){ return top === bottom === true; }

    return allDirs.indexOf(false) === -1;
  };

  // /**
  //  * Uses jQuery methods to return an object of dimension values.
  //  * @function
  //  * @param {jQuery} element - jQuery object for which to get the dimensions.
  //  * @returns {Object} - nested object of integer pixel values
  //  * TODO - if element is window, return only those values.
  //  */
  // function GetDimensions(element){
  //   // GetDimensions(element, element[0]);
  //   var $window = $(window);
  //   var a =  {
  //     width: element.outerWidth(),
  //     height: element.outerHeight(),
  //     offset: element.offset(),
  //     parentDims: {
  //       width: element.parent().outerWidth(),
  //       height: element.parent().outerHeight(),
  //       offset: element.parent().offset()
  //     },
  //     windowDims: {
  //       width: $window.width(),
  //       height: $window.height(),
  //       offset: {
  //         top: $window.scrollTop(),
  //         left: $window.scrollLeft()
  //       }
  //     }
  //   };
  //   return a;
  // }
  /**
   * Uses native methods to return an object of dimension values.
   * @function
   * @param {jQuery || HTML} element - jQuery object or DOM element for which to get the dimensions.
   * @returns {Object} - nested object of integer pixel values
   * TODO - if element is window, return only those values.
   */
  var GetDimensions = function(elem, test){
    elem = elem.length ? elem[0] : elem;
    var rect = elem.getBoundingClientRect(),
        parRect = elem.parentNode.getBoundingClientRect(),
        winRect = document.body.getBoundingClientRect(),
        winY = window.pageYOffset,
        winX = window.pageXOffset;

    return {
      width: rect.width,
      height: rect.height,
      offset: {
        top: rect.top + winY,
        left: rect.left + winX
      },
      parentDims: {
        width: parRect.width,
        height: parRect.height,
        offset: {
          top: parRect.top + winY,
          left: parRect.left + winX
        }
      },
      windowDims: {
        width: winRect.width,
        height: winRect.height,
        offset: {
          top: winY,
          left: winX
        }
      }
    };
  };
  /**
   * Returns an object of top and left integer pixel values for dynamically rendered elements,
   * such as: Tooltip, Reveal, and Dropdown
   * @function
   * @param {jQuery} element - jQuery object for the element being positioned.
   * @param {jQuery} anchor - jQuery object for the element's anchor point.
   * @param {String} position - a string relating to the desired position of the element, relative to it's anchor
   * @param {Number} vOffset - integer pixel value of desired vertical separation between anchor and element.
   * @param {Number} hOffset - integer pixel value of desired horizontal separation between anchor and element.
   * @param {Boolean} isOverflow - if a collision event is detected, sets to true to default the element to full width - any desired offset.
   * TODO alter/rewrite to work with `em` values as well/instead of pixels
   */
  var GetOffsets = function(element, anchor, position, vOffset, hOffset, isOverflow){
    var $eleDims = GetDimensions(element),
    // var $eleDims = GetDimensions(element),
        $anchorDims = anchor ? GetDimensions(anchor) : null;
        // $anchorDims = anchor ? GetDimensions(anchor) : null;
    switch(position){
      case 'top':
        return {
          left: $anchorDims.offset.left,
          top: $anchorDims.offset.top - ($eleDims.height + vOffset)
        };
        break;
      case 'left':
        return {
          left: $anchorDims.offset.left - ($eleDims.width + hOffset),
          top: $anchorDims.offset.top
        };
        break;
      case 'right':
        return {
          left: $anchorDims.offset.left + $anchorDims.width + hOffset,
          top: $anchorDims.offset.top
        };
        break;
      case 'center top':
        return {
          left: ($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2),
          top: $anchorDims.offset.top - ($eleDims.height + vOffset)
        };
        break;
      case 'center bottom':
        return {
          left: isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)),
          top: $anchorDims.offset.top + $anchorDims.height + vOffset
        };
        break;
      case 'center left':
        return {
          left: $anchorDims.offset.left - ($eleDims.width + hOffset),
          top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2)
        };
        break;
      case 'center right':
        return {
          left: $anchorDims.offset.left + $anchorDims.width + hOffset + 1,
          top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2)
        };
        break;
      case 'center':
        return {
          left: ($eleDims.windowDims.offset.left + ($eleDims.windowDims.width / 2)) - ($eleDims.width / 2),
          top: ($eleDims.windowDims.offset.top + ($eleDims.windowDims.height / 2)) - ($eleDims.height / 2)
        };
        break;
      case 'reveal':
        return {
          left: ($eleDims.windowDims.width - $eleDims.width) / 2,
          top: $eleDims.windowDims.offset.top + vOffset
        };
      case 'reveal full':
        return {
          left: $eleDims.windowDims.offset.left,
          top: $eleDims.windowDims.offset.top,
        };
        break;
      default:
        return {
          left: $anchorDims.offset.left,
          top: $anchorDims.offset.top + $anchorDims.height + vOffset
        };
    }
  };
  Foundation.Box = {
    ImNotTouchingYou: ImNotTouchingYou,
    GetDimensions: GetDimensions,
    GetOffsets: GetOffsets
  };
  // Foundation.ImNotTouchingYou = ImNotTouchingYou;
  // Foundation.GetDimensions = GetDimensions;
  // Foundation.GetOffsets = GetOffsets;
}(jQuery, window.Foundation, window);
<commit_msg>restricts GetDimensions to elements other than window or document, removes old GetDimensions<commit_after>!function($, Foundation, window){
  /**
   * Compares the dimensions of an element to a container and determines collision events with container.
   * @function
   * @param {jQuery} element - jQuery object to test for collisions.
   * @param {jQuery} parent - jQuery object to use as bounding container.
   * @param {Boolean} lrOnly - set to true to check left and right values only.
   * @param {Boolean} tbOnly - set to true to check top and bottom values only.
   * @default if no parent object passed, detects collisions with `window`.
   * @returns {Boolean} - true if collision free, false if a collision in any direction.
   */
  var ImNotTouchingYou = function(element, parent, lrOnly, tbOnly){
    var eleDims = GetDimensions(element),
        top, bottom, left, right;

    if(parent){
      var parDims = GetDimensions(parent);

      bottom = (eleDims.offset.top + eleDims.height <= parDims.height + parDims.offset.top);
      top    = (eleDims.offset.top >= parDims.offset.top);
      left   = (eleDims.offset.left >= parDims.offset.left);
      right  = (eleDims.offset.left + eleDims.width <= parDims.width);
    }else{
      bottom = (eleDims.offset.top + eleDims.height <= eleDims.windowDims.height + eleDims.windowDims.offset.top);
      top    = (eleDims.offset.top >= eleDims.windowDims.offset.top);
      left   = (eleDims.offset.left >= eleDims.windowDims.offset.left);
      right  = (eleDims.offset.left + eleDims.width <= eleDims.windowDims.width);
    }
    var allDirs = [bottom, top, left, right];

    if(lrOnly){ return left === right === true; }
    if(tbOnly){ return top === bottom === true; }

    return allDirs.indexOf(false) === -1;
  };

  /**
   * Uses native methods to return an object of dimension values.
   * @function
   * @param {jQuery || HTML} element - jQuery object or DOM element for which to get the dimensions. Can be any element other that document or window.
   * @returns {Object} - nested object of integer pixel values
   * TODO - if element is window, return only those values.
   */
  var GetDimensions = function(elem, test){
    elem = elem.length ? elem[0] : elem;

    if(window || document === elem){ throw new Error("I'm sorry, Dave. I'm afraid I can't do that."); }

    var rect = elem.getBoundingClientRect(),
        parRect = elem.parentNode.getBoundingClientRect(),
        winRect = document.body.getBoundingClientRect(),
        winY = window.pageYOffset,
        winX = window.pageXOffset;

    return {
      width: rect.width,
      height: rect.height,
      offset: {
        top: rect.top + winY,
        left: rect.left + winX
      },
      parentDims: {
        width: parRect.width,
        height: parRect.height,
        offset: {
          top: parRect.top + winY,
          left: parRect.left + winX
        }
      },
      windowDims: {
        width: winRect.width,
        height: winRect.height,
        offset: {
          top: winY,
          left: winX
        }
      }
    };
  };
  /**
   * Returns an object of top and left integer pixel values for dynamically rendered elements,
   * such as: Tooltip, Reveal, and Dropdown
   * @function
   * @param {jQuery} element - jQuery object for the element being positioned.
   * @param {jQuery} anchor - jQuery object for the element's anchor point.
   * @param {String} position - a string relating to the desired position of the element, relative to it's anchor
   * @param {Number} vOffset - integer pixel value of desired vertical separation between anchor and element.
   * @param {Number} hOffset - integer pixel value of desired horizontal separation between anchor and element.
   * @param {Boolean} isOverflow - if a collision event is detected, sets to true to default the element to full width - any desired offset.
   * TODO alter/rewrite to work with `em` values as well/instead of pixels
   */
  var GetOffsets = function(element, anchor, position, vOffset, hOffset, isOverflow){
    var $eleDims = GetDimensions(element),
    // var $eleDims = GetDimensions(element),
        $anchorDims = anchor ? GetDimensions(anchor) : null;
        // $anchorDims = anchor ? GetDimensions(anchor) : null;
    switch(position){
      case 'top':
        return {
          left: $anchorDims.offset.left,
          top: $anchorDims.offset.top - ($eleDims.height + vOffset)
        };
        break;
      case 'left':
        return {
          left: $anchorDims.offset.left - ($eleDims.width + hOffset),
          top: $anchorDims.offset.top
        };
        break;
      case 'right':
        return {
          left: $anchorDims.offset.left + $anchorDims.width + hOffset,
          top: $anchorDims.offset.top
        };
        break;
      case 'center top':
        return {
          left: ($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2),
          top: $anchorDims.offset.top - ($eleDims.height + vOffset)
        };
        break;
      case 'center bottom':
        return {
          left: isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)),
          top: $anchorDims.offset.top + $anchorDims.height + vOffset
        };
        break;
      case 'center left':
        return {
          left: $anchorDims.offset.left - ($eleDims.width + hOffset),
          top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2)
        };
        break;
      case 'center right':
        return {
          left: $anchorDims.offset.left + $anchorDims.width + hOffset + 1,
          top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2)
        };
        break;
      case 'center':
        return {
          left: ($eleDims.windowDims.offset.left + ($eleDims.windowDims.width / 2)) - ($eleDims.width / 2),
          top: ($eleDims.windowDims.offset.top + ($eleDims.windowDims.height / 2)) - ($eleDims.height / 2)
        };
        break;
      case 'reveal':
        return {
          left: ($eleDims.windowDims.width - $eleDims.width) / 2,
          top: $eleDims.windowDims.offset.top + vOffset
        };
      case 'reveal full':
        return {
          left: $eleDims.windowDims.offset.left,
          top: $eleDims.windowDims.offset.top,
        };
        break;
      default:
        return {
          left: $anchorDims.offset.left,
          top: $anchorDims.offset.top + $anchorDims.height + vOffset
        };
    }
  };
  Foundation.Box = {
    ImNotTouchingYou: ImNotTouchingYou,
    GetDimensions: GetDimensions,
    GetOffsets: GetOffsets
  };
}(jQuery, window.Foundation, window);
<|endoftext|>


<commit_before>// Copyright 2017-2020, University of Colorado Boulder

/**
 * Builds a runnable (something that builds like a simulation)
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

'use strict';

// modules
const _ = require( 'lodash' ); // eslint-disable-line require-statement-match
const assert = require( 'assert' );
const ChipperConstants = require( '../common/ChipperConstants' );
const ChipperStringUtils = require( '../common/ChipperStringUtils' );
const copyDirectory = require( './copyDirectory' );
const copySupplementalPhetioFiles = require( './phet-io/copySupplementalPhetioFiles' );
const generateThumbnails = require( './generateThumbnails' );
const generateTwitterCard = require( './generateTwitterCard' );
const getA11yViewHTMLFromTemplate = require( './getA11yViewHTMLFromTemplate' );
const getAllThirdPartyEntries = require( './getAllThirdPartyEntries' );
const getDependencies = require( './getDependencies' );
const getInitializationScript = require( './getInitializationScript' );
const getLocalesFromRepository = require( './getLocalesFromRepository' );
const getPhetLibs = require( './getPhetLibs' );
const getPreloads = require( './getPreloads' );
const getStringMap = require( './getStringMap' );
const getTitleStringKey = require( './getTitleStringKey' );
const grunt = require( 'grunt' );
const jimp = require( 'jimp' );
const loadFileAsDataURI = require( '../common/loadFileAsDataURI' );
const minify = require( './minify' );
const nodeHTMLEncoder = require( 'node-html-encoder' ); // eslint-disable-line require-statement-match
const packageRunnable = require( './packageRunnable' );
const packageXHTML = require( './packageXHTML' );
const reportUnusedStrings = require( './reportUnusedStrings' );
const webpackBuild = require( './webpackBuild' );
const zlib = require( 'zlib' );

// TODO: Re-enable these? https://github.com/phetsims/chipper/issues/909
// const reportUnusedMedia = require( './reportUnusedMedia' );

const recordTime = async ( asyncCallback, timeCallback ) => {
  const beforeTime = Date.now();
  const result = await asyncCallback();
  const afterTime = Date.now();
  timeCallback( afterTime - beforeTime, result );
  return result;
};

/**
 * Builds a runnable (e.g. a simulation).
 * @public
 *
 * @param {string} repo
 * @param {Object} minifyOptions - see minify.js
 * @param {boolean} instrument - If the sim should be instrumented
 * @param {boolean} allHTML - If the _all.html file should be generated
 * @param {string} brand
 * @param {string} localesOption - e.g,. '*', 'en,es', etc.
 * @returns {Promise} - Does not resolve a value
 */
module.exports = async function( repo, minifyOptions, instrument, allHTML, brand, localesOption ) {
  assert( typeof repo === 'string' );
  assert( typeof minifyOptions === 'object' );
  assert( _.includes( ChipperConstants.BRANDS, brand ), 'Unknown brand in buildRunnable: ' + brand );

  if ( brand === 'phet-io' ) {
    assert( grunt.file.exists( '../phet-io' ), 'Aborting the build of phet-io brand since proprietary repositories are not checked out.\nPlease use --brands=={{BRAND}} in the future to avoid this.' );
  }

  const packageObject = grunt.file.readJSON( `../${repo}/package.json` );
  const encoder = new nodeHTMLEncoder.Encoder( 'entity' );

  // All html files share the same build timestamp
  let timestamp = new Date().toISOString().split( 'T' ).join( ' ' );
  timestamp = timestamp.substring( 0, timestamp.indexOf( '.' ) ) + ' UTC';

  // Start running webpack
  const webpackResult = await recordTime( async () => {
    return await webpackBuild( repo, brand );
  }, time => {
    grunt.log.ok( `Webpack build complete: ${time}ms` );
  } );

  // NOTE: This build currently (due to the string/mipmap plugins) modifies globals. Some operations need to be done after this.
  const webpackJS = 'phet.chipper.runWebpack = function() {' + webpackResult.js + '};';

  // Debug version is independent of passed in minifyOptions.  PhET-iO brand is minified, but leaves assertions & logging.
  const debugMinifyOptions = brand === 'phet-io' ? {
    stripAssertions: false,
    stripLogging: false
  } : {
    minify: false
  };

  // After all media plugins have completed (which happens in requirejs:build), report which media files in the repository are unused.
  // TODO: Re-enable this? https://github.com/phetsims/chipper/issues/909
  // reportUnusedMedia( packageObject.phet.requirejsNamespace );

  const phetLibs = getPhetLibs( repo, brand );
  const allLocales = [ ChipperConstants.FALLBACK_LOCALE, ...getLocalesFromRepository( repo ) ];
  const locales = localesOption === '*' ? allLocales : localesOption.split( ',' );
  const dependencies = await getDependencies( repo );
  const version = packageObject.version; // Include the one-off name in the version
  const thirdPartyEntries = getAllThirdPartyEntries( repo, brand );
  const simTitleStringKey = getTitleStringKey( repo );

  const stringMap = getStringMap( allLocales, phetLibs, webpackResult.usedModules );

  // After our string map is consturcted, report which of the translatable strings are unused.
  reportUnusedStrings( repo, packageObject.phet.requirejsNamespace, stringMap[ ChipperConstants.FALLBACK_LOCALE ] );

  // If we have NO strings for a given locale that we want, we'll need to fill it in with all English strings, see
  // https://github.com/phetsims/perennial/issues/83
  for ( const locale of locales ) {
    if ( !stringMap[ locale ] ) {
      stringMap[ locale ] = stringMap[ ChipperConstants.FALLBACK_LOCALE ];
    }
  }

  const englishTitle = stringMap[ ChipperConstants.FALLBACK_LOCALE ][ simTitleStringKey ];
  assert( englishTitle, `missing entry for sim title, key = ${simTitleStringKey}` );

  // Select the HTML comment header based on the brand, see https://github.com/phetsims/chipper/issues/156
  let htmlHeader;
  if ( brand === 'phet-io' ) {

    // License text provided by @kathy-phet in https://github.com/phetsims/chipper/issues/148#issuecomment-112584773
    htmlHeader = englishTitle + ' ' + version + '\n' +
                 'Copyright 2002-' + grunt.template.today( 'yyyy' ) + ', Regents of the University of Colorado\n' +
                 'PhET Interactive Simulations, University of Colorado Boulder\n' +
                 '\n' +
                 'This Interoperable PhET Simulation file requires a license.\n' +
                 'USE WITHOUT A LICENSE AGREEMENT IS STRICTLY PROHIBITED.\n' +
                 'Contact phethelp@colorado.edu regarding licensing.\n' +
                 'https://phet.colorado.edu/en/licensing';
  }
  else {
    htmlHeader = englishTitle + ' ' + version + '\n' +
                 'Copyright 2002-' + grunt.template.today( 'yyyy' ) + ', Regents of the University of Colorado\n' +
                 'PhET Interactive Simulations, University of Colorado Boulder\n' +
                 '\n' +
                 'This file is licensed under Creative Commons Attribution 4.0\n' +
                 'For alternate source code licensing, see https://github.com/phetsims\n' +
                 'For licenses for third-party software used by this simulation, see below\n' +
                 'For more information, see https://phet.colorado.edu/en/licensing/html\n' +
                 '\n' +
                 'The PhET name and PhET logo are registered trademarks of The Regents of the\n' +
                 'University of Colorado. Permission is granted to use the PhET name and PhET logo\n' +
                 'only for attribution purposes. Use of the PhET name and/or PhET logo for promotional,\n' +
                 'marketing, or advertising purposes requires a separate license agreement from the\n' +
                 'University of Colorado. Contact phethelp@colorado.edu regarding licensing.';
  }

  // Scripts that are run before our main minifiable content
  const startupScripts = [
    // Splash image
    `window.PHET_SPLASH_DATA_URI="${loadFileAsDataURI( `../brand/${brand}/images/splash.svg` )}";`
  ];

  const minifiableScripts = [
    // Preloads
    ...getPreloads( repo, brand, true ).map( filename => grunt.file.read( filename ) ),

    // Our main require.js content, wrapped in a function called in the startup below
    webpackJS,

    // Main startup
    grunt.file.read( '../chipper/templates/chipper-startup.js' )
  ];

  const productionScripts = await recordTime( async () => {
    return [
      ...startupScripts,
      ...minifiableScripts.map( js => minify( js, minifyOptions ) )
    ];
  }, ( time, scripts ) => {
    grunt.log.ok( `Production minification complete: ${time}ms (${_.sum( scripts.map( js => js.length ) )} bytes)` );
  } );
  const debugScripts = await recordTime( async () => {
    return [
      ...startupScripts,
      ...minifiableScripts.map( js => minify( js, debugMinifyOptions ) )
    ];
  }, ( time, scripts ) => {
    grunt.log.ok( `Debug minification complete: ${time}ms (${_.sum( scripts.map( js => js.length ) )} bytes)` );
  } );

  const commonInitializationOptions = {
    brand: brand,
    repo: repo,
    stringMap: stringMap,
    dependencies: dependencies,
    timestamp: timestamp,
    version: version,
    thirdPartyEntries: thirdPartyEntries,
    packageObject: packageObject
  };

  // Create the build-specific directory
  const buildDir = `../${repo}/build/${brand}`;
  grunt.file.mkdir( buildDir );

  // {{locale}}.html
  if ( brand !== 'phet-io' ) {
    for ( const locale of locales ) {
      const initializationScript = getInitializationScript( _.extend( {
        locale: locale,
        includeAllLocales: false,
        isDebugBuild: false
      }, commonInitializationOptions ) );
      grunt.file.write( `${buildDir}/${repo}_${locale}_${brand}.html`, packageRunnable( {
        repo: repo,
        stringMap: stringMap,
        htmlHeader: htmlHeader,
        locale: locale,
        scripts: [ initializationScript, ...productionScripts ]
      } ) );
    }
  }

  // _all.html (forced for phet-io)
  if ( allHTML || brand === 'phet-io' ) {
    const initializationScript = getInitializationScript( _.extend( {
      locale: ChipperConstants.FALLBACK_LOCALE,
      includeAllLocales: true,
      isDebugBuild: false
    }, commonInitializationOptions ) );

    const allHTMLFilename = `${buildDir}/${repo}_all_${brand}.html`;
    const allHTMLContents = packageRunnable( {
      repo: repo,
      stringMap: stringMap,
      htmlHeader: htmlHeader,
      locale: ChipperConstants.FALLBACK_LOCALE,
      scripts: [ initializationScript, ...productionScripts ]
    } );

    grunt.file.write( allHTMLFilename, allHTMLContents );
    grunt.file.write( allHTMLFilename + '.gz', zlib.gzipSync( allHTMLContents ) );

  }

  // Debug build (always included)
  const debugInitializationScript = getInitializationScript( _.extend( {
    locale: ChipperConstants.FALLBACK_LOCALE,
    includeAllLocales: true,
    isDebugBuild: true
  }, commonInitializationOptions ) );
  grunt.file.write( `${buildDir}/${repo}_all_${brand}_debug.html`, packageRunnable( {
    repo: repo,
    stringMap: stringMap,
    htmlHeader: htmlHeader,
    locale: ChipperConstants.FALLBACK_LOCALE,
    scripts: [ debugInitializationScript, ...debugScripts ]
  } ) );

  // XHTML build (ePub compatibility, etc.)
  const xhtmlDir = `${buildDir}/xhtml`;
  grunt.file.mkdir( xhtmlDir );
  const xhtmlInitializationScript = getInitializationScript( _.extend( {
    locale: ChipperConstants.FALLBACK_LOCALE,
    includeAllLocales: true,
    isDebugBuild: false
  }, commonInitializationOptions ) );
  packageXHTML( xhtmlDir, {
    repo: repo,
    brand: brand,
    stringMap: stringMap,
    htmlHeader: htmlHeader,
    scripts: [ xhtmlInitializationScript, ...productionScripts ]
  } );

  // dependencies.json
  grunt.file.write( `${buildDir}/dependencies.json`, JSON.stringify( dependencies, null, 2 ) );

  // -iframe.html (English is assumed as the locale).
  if ( _.includes( locales, ChipperConstants.FALLBACK_LOCALE ) && brand === 'phet' ) {
    const englishTitle = stringMap[ ChipperConstants.FALLBACK_LOCALE ][ getTitleStringKey( repo ) ];

    grunt.log.debug( 'Constructing HTML for iframe testing from template' );
    let iframeTestHtml = grunt.file.read( '../chipper/templates/sim-iframe.html' );
    iframeTestHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_SIM_TITLE}}', encoder.htmlEncode( englishTitle + ' iframe test' ) );
    iframeTestHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_REPOSITORY}}', repo );

    const iframeLocales = [ 'en' ].concat( allHTML ? [ 'all' ] : [] );
    iframeLocales.forEach( locale => {
      const iframeHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_LOCALE}}', locale );
      grunt.file.write( `${buildDir}/${repo}_${locale}_iframe_phet.html`, iframeHtml );
    } );
  }

  // If the sim is a11y outfitted, then add the a11y pdom viewer to the build dir. NOTE: Not for phet-io builds.
  if ( packageObject.phet.accessible && brand === 'phet' ) {
    // (a11y) Create the a11y-view HTML file for PDOM viewing.
    let a11yHTML = getA11yViewHTMLFromTemplate( repo );

    // this replaceAll is outside of the getA11yViewHTMLFromTemplate because we only want it filled in during the build
    a11yHTML = ChipperStringUtils.replaceAll( a11yHTML, '{{IS_BUILT}}', 'true' );

    grunt.file.write( `${buildDir}/${repo}${ChipperConstants.A11Y_VIEW_HTML_SUFFIX}`, a11yHTML );
  }

  // copy over supplemental files or dirs to package with the build. Only supported in phet brand
  if ( packageObject.phet && packageObject.phet.packageWithBuild ) {

    assert( Array.isArray( packageObject.phet.packageWithBuild ) );
    packageObject.phet.packageWithBuild.forEach( path => {

      assert( typeof path === 'string', 'path should be a string' );
      assert( grunt.file.exists( path ), `path does not exist: ${path}` );
      if ( grunt.file.isDir( path ) ) {
        copyDirectory( path, `${buildDir}/${path}` );
      }
      else {
        grunt.file.copy( path, `${buildDir}/${path}` );
      }
    } );
  }

  if ( brand === 'phet-io' ) {
    await copySupplementalPhetioFiles( repo, version, englishTitle, packageObject );
  }

  // Thumbnails and twitter card
  if ( grunt.file.exists( `../${repo}/assets/${repo}-screenshot.png` ) ) {
    const thumbnailSizes = [
      { width: 128, height: 84 },
      { width: 600, height: 394 }
    ];
    for ( const size of thumbnailSizes ) {
      grunt.file.write( `${buildDir}/${repo}-${size.width}.png`, await generateThumbnails( repo, size.width, size.height, 100, jimp.MIME_PNG ) );
    }

    if ( brand === 'phet' ) {
      grunt.file.write( `${buildDir}/${repo}-ios.png`, await generateThumbnails( repo, 420, 276, 90, jimp.MIME_JPEG ) );
      grunt.file.write( `${buildDir}/${repo}-twitter-card.png`, await generateTwitterCard( repo ) );
    }
  }
};
<commit_msg>accessible => supportsInteractiveDescriptions, see #937<commit_after>// Copyright 2017-2020, University of Colorado Boulder

/**
 * Builds a runnable (something that builds like a simulation)
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

'use strict';

// modules
const _ = require( 'lodash' ); // eslint-disable-line require-statement-match
const assert = require( 'assert' );
const ChipperConstants = require( '../common/ChipperConstants' );
const ChipperStringUtils = require( '../common/ChipperStringUtils' );
const copyDirectory = require( './copyDirectory' );
const copySupplementalPhetioFiles = require( './phet-io/copySupplementalPhetioFiles' );
const generateThumbnails = require( './generateThumbnails' );
const generateTwitterCard = require( './generateTwitterCard' );
const getA11yViewHTMLFromTemplate = require( './getA11yViewHTMLFromTemplate' );
const getAllThirdPartyEntries = require( './getAllThirdPartyEntries' );
const getDependencies = require( './getDependencies' );
const getInitializationScript = require( './getInitializationScript' );
const getLocalesFromRepository = require( './getLocalesFromRepository' );
const getPhetLibs = require( './getPhetLibs' );
const getPreloads = require( './getPreloads' );
const getStringMap = require( './getStringMap' );
const getTitleStringKey = require( './getTitleStringKey' );
const grunt = require( 'grunt' );
const jimp = require( 'jimp' );
const loadFileAsDataURI = require( '../common/loadFileAsDataURI' );
const minify = require( './minify' );
const nodeHTMLEncoder = require( 'node-html-encoder' ); // eslint-disable-line require-statement-match
const packageRunnable = require( './packageRunnable' );
const packageXHTML = require( './packageXHTML' );
const reportUnusedStrings = require( './reportUnusedStrings' );
const webpackBuild = require( './webpackBuild' );
const zlib = require( 'zlib' );

// TODO: Re-enable these? https://github.com/phetsims/chipper/issues/909
// const reportUnusedMedia = require( './reportUnusedMedia' );

const recordTime = async ( asyncCallback, timeCallback ) => {
  const beforeTime = Date.now();
  const result = await asyncCallback();
  const afterTime = Date.now();
  timeCallback( afterTime - beforeTime, result );
  return result;
};

/**
 * Builds a runnable (e.g. a simulation).
 * @public
 *
 * @param {string} repo
 * @param {Object} minifyOptions - see minify.js
 * @param {boolean} instrument - If the sim should be instrumented
 * @param {boolean} allHTML - If the _all.html file should be generated
 * @param {string} brand
 * @param {string} localesOption - e.g,. '*', 'en,es', etc.
 * @returns {Promise} - Does not resolve a value
 */
module.exports = async function( repo, minifyOptions, instrument, allHTML, brand, localesOption ) {
  assert( typeof repo === 'string' );
  assert( typeof minifyOptions === 'object' );
  assert( _.includes( ChipperConstants.BRANDS, brand ), 'Unknown brand in buildRunnable: ' + brand );

  if ( brand === 'phet-io' ) {
    assert( grunt.file.exists( '../phet-io' ), 'Aborting the build of phet-io brand since proprietary repositories are not checked out.\nPlease use --brands=={{BRAND}} in the future to avoid this.' );
  }

  const packageObject = grunt.file.readJSON( `../${repo}/package.json` );
  const encoder = new nodeHTMLEncoder.Encoder( 'entity' );

  // All html files share the same build timestamp
  let timestamp = new Date().toISOString().split( 'T' ).join( ' ' );
  timestamp = timestamp.substring( 0, timestamp.indexOf( '.' ) ) + ' UTC';

  // Start running webpack
  const webpackResult = await recordTime( async () => {
    return await webpackBuild( repo, brand );
  }, time => {
    grunt.log.ok( `Webpack build complete: ${time}ms` );
  } );

  // NOTE: This build currently (due to the string/mipmap plugins) modifies globals. Some operations need to be done after this.
  const webpackJS = 'phet.chipper.runWebpack = function() {' + webpackResult.js + '};';

  // Debug version is independent of passed in minifyOptions.  PhET-iO brand is minified, but leaves assertions & logging.
  const debugMinifyOptions = brand === 'phet-io' ? {
    stripAssertions: false,
    stripLogging: false
  } : {
    minify: false
  };

  // After all media plugins have completed (which happens in requirejs:build), report which media files in the repository are unused.
  // TODO: Re-enable this? https://github.com/phetsims/chipper/issues/909
  // reportUnusedMedia( packageObject.phet.requirejsNamespace );

  const phetLibs = getPhetLibs( repo, brand );
  const allLocales = [ ChipperConstants.FALLBACK_LOCALE, ...getLocalesFromRepository( repo ) ];
  const locales = localesOption === '*' ? allLocales : localesOption.split( ',' );
  const dependencies = await getDependencies( repo );
  const version = packageObject.version; // Include the one-off name in the version
  const thirdPartyEntries = getAllThirdPartyEntries( repo, brand );
  const simTitleStringKey = getTitleStringKey( repo );

  const stringMap = getStringMap( allLocales, phetLibs, webpackResult.usedModules );

  // After our string map is consturcted, report which of the translatable strings are unused.
  reportUnusedStrings( repo, packageObject.phet.requirejsNamespace, stringMap[ ChipperConstants.FALLBACK_LOCALE ] );

  // If we have NO strings for a given locale that we want, we'll need to fill it in with all English strings, see
  // https://github.com/phetsims/perennial/issues/83
  for ( const locale of locales ) {
    if ( !stringMap[ locale ] ) {
      stringMap[ locale ] = stringMap[ ChipperConstants.FALLBACK_LOCALE ];
    }
  }

  const englishTitle = stringMap[ ChipperConstants.FALLBACK_LOCALE ][ simTitleStringKey ];
  assert( englishTitle, `missing entry for sim title, key = ${simTitleStringKey}` );

  // Select the HTML comment header based on the brand, see https://github.com/phetsims/chipper/issues/156
  let htmlHeader;
  if ( brand === 'phet-io' ) {

    // License text provided by @kathy-phet in https://github.com/phetsims/chipper/issues/148#issuecomment-112584773
    htmlHeader = englishTitle + ' ' + version + '\n' +
                 'Copyright 2002-' + grunt.template.today( 'yyyy' ) + ', Regents of the University of Colorado\n' +
                 'PhET Interactive Simulations, University of Colorado Boulder\n' +
                 '\n' +
                 'This Interoperable PhET Simulation file requires a license.\n' +
                 'USE WITHOUT A LICENSE AGREEMENT IS STRICTLY PROHIBITED.\n' +
                 'Contact phethelp@colorado.edu regarding licensing.\n' +
                 'https://phet.colorado.edu/en/licensing';
  }
  else {
    htmlHeader = englishTitle + ' ' + version + '\n' +
                 'Copyright 2002-' + grunt.template.today( 'yyyy' ) + ', Regents of the University of Colorado\n' +
                 'PhET Interactive Simulations, University of Colorado Boulder\n' +
                 '\n' +
                 'This file is licensed under Creative Commons Attribution 4.0\n' +
                 'For alternate source code licensing, see https://github.com/phetsims\n' +
                 'For licenses for third-party software used by this simulation, see below\n' +
                 'For more information, see https://phet.colorado.edu/en/licensing/html\n' +
                 '\n' +
                 'The PhET name and PhET logo are registered trademarks of The Regents of the\n' +
                 'University of Colorado. Permission is granted to use the PhET name and PhET logo\n' +
                 'only for attribution purposes. Use of the PhET name and/or PhET logo for promotional,\n' +
                 'marketing, or advertising purposes requires a separate license agreement from the\n' +
                 'University of Colorado. Contact phethelp@colorado.edu regarding licensing.';
  }

  // Scripts that are run before our main minifiable content
  const startupScripts = [
    // Splash image
    `window.PHET_SPLASH_DATA_URI="${loadFileAsDataURI( `../brand/${brand}/images/splash.svg` )}";`
  ];

  const minifiableScripts = [
    // Preloads
    ...getPreloads( repo, brand, true ).map( filename => grunt.file.read( filename ) ),

    // Our main require.js content, wrapped in a function called in the startup below
    webpackJS,

    // Main startup
    grunt.file.read( '../chipper/templates/chipper-startup.js' )
  ];

  const productionScripts = await recordTime( async () => {
    return [
      ...startupScripts,
      ...minifiableScripts.map( js => minify( js, minifyOptions ) )
    ];
  }, ( time, scripts ) => {
    grunt.log.ok( `Production minification complete: ${time}ms (${_.sum( scripts.map( js => js.length ) )} bytes)` );
  } );
  const debugScripts = await recordTime( async () => {
    return [
      ...startupScripts,
      ...minifiableScripts.map( js => minify( js, debugMinifyOptions ) )
    ];
  }, ( time, scripts ) => {
    grunt.log.ok( `Debug minification complete: ${time}ms (${_.sum( scripts.map( js => js.length ) )} bytes)` );
  } );

  const commonInitializationOptions = {
    brand: brand,
    repo: repo,
    stringMap: stringMap,
    dependencies: dependencies,
    timestamp: timestamp,
    version: version,
    thirdPartyEntries: thirdPartyEntries,
    packageObject: packageObject
  };

  // Create the build-specific directory
  const buildDir = `../${repo}/build/${brand}`;
  grunt.file.mkdir( buildDir );

  // {{locale}}.html
  if ( brand !== 'phet-io' ) {
    for ( const locale of locales ) {
      const initializationScript = getInitializationScript( _.extend( {
        locale: locale,
        includeAllLocales: false,
        isDebugBuild: false
      }, commonInitializationOptions ) );
      grunt.file.write( `${buildDir}/${repo}_${locale}_${brand}.html`, packageRunnable( {
        repo: repo,
        stringMap: stringMap,
        htmlHeader: htmlHeader,
        locale: locale,
        scripts: [ initializationScript, ...productionScripts ]
      } ) );
    }
  }

  // _all.html (forced for phet-io)
  if ( allHTML || brand === 'phet-io' ) {
    const initializationScript = getInitializationScript( _.extend( {
      locale: ChipperConstants.FALLBACK_LOCALE,
      includeAllLocales: true,
      isDebugBuild: false
    }, commonInitializationOptions ) );

    const allHTMLFilename = `${buildDir}/${repo}_all_${brand}.html`;
    const allHTMLContents = packageRunnable( {
      repo: repo,
      stringMap: stringMap,
      htmlHeader: htmlHeader,
      locale: ChipperConstants.FALLBACK_LOCALE,
      scripts: [ initializationScript, ...productionScripts ]
    } );

    grunt.file.write( allHTMLFilename, allHTMLContents );
    grunt.file.write( allHTMLFilename + '.gz', zlib.gzipSync( allHTMLContents ) );

  }

  // Debug build (always included)
  const debugInitializationScript = getInitializationScript( _.extend( {
    locale: ChipperConstants.FALLBACK_LOCALE,
    includeAllLocales: true,
    isDebugBuild: true
  }, commonInitializationOptions ) );
  grunt.file.write( `${buildDir}/${repo}_all_${brand}_debug.html`, packageRunnable( {
    repo: repo,
    stringMap: stringMap,
    htmlHeader: htmlHeader,
    locale: ChipperConstants.FALLBACK_LOCALE,
    scripts: [ debugInitializationScript, ...debugScripts ]
  } ) );

  // XHTML build (ePub compatibility, etc.)
  const xhtmlDir = `${buildDir}/xhtml`;
  grunt.file.mkdir( xhtmlDir );
  const xhtmlInitializationScript = getInitializationScript( _.extend( {
    locale: ChipperConstants.FALLBACK_LOCALE,
    includeAllLocales: true,
    isDebugBuild: false
  }, commonInitializationOptions ) );
  packageXHTML( xhtmlDir, {
    repo: repo,
    brand: brand,
    stringMap: stringMap,
    htmlHeader: htmlHeader,
    scripts: [ xhtmlInitializationScript, ...productionScripts ]
  } );

  // dependencies.json
  grunt.file.write( `${buildDir}/dependencies.json`, JSON.stringify( dependencies, null, 2 ) );

  // -iframe.html (English is assumed as the locale).
  if ( _.includes( locales, ChipperConstants.FALLBACK_LOCALE ) && brand === 'phet' ) {
    const englishTitle = stringMap[ ChipperConstants.FALLBACK_LOCALE ][ getTitleStringKey( repo ) ];

    grunt.log.debug( 'Constructing HTML for iframe testing from template' );
    let iframeTestHtml = grunt.file.read( '../chipper/templates/sim-iframe.html' );
    iframeTestHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_SIM_TITLE}}', encoder.htmlEncode( englishTitle + ' iframe test' ) );
    iframeTestHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_REPOSITORY}}', repo );

    const iframeLocales = [ 'en' ].concat( allHTML ? [ 'all' ] : [] );
    iframeLocales.forEach( locale => {
      const iframeHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_LOCALE}}', locale );
      grunt.file.write( `${buildDir}/${repo}_${locale}_iframe_phet.html`, iframeHtml );
    } );
  }

  // If the sim is a11y outfitted, then add the a11y pdom viewer to the build dir. NOTE: Not for phet-io builds.
  if ( packageObject.phet.supportsInteractiveDescriptions && brand === 'phet' ) {
    // (a11y) Create the a11y-view HTML file for PDOM viewing.
    let a11yHTML = getA11yViewHTMLFromTemplate( repo );

    // this replaceAll is outside of the getA11yViewHTMLFromTemplate because we only want it filled in during the build
    a11yHTML = ChipperStringUtils.replaceAll( a11yHTML, '{{IS_BUILT}}', 'true' );

    grunt.file.write( `${buildDir}/${repo}${ChipperConstants.A11Y_VIEW_HTML_SUFFIX}`, a11yHTML );
  }

  // copy over supplemental files or dirs to package with the build. Only supported in phet brand
  if ( packageObject.phet && packageObject.phet.packageWithBuild ) {

    assert( Array.isArray( packageObject.phet.packageWithBuild ) );
    packageObject.phet.packageWithBuild.forEach( path => {

      assert( typeof path === 'string', 'path should be a string' );
      assert( grunt.file.exists( path ), `path does not exist: ${path}` );
      if ( grunt.file.isDir( path ) ) {
        copyDirectory( path, `${buildDir}/${path}` );
      }
      else {
        grunt.file.copy( path, `${buildDir}/${path}` );
      }
    } );
  }

  if ( brand === 'phet-io' ) {
    await copySupplementalPhetioFiles( repo, version, englishTitle, packageObject );
  }

  // Thumbnails and twitter card
  if ( grunt.file.exists( `../${repo}/assets/${repo}-screenshot.png` ) ) {
    const thumbnailSizes = [
      { width: 128, height: 84 },
      { width: 600, height: 394 }
    ];
    for ( const size of thumbnailSizes ) {
      grunt.file.write( `${buildDir}/${repo}-${size.width}.png`, await generateThumbnails( repo, size.width, size.height, 100, jimp.MIME_PNG ) );
    }

    if ( brand === 'phet' ) {
      grunt.file.write( `${buildDir}/${repo}-ios.png`, await generateThumbnails( repo, 420, 276, 90, jimp.MIME_JPEG ) );
      grunt.file.write( `${buildDir}/${repo}-twitter-card.png`, await generateTwitterCard( repo ) );
    }
  }
};
<|endoftext|>


<commit_before>// Curious client-side JSON parsing
//

(function(){

  var CuriousQuery = function() {
    this.terms = [];
    this.relationships = [];
    this.classes = [];
  };

  CuriousQuery.prototype = {
    query: function() { return this.terms.join(' '); },
    add: function(term, relationship, klass) {
      this.terms.push(term);
      this.relationships.push(relationship);
      this.classes.push(klass);
      return this;
    },
  };

  var CuriousObjects = (function() {
    function CuriousObject(hash_data) {
      this.id = hash_data.id;
      for (var k in hash_data) {
        this[k] = hash_data[k];
      }
      this.__url = null;
      this.__model = null;
      this.__dirty = false;
    }

    function parse_objects(data, model, klass) {
      if (data.objects === undefined) { return []; }
      var objects = [];
      for (var i=0; i<data.objects.length; i++) {
        var obj = data.objects[i];
        var url = data.urls[i];
        var obj_data = {};
        for (var j=0; j<data.fields.length; j++) {
          obj_data[data.fields[j]] = obj[j];
        }
        var obj;
        if (!klass)
          obj = new CuriousObject(obj_data);
        else {
          obj = new klass();
          for (var k in obj_data) { obj[k] = obj_data[k]; }
        }
        obj.id = obj_data.id;
        obj.__url = url;
        obj.__model = model;
        objects.push(obj);
      }
      return objects;
    }

    function parse_results_with_trees(relationships, classes, results, existing_object_dicts) {
      // get objects associated with each subquery. for each subquery, build a
      // hash of ID to object. existing_object_dicts should be an array of dicts,
      // each dict is a mapping of ID to existing objects. if existing objects
      // are specified, will build relationships using existing objects.

      var objects = [];
      var trees = [];

      for (var i=0; i<results.data.length; i++) {
        var klass = null;
        if (classes)
          klass = classes[i];
        var model = results.results[i].model;
        var result_objects = parse_objects(results.data[i], model, klass);
        var d = {};
        for (var j=0; j<result_objects.length; j++) {
          if (existing_object_dicts !== undefined && existing_object_dicts !== null &&
              existing_object_dicts[i] !== undefined && existing_object_dicts[i] !== null &&
              existing_object_dicts[i][result_objects[j].id] !== undefined) {
            d[result_objects[j].id] = existing_object_dicts[i][result_objects[j].id];
          }
          else {
            d[result_objects[j].id] = result_objects[j];
          }
        }
        objects.push(d);
        trees.push(null);
      }

      // for each subquery, add a relationship to results of next subquery, and
      // then a reverse relationship
      for (var i=1; i<results.results.length; i++) {
        var rel = relationships[i];

        var res_tups = results.results[i].objects;
        var join_idx = results.results[i].join_index;
        var join_src = objects[join_idx];
        var join_obj = objects[i];
        var rev = relationships[join_idx];
        trees[i] = results.results[i].tree;

        // add empty replationship
        for (var k in join_src) { join_src[k][rel] = []; }
        for (var k in join_obj) { join_obj[k][rev] = []; }

        for (var j=0; j<res_tups.length; j++) {
          var obj_src = res_tups[j];
          var src = join_src[obj_src[1]];
          if (obj_src[0]) {
            var obj = join_obj[obj_src[0]];

            // forward relationship from query to next query
            src[rel].push(obj);
            // reverse relationship
            obj[rev].push(src);
          }
        }
      }

      return {objects: objects, trees: trees};
    }

    function parse_results(relationships, classes, results, existing_object_dicts) {
      return parse_results_with_trees(relationships, classes, results, existing_object_dicts).objects;
    }

    function dict_to_array(d) {
      var r = [];
      for (var k in d) { r.push(d[k]); }
      return r;
    }

    function array_to_dict(a) {
      var d = {};
      for (var i=0; i<a.length; i++) {
        d[a[i].id] = a[i];
      }
      return d;
    }

    function id_list(objects) {
      var ids = [];
      for (var i=0; i<objects.length; i++) {
        if (ids.indexOf(objects[i].id) < 0) {
          ids.push(objects[i].id);
        }
      }
      return ids;
    }

    function id_str(objects) {
      var ids = id_list(objects);
      if (ids.length === 0) { return null; }
      return ids.join(',');
    }

    return {
      parse: parse_results,
      parse_with_trees: parse_results_with_trees,
      d2a: dict_to_array,
      a2d: array_to_dict,
      id_list: id_list,
      id_str: id_str
    }

  }());

  // Helper for making a Curious query and getting back parsed objects. Use with
  // angular $http compatible HTTP request facilities (e.g. jQuery?)

  var CuriousQ = function(curious_url, http, app_default_params, quiet) {

    function __get(q, params, relationships, existing_object_arrays, cb, trees_cb) {
      var i, k;
      var args, immutable_args;
      var data_array;
      var existing_object_dicts;
      var post_cb;

      if (quiet === undefined || quiet !== true) {
        console.warn(q);
      }

      if (existing_object_arrays) {
        existing_object_dicts = [];
        for (i = 0; i < existing_object_arrays.length; i++) {
          data_array = existing_object_arrays[i];
          if (data_array) {
            existing_object_dicts.push(CuriousObjects.a2d(data_array));
          }
          else {
            existing_object_dicts.push(null);
          }
        }
      }

      // Default args
      args = {x: 0, fk: 0};

      // App-level arg settings
      if (app_default_params) {
        for (k in app_default_params) {
          if (app_default_params.hasOwnProperty(k)) {
            args[k] = app_default_params[k];
          }
        }
      }

      // Query-level arg settings
      if (params) {
        for (k in params) {
          if (app_default_params.hasOwnProperty(k)) {
            args[k] = params[k];
          }
        }
      }

      // Values for immutable args: these are always set, no matter what
      immutable_args = {d: 1, q: q};
      for (k in immutable_args) {
        args[k] = immutable_args[k];
      }

      post_cb = function(resp) {
        var i;
        var objects;
        var res;

        res = CuriousObjects.parse_with_trees(relationships, classes, resp.result, existing_object_dicts);
        objects = res.objects;

        for (i = 0; i < objects.length; i++) { objects[i] = CuriousObjects.d2a(objects[i]); }
        cb(objects);
        if (trees_cb) { trees_cb(res.trees); }
      };

      return http.post(curious_url, args).success(post_cb);
    }

    function query(query_object, cb, params, tree_cb) {
      var q = query_object.query();
      __get(q, params, query_object.relationships, query_object.classes, null, cb, tree_cb);
    }

    function get(q, relationships, cb, params, tree_cb) {
      return __get(q, params, relationships, null, null, cb, tree_cb);
    }

    function get_with_objs(q, relationships, existing_object_arrays, cb, params, tree_cb) {
      return __get(q, params, relationships, null, existing_object_arrays, cb, tree_cb);
    }

    function get_with_start(q, relationships, starting_objects, cb, params, tree_cb) {
      var i;
      var existing_object_arrays = [];

      for (i = 0; i < relationships.length; i++) {
        existing_object_arrays.push(null);
      }
      existing_object_arrays[0] = starting_objects;

      return __get(q, params, relationships, null, existing_object_arrays, cb, tree_cb);
    }

    return {
      query: query,
      get: get,
      get_with_objs: get_with_objs,
      get_with_start: get_with_start
    }
  };

  var ex = undefined;
  if (typeof window !== 'undefined') { ex = window; }
  else if (typeof exports !== 'undefined' && exports) { ex = exports; }
  ex.CuriousQ = CuriousQ;
  ex.CuriousObjects = CuriousObjects;
  ex.CuriousQuery = CuriousQuery;

})();
<commit_msg>Fix typeo<commit_after>// Curious client-side JSON parsing
//

(function(){

  var CuriousQuery = function() {
    this.terms = [];
    this.relationships = [];
    this.classes = [];
  };

  CuriousQuery.prototype = {
    query: function() { return this.terms.join(' '); },
    add: function(term, relationship, klass) {
      this.terms.push(term);
      this.relationships.push(relationship);
      this.classes.push(klass);
      return this;
    },
  };

  var CuriousObjects = (function() {
    function CuriousObject(hash_data) {
      this.id = hash_data.id;
      for (var k in hash_data) {
        this[k] = hash_data[k];
      }
      this.__url = null;
      this.__model = null;
      this.__dirty = false;
    }

    function parse_objects(data, model, klass) {
      if (data.objects === undefined) { return []; }
      var objects = [];
      for (var i=0; i<data.objects.length; i++) {
        var obj = data.objects[i];
        var url = data.urls[i];
        var obj_data = {};
        for (var j=0; j<data.fields.length; j++) {
          obj_data[data.fields[j]] = obj[j];
        }
        var obj;
        if (!klass)
          obj = new CuriousObject(obj_data);
        else {
          obj = new klass();
          for (var k in obj_data) { obj[k] = obj_data[k]; }
        }
        obj.id = obj_data.id;
        obj.__url = url;
        obj.__model = model;
        objects.push(obj);
      }
      return objects;
    }

    function parse_results_with_trees(relationships, classes, results, existing_object_dicts) {
      // get objects associated with each subquery. for each subquery, build a
      // hash of ID to object. existing_object_dicts should be an array of dicts,
      // each dict is a mapping of ID to existing objects. if existing objects
      // are specified, will build relationships using existing objects.

      var objects = [];
      var trees = [];

      for (var i=0; i<results.data.length; i++) {
        var klass = null;
        if (classes)
          klass = classes[i];
        var model = results.results[i].model;
        var result_objects = parse_objects(results.data[i], model, klass);
        var d = {};
        for (var j=0; j<result_objects.length; j++) {
          if (existing_object_dicts !== undefined && existing_object_dicts !== null &&
              existing_object_dicts[i] !== undefined && existing_object_dicts[i] !== null &&
              existing_object_dicts[i][result_objects[j].id] !== undefined) {
            d[result_objects[j].id] = existing_object_dicts[i][result_objects[j].id];
          }
          else {
            d[result_objects[j].id] = result_objects[j];
          }
        }
        objects.push(d);
        trees.push(null);
      }

      // for each subquery, add a relationship to results of next subquery, and
      // then a reverse relationship
      for (var i=1; i<results.results.length; i++) {
        var rel = relationships[i];

        var res_tups = results.results[i].objects;
        var join_idx = results.results[i].join_index;
        var join_src = objects[join_idx];
        var join_obj = objects[i];
        var rev = relationships[join_idx];
        trees[i] = results.results[i].tree;

        // add empty replationship
        for (var k in join_src) { join_src[k][rel] = []; }
        for (var k in join_obj) { join_obj[k][rev] = []; }

        for (var j=0; j<res_tups.length; j++) {
          var obj_src = res_tups[j];
          var src = join_src[obj_src[1]];
          if (obj_src[0]) {
            var obj = join_obj[obj_src[0]];

            // forward relationship from query to next query
            src[rel].push(obj);
            // reverse relationship
            obj[rev].push(src);
          }
        }
      }

      return {objects: objects, trees: trees};
    }

    function parse_results(relationships, classes, results, existing_object_dicts) {
      return parse_results_with_trees(relationships, classes, results, existing_object_dicts).objects;
    }

    function dict_to_array(d) {
      var r = [];
      for (var k in d) { r.push(d[k]); }
      return r;
    }

    function array_to_dict(a) {
      var d = {};
      for (var i=0; i<a.length; i++) {
        d[a[i].id] = a[i];
      }
      return d;
    }

    function id_list(objects) {
      var ids = [];
      for (var i=0; i<objects.length; i++) {
        if (ids.indexOf(objects[i].id) < 0) {
          ids.push(objects[i].id);
        }
      }
      return ids;
    }

    function id_str(objects) {
      var ids = id_list(objects);
      if (ids.length === 0) { return null; }
      return ids.join(',');
    }

    return {
      parse: parse_results,
      parse_with_trees: parse_results_with_trees,
      d2a: dict_to_array,
      a2d: array_to_dict,
      id_list: id_list,
      id_str: id_str
    }

  }());

  // Helper for making a Curious query and getting back parsed objects. Use with
  // angular $http compatible HTTP request facilities (e.g. jQuery?)

  var CuriousQ = function(curious_url, http, app_default_params, quiet) {

    function __get(q, params, relationships, existing_object_arrays, cb, trees_cb) {
      var i, k;
      var args, immutable_args;
      var data_array;
      var existing_object_dicts;
      var post_cb;

      if (quiet === undefined || quiet !== true) {
        console.warn(q);
      }

      if (existing_object_arrays) {
        existing_object_dicts = [];
        for (i = 0; i < existing_object_arrays.length; i++) {
          data_array = existing_object_arrays[i];
          if (data_array) {
            existing_object_dicts.push(CuriousObjects.a2d(data_array));
          }
          else {
            existing_object_dicts.push(null);
          }
        }
      }

      // Default args
      args = {x: 0, fk: 0};

      // App-level arg settings
      if (app_default_params) {
        for (k in app_default_params) {
          if (app_default_params.hasOwnProperty(k)) {
            args[k] = app_default_params[k];
          }
        }
      }

      // Query-level arg settings
      if (params) {
        for (k in params) {
          if (params.hasOwnProperty(k)) {
            args[k] = params[k];
          }
        }
      }

      // Values for immutable args: these are always set, no matter what
      immutable_args = {d: 1, q: q};
      for (k in immutable_args) {
        args[k] = immutable_args[k];
      }

      post_cb = function(resp) {
        var i;
        var objects;
        var res;

        res = CuriousObjects.parse_with_trees(relationships, classes, resp.result, existing_object_dicts);
        objects = res.objects;

        for (i = 0; i < objects.length; i++) { objects[i] = CuriousObjects.d2a(objects[i]); }
        cb(objects);
        if (trees_cb) { trees_cb(res.trees); }
      };

      return http.post(curious_url, args).success(post_cb);
    }

    function query(query_object, cb, params, tree_cb) {
      var q = query_object.query();
      __get(q, params, query_object.relationships, query_object.classes, null, cb, tree_cb);
    }

    function get(q, relationships, cb, params, tree_cb) {
      return __get(q, params, relationships, null, null, cb, tree_cb);
    }

    function get_with_objs(q, relationships, existing_object_arrays, cb, params, tree_cb) {
      return __get(q, params, relationships, null, existing_object_arrays, cb, tree_cb);
    }

    function get_with_start(q, relationships, starting_objects, cb, params, tree_cb) {
      var i;
      var existing_object_arrays = [];

      for (i = 0; i < relationships.length; i++) {
        existing_object_arrays.push(null);
      }
      existing_object_arrays[0] = starting_objects;

      return __get(q, params, relationships, null, existing_object_arrays, cb, tree_cb);
    }

    return {
      query: query,
      get: get,
      get_with_objs: get_with_objs,
      get_with_start: get_with_start
    }
  };

  var ex = undefined;
  if (typeof window !== 'undefined') { ex = window; }
  else if (typeof exports !== 'undefined' && exports) { ex = exports; }
  ex.CuriousQ = CuriousQ;
  ex.CuriousObjects = CuriousObjects;
  ex.CuriousQuery = CuriousQuery;

})();
<|endoftext|>