<commit_before>// Copyright (c) 2017, Rubén Rincón
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

"use strict";

var options = require('../options');
var _ = require('underscore');
var $ = require('jquery');
var Promise = require('es6-promise').Promise;
var ga = require('../analytics');
var Components = require('../components');
var Libraries = require('../libs-widget');

require('selectize');

function Conformance(hub, container, state) {
    this.hub = hub;
    this.container = container;
    this.eventHub = hub.createEventHub();
    this.compilerService = hub.compilerService;
    this.domRoot = container.getElement();
    this.domRoot.html($('#conformance').html());
    this.editorId = state.editorid;
    this.maxCompilations = options.cvCompilerCountMax || 6;
    this.langId = state.langId || _.keys(options.languages)[0];
    this.source = state.source || "";
    this.sourceNeedsExpanding = true;
    this.expandedSource = null;

    this.status = {
        allowCompile: false,
        allowAdd: true
    };
    this.stateByLang = {};

    this.initButtons();
    this.initLibraries(state);
    this.initCallbacks();
    this.initFromState(state);
    this.handleToolbarUI();
    ga.proxy('send', {
        hitType: 'event',
        eventCategory: 'OpenViewPane',
        eventAction: 'Conformance'
    });

    // Dismiss the popover on escape.
    $(document).on('keyup.editable', _.bind(function (e) {
        if (e.which === 27) {
            this.libsButton.popover('hide');
        }
    }, this));

    // Dismiss on any click that isn't either in the opening element, inside
    // the popover or on any alert
    $(document).on('click', _.bind(function (e) {
        var elem = this.libsButton;
        var target = $(e.target);
        if (!target.is(elem) && elem.has(target).length === 0 && target.closest('.popover').length === 0) {
            elem.popover('hide');
        }
    }, this));
}

Conformance.prototype.onLibsChanged = function () {
    this.saveState();
    this.compileAll();
};

Conformance.prototype.initLibraries = function (state) {
    this.libsWidget = new Libraries.Widget(this.langId, false, false,
        this.libsButton, state, _.bind(this.onLibsChanged, this));
};

Conformance.prototype.initButtons = function () {
    this.selectorList = this.domRoot.find('.compiler-list');
    this.addCompilerButton = this.domRoot.find('.add-compiler');
    this.selectorTemplate = $('#compiler-selector').find('.form-row');
    this.topBar = this.domRoot.find('.top-bar');
    this.libsButton = this.topBar.find('.show-libs');
    this.hideable = this.domRoot.find('.hideable');
};

Conformance.prototype.initCallbacks = function () {
    this.container.on('destroy', function () {
        this.eventHub.unsubscribe();
        this.eventHub.emit("conformanceViewClose", this.editorId);
    }, this);

    this.container.on('destroy', this.close, this);
    this.container.on('open', function () {
        this.eventHub.emit("conformanceViewOpen", this.editorId);
    }, this);

    this.container.on('resize', this.resize, this);
    this.container.on('shown', this.resize, this);
    this.eventHub.on('resize', this.resize, this);
    this.eventHub.on('editorChange', this.onEditorChange, this);
    this.eventHub.on('editorClose', this.onEditorClose, this);
    this.eventHub.on('languageChange', this.onLanguageChange, this);

    this.addCompilerButton.on('click', _.bind(function () {
        this.addCompilerSelector();
        this.saveState();
    }, this));
};

Conformance.prototype.setTitle = function (compilerCount) {
    this.container.setTitle("Conformance viewer (Editor #" + this.editorId + ") " + (
        compilerCount !== 0 ? (compilerCount + "/" + this.maxCompilations) : ""
    ));
};

Conformance.prototype.addCompilerSelector = function (config) {
    if (!config) {
        config = {
            // Compiler id which is being used
            compilerId: "",
            // Options which are in use
            options: options.compileOptions[this.langId]
        };
    }

    var newEntry = this.selectorTemplate.clone();

    var onOptionsChange = _.debounce(_.bind(function () {
        this.saveState();
        this.compileChild(newEntry);
    }, this), 800);

    var optionsField = newEntry.find('.options')
        .val(config.options)
        .on("change", onOptionsChange)
        .on("keyup", onOptionsChange);

    newEntry.find('.close').not('.extract-compiler')
        .on("click", _.bind(function () {
            this.removeCompilerSelector(newEntry);
        }, this));

    this.selectorList.append(newEntry);

    var status = newEntry.find('.status-icon');
    var prependOptions = newEntry.find('.prepend-options');
    var popCompilerButton = newEntry.find('.extract-compiler');

    var onCompilerChange = _.bind(function (compilerId) {
        popCompilerButton.toggleClass('d-none', !compilerId);
        // Hide the results icon when a new compiler is selected
        this.handleStatusIcon(status, {code: 0});
        var compiler = this.compilerService.findCompiler(this.langId, compilerId);
        if (compiler) this.setCompilationOptionsPopover(prependOptions, compiler.options);
    }, this);

    var compilerPicker = newEntry.find('.compiler-picker').selectize({
        sortField: this.compilerService.getSelectizerOrder(),
        valueField: 'id',
        labelField: 'name',
        searchField: ['name'],
        optgroupField: 'group',
        optgroups: this.compilerService.getGroupsInUse(this.langId),
        lockOptgroupOrder: true,
        options: _.map(this.getCurrentLangCompilers(), _.identity),
        items: config.compilerId ? [config.compilerId] : [],
        dropdownParent: 'body',
        closeAfterSelect: true
    }).on('change', _.bind(function (e) {
        onCompilerChange($(e.target).val());
        this.compileChild(newEntry);
    }, this));
    onCompilerChange(config.compilerId);

    var getCompilerConfig = _.bind(function () {
        return Components.getCompilerWith(
            this.editorId, undefined, optionsField.val(), compilerPicker.val(), this.langId, this.lastState.libs);
    }, this);

    this.container.layoutManager
        .createDragSource(popCompilerButton, getCompilerConfig);

    popCompilerButton.click(_.bind(function () {
        var insertPoint = this.hub.findParentRowOrColumn(this.container) ||
            this.container.layoutManager.root.contentItems[0];
        insertPoint.addChild(getCompilerConfig);
    }, this));

    this.handleToolbarUI();
    this.saveState();
};

Conformance.prototype.getCurrentLangCompilers = function () {
    return this.compilerService.getCompilersForLang(this.langId);
};

Conformance.prototype.setCompilationOptionsPopover = function (element, content) {
    element.popover('dispose');
    element.popover({
        content: content || 'No options in use',
        template: '

' +
            '

'
    });
};

Conformance.prototype.removeCompilerSelector = function (element) {
    if (element) element.remove();
    this.handleToolbarUI();
    this.saveState();
};

Conformance.prototype.expandSource = function () {
    if (this.sourceNeedsExpanding || !this.expandedSource) {
        return this.compilerService.expand(this.source).then(_.bind(function (expandedSource) {
            this.expandedSource = expandedSource;
            this.sourceNeedsExpanding = false;
            return expandedSource;
        }, this));
    }
    return Promise.resolve(this.expandedSource);
};

Conformance.prototype.onEditorChange = function (editorId, newSource, langId) {
    if (editorId === this.editorId) {
        this.langId = langId;
        this.source = newSource;
        this.sourceNeedsExpanding = true;
        this.compileAll();
    }
};

Conformance.prototype.onEditorClose = function (editorId) {
    if (editorId === this.editorId) {
        this.close();
        _.defer(function (self) {
            self.container.close();
        }, this);
    }
};

Conformance.prototype.onCompileResponse = function (child, result) {
    var allText = _.pluck((result.stdout || []).concat(result.stderr || []), 'text').join("\n");
    var failed = result.code !== 0;
    var warns = !failed && !!allText;

    this.setCompilationOptionsPopover(child.find('.prepend-options'),
        result.compilationOptions ? result.compilationOptions.join(' ') : '');
    child.find('.compiler-out')
        .prop('title', allText.replace(/\x1b\[[0-9;]*m(.\[K)?/g, ''))
        .toggleClass('d-none', !allText);
    this.handleStatusIcon(child.find('.status-icon'), {code: failed ? 3 : (warns ? 2 : 1), compilerOut: result.code});
    this.saveState();
};

Conformance.prototype.compileChild = function (child) {
    // Hide previous status icons
    var picker = child.find('.compiler-picker');

    if (!picker || !picker.val()) return;

    this.handleStatusIcon(child.find('.status-icon'), {code: 4});

    this.expandSource().then(_.bind(function (expandedSource) {
        var request = {
            source: expandedSource,
            compiler: picker.val(),
            options: {
                userArguments: child.find(".options").val() || "",
                filters: {},
                compilerOptions: {produceAst: false, produceOptInfo: false},
                libraries: []
            }
        };

        _.each(this.libsWidget.getLibsInUse(), function (item) {
            request.options.libraries.push({
                id: item.libId,
                version: item.versionId
            });
        });

        // This error function ensures that the user will know we had a problem (As we don't save asm)
        this.compilerService.submit(request)
            .then(_.bind(function (x) {
                this.onCompileResponse(child, x.result);
            }, this))
            .catch(_.bind(function (x) {
                this.onCompileResponse(child, {
                    asm: "",
                    code: -1,
                    stdout: "",
                    stderr: x.error
                });
            }, this));
    }, this));
};

Conformance.prototype.compileAll = function () {
    _.each(this.selectorList.children(), _.bind(function (child) {
        this.compileChild($(child));
    }, this));
};

Conformance.prototype.handleToolbarUI = function () {
    var compilerCount = this.selectorList.children().length;

    // Only allow new compilers if we allow for more
    this.addCompilerButton.prop("disabled", compilerCount >= this.maxCompilations);

    this.setTitle(compilerCount);
};

Conformance.prototype.handleStatusIcon = function (element, status) {
    if (!element) return;

    function ariaLabel() {
        // Compiling...
        if (status.code === 4) return "Compiling";
        if (status.compilerOut === 0) {
            // StdErr.length > 0
            if (status.code === 3) return "Compilation succeeded with errors";
            // StdOut.length > 0
            if (status.code === 2) return "Compilation succeeded with warnings";
            return "Compilation succeeded";
        } else {
            // StdErr.length > 0
            if (status.code === 3) return "Compilation failed with errors";
            // StdOut.length > 0
            if (status.code === 2) return "Compilation failed with warnings";
            return "Compilation failed";
        }
    }

    function color() {
        // Compiling...
        if (status.code === 4) return "black";
        if (status.compilerOut === 0) {
            // StdErr.length > 0
            if (status.code === 3) return "#FF6645";
            // StdOut.length > 0
            if (status.code === 2) return "#FF6500";
            return "#12BB12";
        } else {
            // StdErr.length > 0
            if (status.code === 3) return "#FF1212";
            // StdOut.length > 0
            if (status.code === 2) return "#BB8700";
            return "#FF6645";
        }
    }

    element
        .removeClass()
        .addClass('status-icon fas')
        .css('color', color())
        .toggle(status.code !== 0)
        .prop('aria-label', ariaLabel())
        .prop('data-status', status.code)
        .toggleClass('fa-spinner', status.code === 4)
        .toggleClass('fa-times-circle', status.code === 3)
        .toggleClass('fa-check-circle', status.code === 1 || status.code === 2);
};

Conformance.prototype.currentState = function () {
    var compilers = _.map(this.selectorList.children(), function (child) {
        child = $(child);
        return {
            compilerId: child.find('.compiler-picker').val() || "",
            options: child.find(".options").val() || ""
        };
    });
    return {
        editorid: this.editorId,
        langId: this.langId,
        compilers: compilers,
        libs: this.libsWidget.get()
    };
};

Conformance.prototype.saveState = function () {
    this.lastState = this.currentState();
    this.container.setState(this.lastState);
};

Conformance.prototype.resize = function () {
    this.updateHideables();
    this.selectorList.css("height", this.domRoot.height() - this.topBar.outerHeight(true));
};

Conformance.prototype.updateHideables = function () {
    this.hideable.toggle(this.domRoot.width() > this.addCompilerButton.width());
};

Conformance.prototype.updateLibraries = function () {
    this.libsWidget.setNewLangId(this.langId);
};

Conformance.prototype.onLanguageChange = function (editorId, newLangId) {
    if (editorId === this.editorId && this.langId !== newLangId) {
        var oldLangId = this.langId;
        this.stateByLang[oldLangId] = this.currentState();

        this.langId = newLangId;
        this.selectorList.children().remove();
        var langState = this.stateByLang[newLangId];
        this.initFromState(langState);
        this.updateLibraries();
        this.handleToolbarUI();
        this.saveState();
    }
};

Conformance.prototype.close = function () {
    this.eventHub.unsubscribe();
    this.eventHub.emit("conformanceViewClose", this.editorId);
};

Conformance.prototype.initFromState = function (state) {
    if (state && state.compilers) {
        this.lastState = state;
        _.each(state.compilers, _.bind(this.addCompilerSelector, this));
    } else {
        this.lastState = this.currentState();
    }
};

module.exports = {
    Conformance: Conformance
};
<commit_msg>only list libs available for all compilers<commit_after>// Copyright (c) 2017, Rubén Rincón
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

"use strict";

var options = require('../options');
var _ = require('underscore');
var $ = require('jquery');
var Promise = require('es6-promise').Promise;
var ga = require('../analytics');
var Components = require('../components');
var Libraries = require('../libs-widget');

require('selectize');

function Conformance(hub, container, state) {
    this.hub = hub;
    this.container = container;
    this.eventHub = hub.createEventHub();
    this.compilerService = hub.compilerService;
    this.domRoot = container.getElement();
    this.domRoot.html($('#conformance').html());
    this.editorId = state.editorid;
    this.maxCompilations = options.cvCompilerCountMax || 6;
    this.langId = state.langId || _.keys(options.languages)[0];
    this.source = state.source || "";
    this.sourceNeedsExpanding = true;
    this.expandedSource = null;

    this.status = {
        allowCompile: false,
        allowAdd: true
    };
    this.stateByLang = {};

    this.initButtons();
    this.initLibraries(state);
    this.initCallbacks();
    this.initFromState(state);
    this.handleToolbarUI();
    ga.proxy('send', {
        hitType: 'event',
        eventCategory: 'OpenViewPane',
        eventAction: 'Conformance'
    });

    // Dismiss the popover on escape.
    $(document).on('keyup.editable', _.bind(function (e) {
        if (e.which === 27) {
            this.libsButton.popover('hide');
        }
    }, this));

    // Dismiss on any click that isn't either in the opening element, inside
    // the popover or on any alert
    $(document).on('click', _.bind(function (e) {
        var elem = this.libsButton;
        var target = $(e.target);
        if (!target.is(elem) && elem.has(target).length === 0 && target.closest('.popover').length === 0) {
            elem.popover('hide');
        }
    }, this));
}

Conformance.prototype.onLibsChanged = function () {
    this.saveState();
    this.compileAll();
};

Conformance.prototype.initLibraries = function (state) {
    this.libsWidget = new Libraries.Widget(this.langId, false, false,
        this.libsButton, state, _.bind(this.onLibsChanged, this));
};

Conformance.prototype.initButtons = function () {
    this.selectorList = this.domRoot.find('.compiler-list');
    this.addCompilerButton = this.domRoot.find('.add-compiler');
    this.selectorTemplate = $('#compiler-selector').find('.form-row');
    this.topBar = this.domRoot.find('.top-bar');
    this.libsButton = this.topBar.find('.show-libs');
    this.hideable = this.domRoot.find('.hideable');
};

Conformance.prototype.initCallbacks = function () {
    this.container.on('destroy', function () {
        this.eventHub.unsubscribe();
        this.eventHub.emit("conformanceViewClose", this.editorId);
    }, this);

    this.container.on('destroy', this.close, this);
    this.container.on('open', function () {
        this.eventHub.emit("conformanceViewOpen", this.editorId);
    }, this);

    this.container.on('resize', this.resize, this);
    this.container.on('shown', this.resize, this);
    this.eventHub.on('resize', this.resize, this);
    this.eventHub.on('editorChange', this.onEditorChange, this);
    this.eventHub.on('editorClose', this.onEditorClose, this);
    this.eventHub.on('languageChange', this.onLanguageChange, this);

    this.addCompilerButton.on('click', _.bind(function () {
        this.addCompilerSelector();
        this.saveState();
    }, this));
};

Conformance.prototype.setTitle = function (compilerCount) {
    this.container.setTitle("Conformance viewer (Editor #" + this.editorId + ") " + (
        compilerCount !== 0 ? (compilerCount + "/" + this.maxCompilations) : ""
    ));
};

Conformance.prototype.addCompilerSelector = function (config) {
    if (!config) {
        config = {
            // Compiler id which is being used
            compilerId: "",
            // Options which are in use
            options: options.compileOptions[this.langId]
        };
    }

    var newEntry = this.selectorTemplate.clone();

    var onOptionsChange = _.debounce(_.bind(function () {
        this.saveState();
        this.compileChild(newEntry);
    }, this), 800);

    var optionsField = newEntry.find('.options')
        .val(config.options)
        .on("change", onOptionsChange)
        .on("keyup", onOptionsChange);

    newEntry.find('.close').not('.extract-compiler')
        .on("click", _.bind(function () {
            this.removeCompilerSelector(newEntry);
        }, this));

    this.selectorList.append(newEntry);

    var status = newEntry.find('.status-icon');
    var prependOptions = newEntry.find('.prepend-options');
    var popCompilerButton = newEntry.find('.extract-compiler');

    var onCompilerChange = _.bind(function (compilerId) {
        popCompilerButton.toggleClass('d-none', !compilerId);
        // Hide the results icon when a new compiler is selected
        this.handleStatusIcon(status, {code: 0});
        var compiler = this.compilerService.findCompiler(this.langId, compilerId);
        if (compiler) this.setCompilationOptionsPopover(prependOptions, compiler.options);
        this.updateLibraries();
    }, this);

    var compilerPicker = newEntry.find('.compiler-picker').selectize({
        sortField: this.compilerService.getSelectizerOrder(),
        valueField: 'id',
        labelField: 'name',
        searchField: ['name'],
        optgroupField: 'group',
        optgroups: this.compilerService.getGroupsInUse(this.langId),
        lockOptgroupOrder: true,
        options: _.map(this.getCurrentLangCompilers(), _.identity),
        items: config.compilerId ? [config.compilerId] : [],
        dropdownParent: 'body',
        closeAfterSelect: true
    }).on('change', _.bind(function (e) {
        onCompilerChange($(e.target).val());
        this.compileChild(newEntry);
    }, this));
    onCompilerChange(config.compilerId);

    var getCompilerConfig = _.bind(function () {
        return Components.getCompilerWith(
            this.editorId, undefined, optionsField.val(), compilerPicker.val(), this.langId, this.lastState.libs);
    }, this);

    this.container.layoutManager
        .createDragSource(popCompilerButton, getCompilerConfig);

    popCompilerButton.click(_.bind(function () {
        var insertPoint = this.hub.findParentRowOrColumn(this.container) ||
            this.container.layoutManager.root.contentItems[0];
        insertPoint.addChild(getCompilerConfig);
    }, this));

    this.handleToolbarUI();
    this.saveState();
};

Conformance.prototype.getCurrentLangCompilers = function () {
    return this.compilerService.getCompilersForLang(this.langId);
};

Conformance.prototype.setCompilationOptionsPopover = function (element, content) {
    element.popover('dispose');
    element.popover({
        content: content || 'No options in use',
        template: '

' +
            '

'
    });
};

Conformance.prototype.removeCompilerSelector = function (element) {
    if (element) element.remove();
    this.handleToolbarUI();
    this.saveState();
};

Conformance.prototype.expandSource = function () {
    if (this.sourceNeedsExpanding || !this.expandedSource) {
        return this.compilerService.expand(this.source).then(_.bind(function (expandedSource) {
            this.expandedSource = expandedSource;
            this.sourceNeedsExpanding = false;
            return expandedSource;
        }, this));
    }
    return Promise.resolve(this.expandedSource);
};

Conformance.prototype.onEditorChange = function (editorId, newSource, langId) {
    if (editorId === this.editorId) {
        this.langId = langId;
        this.source = newSource;
        this.sourceNeedsExpanding = true;
        this.compileAll();
    }
};

Conformance.prototype.onEditorClose = function (editorId) {
    if (editorId === this.editorId) {
        this.close();
        _.defer(function (self) {
            self.container.close();
        }, this);
    }
};

Conformance.prototype.onCompileResponse = function (child, result) {
    var allText = _.pluck((result.stdout || []).concat(result.stderr || []), 'text').join("\n");
    var failed = result.code !== 0;
    var warns = !failed && !!allText;

    this.setCompilationOptionsPopover(child.find('.prepend-options'),
        result.compilationOptions ? result.compilationOptions.join(' ') : '');
    child.find('.compiler-out')
        .prop('title', allText.replace(/\x1b\[[0-9;]*m(.\[K)?/g, ''))
        .toggleClass('d-none', !allText);
    this.handleStatusIcon(child.find('.status-icon'), {code: failed ? 3 : (warns ? 2 : 1), compilerOut: result.code});
    this.saveState();
};

Conformance.prototype.compileChild = function (child) {
    // Hide previous status icons
    var picker = child.find('.compiler-picker');

    if (!picker || !picker.val()) return;

    this.handleStatusIcon(child.find('.status-icon'), {code: 4});

    this.expandSource().then(_.bind(function (expandedSource) {
        var request = {
            source: expandedSource,
            compiler: picker.val(),
            options: {
                userArguments: child.find(".options").val() || "",
                filters: {},
                compilerOptions: {produceAst: false, produceOptInfo: false},
                libraries: []
            }
        };

        _.each(this.libsWidget.getLibsInUse(), function (item) {
            request.options.libraries.push({
                id: item.libId,
                version: item.versionId
            });
        });

        // This error function ensures that the user will know we had a problem (As we don't save asm)
        this.compilerService.submit(request)
            .then(_.bind(function (x) {
                this.onCompileResponse(child, x.result);
            }, this))
            .catch(_.bind(function (x) {
                this.onCompileResponse(child, {
                    asm: "",
                    code: -1,
                    stdout: "",
                    stderr: x.error
                });
            }, this));
    }, this));
};

Conformance.prototype.compileAll = function () {
    _.each(this.selectorList.children(), _.bind(function (child) {
        this.compileChild($(child));
    }, this));
};

Conformance.prototype.handleToolbarUI = function () {
    var compilerCount = this.selectorList.children().length;

    // Only allow new compilers if we allow for more
    this.addCompilerButton.prop("disabled", compilerCount >= this.maxCompilations);

    this.setTitle(compilerCount);
};

Conformance.prototype.handleStatusIcon = function (element, status) {
    if (!element) return;

    function ariaLabel() {
        // Compiling...
        if (status.code === 4) return "Compiling";
        if (status.compilerOut === 0) {
            // StdErr.length > 0
            if (status.code === 3) return "Compilation succeeded with errors";
            // StdOut.length > 0
            if (status.code === 2) return "Compilation succeeded with warnings";
            return "Compilation succeeded";
        } else {
            // StdErr.length > 0
            if (status.code === 3) return "Compilation failed with errors";
            // StdOut.length > 0
            if (status.code === 2) return "Compilation failed with warnings";
            return "Compilation failed";
        }
    }

    function color() {
        // Compiling...
        if (status.code === 4) return "black";
        if (status.compilerOut === 0) {
            // StdErr.length > 0
            if (status.code === 3) return "#FF6645";
            // StdOut.length > 0
            if (status.code === 2) return "#FF6500";
            return "#12BB12";
        } else {
            // StdErr.length > 0
            if (status.code === 3) return "#FF1212";
            // StdOut.length > 0
            if (status.code === 2) return "#BB8700";
            return "#FF6645";
        }
    }

    element
        .removeClass()
        .addClass('status-icon fas')
        .css('color', color())
        .toggle(status.code !== 0)
        .prop('aria-label', ariaLabel())
        .prop('data-status', status.code)
        .toggleClass('fa-spinner', status.code === 4)
        .toggleClass('fa-times-circle', status.code === 3)
        .toggleClass('fa-check-circle', status.code === 1 || status.code === 2);
};

Conformance.prototype.currentState = function () {
    var compilers = _.map(this.selectorList.children(), function (child) {
        child = $(child);
        return {
            compilerId: child.find('.compiler-picker').val() || "",
            options: child.find(".options").val() || ""
        };
    });
    return {
        editorid: this.editorId,
        langId: this.langId,
        compilers: compilers,
        libs: this.libsWidget.get()
    };
};

Conformance.prototype.saveState = function () {
    this.lastState = this.currentState();
    this.container.setState(this.lastState);
};

Conformance.prototype.resize = function () {
    this.updateHideables();
    this.selectorList.css("height", this.domRoot.height() - this.topBar.outerHeight(true));
};

Conformance.prototype.updateHideables = function () {
    this.hideable.toggle(this.domRoot.width() > this.addCompilerButton.width());
};

Conformance.prototype.updateLibraries = function () {
    var compilers = _.map(this.selectorList.children(), _.bind(function (child) {
        var compilerId = $(child).find('.compiler-picker').val();
        if (compilerId) {
            return this.compilerService.findCompiler(this.langId, compilerId);
        } else {
            return false;
        }
    }, this));

    var compilerIds = false;
    var libraries = {};
    var first = true;
    _.forEach(compilers, function (compiler) {
        if (compiler) {
            if (compilerIds) {
                compilerIds = compilerIds + "|" + compiler.id;
            } else {
                compilerIds = compiler.id;
            }

            if (first) {
                libraries = _.extend({}, compiler.libs);
                first = false;
            } else {
                libraries = _.omit(libraries, function (value, key) {
                    return !_.keys(compiler.libs).includes(key);
                });
            }
        }
    });

    this.libsWidget.setNewLangId(this.langId, compilerIds, libraries);
};

Conformance.prototype.onLanguageChange = function (editorId, newLangId) {
    if (editorId === this.editorId && this.langId !== newLangId) {
        var oldLangId = this.langId;
        this.stateByLang[oldLangId] = this.currentState();

        this.langId = newLangId;
        this.selectorList.children().remove();
        var langState = this.stateByLang[newLangId];
        this.initFromState(langState);
        this.updateLibraries();
        this.handleToolbarUI();
        this.saveState();
    }
};

Conformance.prototype.close = function () {
    this.eventHub.unsubscribe();
    this.eventHub.emit("conformanceViewClose", this.editorId);
};

Conformance.prototype.initFromState = function (state) {
    if (state && state.compilers) {
        this.lastState = state;
        _.each(state.compilers, _.bind(this.addCompilerSelector, this));
    } else {
        this.lastState = this.currentState();
    }
};

module.exports = {
    Conformance: Conformance
};
<|endoftext|>


<commit_before>var crypto = require('crypto')
  , valid  = require('./valid');

function _notAllowed(req, res) { res.send(new Response.MethodNotAllowed()); }

function _noCache(req, res, next) {
  res.set({
    'Cache-Control' : 'no-cache, no-store, must-revalidate',
    'Pragma'        : 'no-cache',
    'Expires'       : 0
  });

  next();
}

module.exports = function (api) {
  api.use(function (req, res, next) {
    res.header('Access-Control-Allow-Origin', req.headers['origin'] || '*');
    res.header('Access-Control-Allow-Headers', 'Content-Type, If-Modified-Since, X-Debug');
    res.header('Access-Control-Expose-Headers', 'Cache-Control, Content-Disposition, Content-Type, Expires, Last-Modified, Location, Pragma, Vary');
    res.header('Access-Control-Allow-Methods', 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS');
    res.header('Access-Control-Allow-Credentials', 'true');
    res.header('Access-Control-Max-Age', 60 * 60);

    if ('OPTIONS' == req.method) {
      res.status(200).end();
      return;
    }

    next();
  });

  api.get ('/', _notAllowed);

  api.post('/', _noCache, function (req, res, next) {
    let hash = crypto.createHash(config['hash'].algorithm);

    hash.update(req.get('X-Real-IP') + config['hash'].salt + req.get('User-Agent'));

    let key = hash.digest('base64');

    Promise.try(function () {
      req.body = _.pick(req.body, (v) => (!_.isEmpty(v) || _.isFinite(v)));

      valid(req.body, {
        ts   : 'required|numeric',
        lon  : 'required|numeric|minVal:-180|maxVal:180',
        lat  : 'required|numeric|minVal:-90|maxVal:90',
        prec : 'numeric'
      });

      return db.query([
          'INSERT INTO "protest"."members"("received", "uid", "ts", "position", "precision", "note") VALUES (',
              'to_timestamp($1) AT TIME ZONE \'UTC\',',
              '$2,',
              'to_timestamp($3) AT TIME ZONE \'UTC\',',
              'topology.ST_GeomFromText(\'POINT(\' || $4 || \' \' || $5 || \')\', 4326),',
              '$6,',
              '$7',
            ')'
          ].join(' '), [
            req.now,
            key,
            req.body['ts'],
            req.body['lon'].toString(), req.body['lat'].toString(),
            parseInt(req.body['prec']) || null,
            req.body['msg'] || null
          ]
        );
    }).catch(function (err) {
      switch (err.routine) {
        case '_bt_check_unique':
          return;

        case 'ExecConstraints':
          switch (err.constraint) {
            case 'members_ts_valid':
              throw new Response.BadRequest('Protester from the future');
          }
      }

      throw err;
    }).then(function () {
      return db.query([
            'SELECT f.type, f.name, f.extra, (',
                'SELECT COUNT(DISTINCT t."uid") FROM "protest"."members" t WHERE t."fixed" = m."fixed"',
                'AND date_trunc(\'minute\'::text, t."ts") >= date_trunc(\'minute\'::text, to_timestamp($1) AT TIME ZONE \'UTC\')',
                'AND date_trunc(\'minute\'::text, t."ts") <= date_trunc(\'minute\'::text, to_timestamp($2) AT TIME ZONE \'UTC\')',
              ') AS "total"',
            'FROM "protest"."members" m, "position"."fixed" f WHERE m.fixed = f.id AND m."uid" = $3',
            'AND date_trunc(\'minute\'::text, m."ts") = date_trunc(\'minute\'::text, to_timestamp($2) AT TIME ZONE \'UTC\')'
          ].join(' '), [
            req.body['ts'] - config['trail'],
            req.body['ts'],
            key
          ]
        );
    }).then(function (result) {
      if (!result.rowCount) {
        return res.send(new Response.OK({
            type  : null,
            name  : null,
            count : 0,

            lon : 26.085903,
            lat : 44.452702
          }));
      }

      result = result.rows[0];

      res.send(new Response.OK({
        type  : result.type,
        name  : result.name,
        count : result.total,

        lon : 26.085903,
        lat : 44.452702
      }));
    }).catch(valid.Error, function (e) {
      throw new Response.BadRequest(_.values(_.values(e.errors)[0])[0][0]);
    }).catch(function (err) {
      next(err);
    }).done();
  });
};
<commit_msg>Blank User-Agent<commit_after>var crypto = require('crypto')
  , valid  = require('./valid');

function _notAllowed(req, res) { res.send(new Response.MethodNotAllowed()); }

function _noCache(req, res, next) {
  res.set({
    'Cache-Control' : 'no-cache, no-store, must-revalidate',
    'Pragma'        : 'no-cache',
    'Expires'       : 0
  });

  next();
}

module.exports = function (api) {
  api.use(function (req, res, next) {
    res.header('Access-Control-Allow-Origin', req.headers['origin'] || '*');
    res.header('Access-Control-Allow-Headers', 'Content-Type, If-Modified-Since, X-Debug');
    res.header('Access-Control-Expose-Headers', 'Cache-Control, Content-Disposition, Content-Type, Expires, Last-Modified, Location, Pragma, Vary');
    res.header('Access-Control-Allow-Methods', 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS');
    res.header('Access-Control-Allow-Credentials', 'true');
    res.header('Access-Control-Max-Age', 60 * 60);

    if ('OPTIONS' == req.method) {
      res.status(200).end();
      return;
    }

    next();
  });

  api.get ('/', _notAllowed);

  api.post('/', _noCache, function (req, res, next) {
    let hash = crypto.createHash(config['hash'].algorithm);

    hash.update(req.get('X-Real-IP') + config['hash'].salt + (req.get('User-Agent') || ''));

    let key = hash.digest('base64');

    Promise.try(function () {
      req.body = _.pick(req.body, (v) => (!_.isEmpty(v) || _.isFinite(v)));

      valid(req.body, {
        ts   : 'required|numeric',
        lon  : 'required|numeric|minVal:-180|maxVal:180',
        lat  : 'required|numeric|minVal:-90|maxVal:90',
        prec : 'numeric'
      });

      return db.query([
          'INSERT INTO "protest"."members"("received", "uid", "ts", "position", "precision", "note") VALUES (',
              'to_timestamp($1) AT TIME ZONE \'UTC\',',
              '$2,',
              'to_timestamp($3) AT TIME ZONE \'UTC\',',
              'topology.ST_GeomFromText(\'POINT(\' || $4 || \' \' || $5 || \')\', 4326),',
              '$6,',
              '$7',
            ')'
          ].join(' '), [
            req.now,
            key,
            req.body['ts'],
            req.body['lon'].toString(), req.body['lat'].toString(),
            parseInt(req.body['prec']) || null,
            req.body['msg'] || null
          ]
        );
    }).catch(function (err) {
      switch (err.routine) {
        case '_bt_check_unique':
          return;

        case 'ExecConstraints':
          switch (err.constraint) {
            case 'members_ts_valid':
              throw new Response.BadRequest('Protester from the future');
          }
      }

      throw err;
    }).then(function () {
      return db.query([
            'SELECT f.type, f.name, f.extra, (',
                'SELECT COUNT(DISTINCT t."uid") FROM "protest"."members" t WHERE t."fixed" = m."fixed"',
                'AND date_trunc(\'minute\'::text, t."ts") >= date_trunc(\'minute\'::text, to_timestamp($1) AT TIME ZONE \'UTC\')',
                'AND date_trunc(\'minute\'::text, t."ts") <= date_trunc(\'minute\'::text, to_timestamp($2) AT TIME ZONE \'UTC\')',
              ') AS "total"',
            'FROM "protest"."members" m, "position"."fixed" f WHERE m.fixed = f.id AND m."uid" = $3',
            'AND date_trunc(\'minute\'::text, m."ts") = date_trunc(\'minute\'::text, to_timestamp($2) AT TIME ZONE \'UTC\')'
          ].join(' '), [
            req.body['ts'] - config['trail'],
            req.body['ts'],
            key
          ]
        );
    }).then(function (result) {
      if (!result.rowCount) {
        return res.send(new Response.OK({
            type  : null,
            name  : null,
            count : 0,

            lon : 26.085903,
            lat : 44.452702
          }));
      }

      result = result.rows[0];

      res.send(new Response.OK({
        type  : result.type,
        name  : result.name,
        count : result.total,

        lon : 26.085903,
        lat : 44.452702
      }));
    }).catch(valid.Error, function (e) {
      throw new Response.BadRequest(_.values(_.values(e.errors)[0])[0][0]);
    }).catch(function (err) {
      next(err);
    }).done();
  });
};
<|endoftext|>


<commit_before>/**
 * API function to create fetch store.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import invariant from 'invariant';

export function createErrorStore() {
	const RECEIVE_ERROR = 'RECEIVE_ERROR';

	const INITIAL_STATE = {
		errors: {},
		error: undefined,
	};

	const actions = {
		receiveError( error, baseName, args ) {
			invariant( error, 'error is required.' );

			return {
				type: RECEIVE_ERROR,
				payload: {
					error,
					baseName,
					args,
				},
			};
		},
	};

	const controls = {};

	function reducer( state, { type, payload } ) {
		switch ( type ) {
			case RECEIVE_ERROR: {
				const { baseName, args, error } = payload;
				const newState = { ...state };

				if ( baseName ) {
					let key = baseName;
					if ( args ) {
						key += JSON.stringify( args );
					}

					delete newState.error;

					newState.errors = {
						...( state.errors || {} ),
						[ key ]: error,
					};
				} else {
					newState.error = error;
				}

				return newState;
			}

			default: {
				return { ...state };
			}
		}
	}

	const resolvers = {};

	const selectors = {
		getErrorForSelector( state, selectorName, args ) {
			invariant( selectorName, 'selectorName is required.' );
			return selectors.getError( state, selectorName, args );
		},

		getErrorForAction( state, actionName, args ) {
			invariant( actionName, 'actionName is required.' );
			return selectors.getError( state, actionName, args );
		},

		/**
		 * Retrieves the error object from state.
		 *
		 * Returns `undefined` if there is no error set.
		 *```
		 * {
		 *   code: <String>,
		 *   message: <String>,
		 *   data: <Object>
		 * }
		 * ```
		 *
		 * @since n.e.x.t
		 * @private
		 *
		 * @param {Object} state Data store's state.
		 * @param {string} [baseName] A selector or action name.
		 * @param {Object} [args] An arguments object.
		 * @return {(Object|undefined)} Error object if exists, otherwise undefined.
		 */
		getError( state, baseName, args ) {
			if ( ! baseName && ! args ) {
				return state.error;
			}

			if ( baseName ) {
				const { errors } = state;
				let key = baseName;
				if ( args ) {
					key += JSON.stringify( args );
				}

				return errors[ key ];
			}

			return undefined;
		},
	};

	return {
		INITIAL_STATE,
		actions,
		controls,
		reducer,
		resolvers,
		selectors,
	};
}
<commit_msg>Add clearError action.<commit_after>/**
 * API function to create fetch store.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import invariant from 'invariant';

const RECEIVE_ERROR = 'RECEIVE_ERROR';
const CLEAR_ERROR = 'CLEAR_ERROR';

function generateErrorKey( baseName, args ) {
	let key = baseName;
	if ( args ) {
		key += JSON.stringify( args );
	}

	return key;
}

function reducer( state, { type, payload } ) {
	switch ( type ) {
		case RECEIVE_ERROR: {
			const { baseName, args, error } = payload;
			const newState = { ...state };

			if ( baseName ) {
				newState.errors = {
					...( state.errors || {} ),
					[ generateErrorKey( baseName, args ) ]: error,
				};
			} else {
				newState.error = error;
			}

			return newState;
		}

		case CLEAR_ERROR: {
			const { baseName, args } = payload;
			const key = generateErrorKey( baseName, args );

			return {
				...state,
				errors: Object
					.keys( state.errors )
					.filter( ( errorKey ) => errorKey !== key )
					.reduce( ( errors, errorKey ) => ( { ...errors, [ errorKey ]: state.errors[ errorKey ] } ), {} ),
			};
		}

		default: {
			return { ...state };
		}
	}
}

export function createErrorStore() {
	const INITIAL_STATE = {
		errors: {},
		error: undefined,
	};

	const actions = {
		receiveError( error, baseName, args ) {
			invariant( error, 'error is required.' );

			return {
				type: RECEIVE_ERROR,
				payload: {
					error,
					baseName,
					args,
				},
			};
		},

		clearError( baseName, args ) {
			return {
				type: CLEAR_ERROR,
				payload: {
					baseName,
					args,
				},
			};
		},
	};

	const controls = {};

	const resolvers = {};

	const selectors = {
		getErrorForSelector( state, selectorName, args ) {
			invariant( selectorName, 'selectorName is required.' );
			return selectors.getError( state, selectorName, args );
		},

		getErrorForAction( state, actionName, args ) {
			invariant( actionName, 'actionName is required.' );
			return selectors.getError( state, actionName, args );
		},

		/**
		 * Retrieves the error object from state.
		 *
		 * Returns `undefined` if there is no error set.
		 *```
		 * {
		 *   code: <String>,
		 *   message: <String>,
		 *   data: <Object>
		 * }
		 * ```
		 *
		 * @since n.e.x.t
		 * @private
		 *
		 * @param {Object} state Data store's state.
		 * @param {string} [baseName] A selector or action name.
		 * @param {Object} [args] An arguments object.
		 * @return {(Object|undefined)} Error object if exists, otherwise undefined.
		 */
		getError( state, baseName, args ) {
			if ( ! baseName && ! args ) {
				return state.error;
			}

			if ( baseName ) {
				const { errors } = state;

				return errors[ generateErrorKey( baseName, args ) ];
			}

			return undefined;
		},
	};

	return {
		INITIAL_STATE,
		actions,
		controls,
		reducer,
		resolvers,
		selectors,
	};
}
<|endoftext|>


<commit_before>/**
 * Search Console dashboard utility functions.
 *
 * Site Kit by Google, Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import { changeToPercent, readableLargeNumber } from 'GoogleUtil';
import { each } from 'lodash';

let searchConsoleData = false;

function reduceSearchConsoleData( rows ) {
	const dataMap = [
		[
			{ type: 'string', label: 'Day' },
			{ type: 'number', label: 'Clicks' },
			{ type: 'number', label: 'Impressions' },
			{ type: 'number', label: 'CTR' },
			{ type: 'number', label: 'Position' },
		],
	];

	let totalClicks = 0;
	let totalImpressions = 0;
	let totalCTR = 0;
	let totalPosition = 0;
	const count = rows.length;
	each( rows, ( row ) => {
		const date = new Date( row.keys[ 0 ] );
		dataMap.push( [
			( date.getMonth() + 1 ) + '/' + date.getUTCDate(),
			row.clicks,
			row.impressions,
			row.ctr,
			row.position,
		] );
		totalClicks += row.clicks;
		totalImpressions += row.impressions;
		totalCTR += row.ctr;
		totalPosition += row.position;
	} );

	const totalClicksRaw = totalClicks;
	const totalImpressionsRaw = totalImpressions;

	totalClicks = readableLargeNumber( totalClicks );
	totalImpressions = readableLargeNumber( totalImpressions );

	const averageCTR = ( totalCTR / count * 100 ).toFixed( 1 );
	const averageCTRRaw = totalCTR / count;
	const averagePosition = ( totalPosition / count ).toFixed( 1 );

	return {
		dataMap,
		totalClicks,
		totalClicksRaw,
		totalImpressions,
		totalImpressionsRaw,
		averageCTR,
		averageCTRRaw,
		averagePosition,
	};
}

export const extractSearchConsoleDashboardData = ( rows ) => {
	// Split the results in two chunks.
	const half = Math.floor( rows.length / 2 );
	const lastMonthRows = rows.slice( rows.length - half, rows.length );
	const previousMonthRows = rows.slice( 0, rows.length - half );

	const lastMonth = reduceSearchConsoleData( lastMonthRows );
	const previousMonth = reduceSearchConsoleData( previousMonthRows );

	const totalClicksChange = changeToPercent( previousMonth.totalClicksRaw, lastMonth.totalClicksRaw );
	const totalImpressionsChange = changeToPercent( previousMonth.totalImpressionsRaw, lastMonth.totalImpressionsRaw );
	const averageCTRChange = changeToPercent( previousMonth.averageCTRRaw, lastMonth.averageCTRRaw );
	const averagePositionChange = changeToPercent( previousMonth.averagePosition, lastMonth.averagePosition );

	searchConsoleData = {
		dataMap: lastMonth.dataMap,
		totalClicks: lastMonth.totalClicks,
		totalImpressions: lastMonth.totalImpressions,
		averageCTR: lastMonth.averageCTR,
		averagePosition: lastMonth.averagePosition,
		totalClicksChange,
		totalImpressionsChange,
		averageCTRChange,
		averagePositionChange,
	};
	return searchConsoleData;
};

/**
 * Check for Zero data from Search Console API.
 *
 * @param {Object} data The data returned from the Search Console API call.
 * @return {boolean}
 */
export const isDataZeroSearchConsole = ( data ) => {
	if ( ! data.length ) {
		return true;
	}

	const processedData = extractSearchConsoleDashboardData( data );

	const {
		totalClicks,
		totalImpressions,
		averageCTR,
		averagePosition,
	} = processedData;

	return (
		0 === parseInt( totalClicks ) &&
			0 === parseInt( totalImpressions ) &&
			0 === parseInt( averageCTR ) &&
			0 === parseInt( averagePosition )
	);
};
<commit_msg>refactor extractSearchConsoleDashboardData<commit_after>/**
 * Search Console dashboard utility functions.
 *
 * Site Kit by Google, Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import { changeToPercent, readableLargeNumber } from 'GoogleUtil';
import { each } from 'lodash';

function reduceSearchConsoleData( rows ) {
	const dataMap = [
		[
			{ type: 'string', label: 'Day' },
			{ type: 'number', label: 'Clicks' },
			{ type: 'number', label: 'Impressions' },
			{ type: 'number', label: 'CTR' },
			{ type: 'number', label: 'Position' },
		],
	];

	let totalClicks = 0;
	let totalImpressions = 0;
	let totalCTR = 0;
	let totalPosition = 0;
	const count = rows.length;
	each( rows, ( row ) => {
		const date = new Date( row.keys[ 0 ] );
		dataMap.push( [
			( date.getMonth() + 1 ) + '/' + date.getUTCDate(),
			row.clicks,
			row.impressions,
			row.ctr,
			row.position,
		] );
		totalClicks += row.clicks;
		totalImpressions += row.impressions;
		totalCTR += row.ctr;
		totalPosition += row.position;
	} );

	const totalClicksRaw = totalClicks;
	const totalImpressionsRaw = totalImpressions;

	totalClicks = readableLargeNumber( totalClicks );
	totalImpressions = readableLargeNumber( totalImpressions );

	const averageCTR = ( totalCTR / count * 100 ).toFixed( 1 );
	const averageCTRRaw = totalCTR / count;
	const averagePosition = ( totalPosition / count ).toFixed( 1 );

	return {
		dataMap,
		totalClicks,
		totalClicksRaw,
		totalImpressions,
		totalImpressionsRaw,
		averageCTR,
		averageCTRRaw,
		averagePosition,
	};
}

export const extractSearchConsoleDashboardData = ( rows ) => {
	// Split the results in two chunks.
	const half = Math.floor( rows.length / 2 );
	// Rows are from oldest to newest.
	const latestData = reduceSearchConsoleData( rows.slice( half ) );
	const olderData = reduceSearchConsoleData( rows.slice( 0, half ) );

	return {
		dataMap: latestData.dataMap,
		totalClicks: latestData.totalClicks,
		totalImpressions: latestData.totalImpressions,
		averageCTR: latestData.averageCTR,
		averagePosition: latestData.averagePosition,
		totalClicksChange: changeToPercent( olderData.totalClicksRaw, latestData.totalClicksRaw ),
		totalImpressionsChange: changeToPercent( olderData.totalImpressionsRaw, latestData.totalImpressionsRaw ),
		averageCTRChange: changeToPercent( olderData.averageCTRRaw, latestData.averageCTRRaw ),
		averagePositionChange: changeToPercent( olderData.averagePosition, latestData.averagePosition ),
	};
};

/**
 * Check for Zero data from Search Console API.
 *
 * @param {Object} data The data returned from the Search Console API call.
 * @return {boolean}
 */
export const isDataZeroSearchConsole = ( data ) => {
	if ( ! data.length ) {
		return true;
	}

	const processedData = extractSearchConsoleDashboardData( data );

	const {
		totalClicks,
		totalImpressions,
		averageCTR,
		averagePosition,
	} = processedData;

	return (
		0 === parseInt( totalClicks ) &&
			0 === parseInt( totalImpressions ) &&
			0 === parseInt( averageCTR ) &&
			0 === parseInt( averagePosition )
	);
};
<|endoftext|>


<commit_before>#! /usr/bin/env node

/* A script for extracting and parsing the top 600 project Euler programming tasks.
 *
 * All Project Euler content available under CC Share-Alike 2.0 England and Wales
 * See: https://projecteuler.net/copyright
 *
 */

/* eslint no-underscore-dangle: 0 */

const fs = require('fs');
const path = require('path');
const fetch = require('node-fetch');

const url = 'https://raw.githubusercontent.com/freeCodeCamp/freeCodeCamp/staging/seed/challenges/08-coding-interview-questions-and-take-home-assignments/project-euler-problems.json';

const outputPath = 'client/scripts/challenges/projecteuler/raw';

const pathNames = ['001_to_100', '101_to_200', '201_to_300', '301_to_400', '401_to_500', '501_to_600'];

pathNames.forEach(pathName => ensureDirectoryExistence(`${outputPath}/${pathName}/`));

fetch(url)
  .then(res => res.json())
  .then(content => content.challenges)
  .then(challenges => {
    challenges.forEach(challenge => {
      const id = challenge._id;
      const title = challenge.title;
      let escapedTitle = title.replace(/\//g, '&');
      const description = challenge.description.map(line => `<p class="euler__paragraph">${line}</p>`).join('').replace(/^(.*)$/, '
$1
');
      const challengeSeed = challenge.challengeSeed.join('\n');
      const tests = challenge.tests[0];
      const templatedChallenge = toInHouseTemplate(title, description, challengeSeed, tests, id);
      const problemNumberRegex = /^Problem (\d+):/;
      const problemNumber = +title.match(problemNumberRegex)[1];
      let subPath = '';

      if (problemNumber <= 100) {
        subPath = pathNames[0]; // 1_to_100
        escapedTitle = escapedTitle.replace(/^Problem (\d+):/, (match, m1) => {
          let paddedNumber = m1;
          if (m1.length === 1) {
            paddedNumber = `00${m1}`;
          }
          else if (m1.length === 2) {
            paddedNumber = `0${m1}`;
          }
          return `Problem ${paddedNumber}:`;
        });
      } else if (problemNumber <= 200) {
        subPath = pathNames[1];
      } else if (problemNumber <= 300) {
        subPath = pathNames[2];
      } else if (problemNumber <= 400) {
        subPath = pathNames[3];
      } else if (problemNumber <= 500) {
        subPath = pathNames[4];
      } else {
        subPath = pathNames[5];
      }

      fs.writeFileSync(`${outputPath}/${subPath}/${escapedTitle}.raw`, templatedChallenge);
    });
  })
  .catch(err => console.error(err));

// Helpers
// =======

function ensureDirectoryExistence(filePath) {
  if (fs.existsSync(filePath)) {
    return true;
  }
  ensureDirectoryExistence(path.dirname(filePath));
  fs.mkdirSync(filePath);
}

function toInHouseTemplate (title, description, challengeSeed, tests, id) {
  const templatedContent = `
/* eslint spaced-comment: 0 */
/* eslint no-redeclare: 0 */
/* eslint no-unused-vars: 0 */

const assert = require('chai').assert;

/// title: ${title}
/// type: project-euler

/// categories:
math

/// difficulty: ?

/// benchmark:
//replaceWithActualFunctionHere;

/// description:
${description}

/// challengeSeed:
${challengeSeed}

/// solutions:

/// tail:
const replaceThis = 3;

/// tests:
${tests}
/// id: ${id}
`;
  return templatedContent;
}
<commit_msg>update output tempate:<commit_after>#! /usr/bin/env node

/* A script for extracting and parsing the top 600 project Euler programming tasks.
 *
 * All Project Euler content available under CC Share-Alike 2.0 England and Wales
 * See: https://projecteuler.net/copyright
 *
 */

/* eslint no-underscore-dangle: 0 */

const fs = require('fs');
const path = require('path');
const fetch = require('node-fetch');

const url = 'https://raw.githubusercontent.com/freeCodeCamp/freeCodeCamp/staging/seed/challenges/08-coding-interview-questions-and-take-home-assignments/project-euler-problems.json';

const outputPath = 'client/scripts/challenges/projecteuler/raw';

const pathNames = ['001_to_100', '101_to_200', '201_to_300', '301_to_400', '401_to_500', '501_to_600'];

pathNames.forEach(pathName => ensureDirectoryExistence(`${outputPath}/${pathName}/`));

fetch(url)
  .then(res => res.json())
  .then(content => content.challenges)
  .then(challenges => {
    challenges.forEach(challenge => {
      const id = challenge._id;
      const title = challenge.title;
      let escapedTitle = title.replace(/\//g, '&');
      const description = challenge.description.map(line => `<p class="euler__paragraph">${line}</p>`).join('').replace(/^(.*)$/, '
$1
');
      const challengeSeed = challenge.challengeSeed.join('\n');
      const tests = challenge.tests[0];
      const templatedChallenge = toInHouseTemplate(title, description, challengeSeed, tests, id);
      const problemNumberRegex = /^Problem (\d+):/;
      const problemNumber = +title.match(problemNumberRegex)[1];
      let subPath = '';

      if (problemNumber <= 100) {
        subPath = pathNames[0]; // 1_to_100
        escapedTitle = escapedTitle.replace(/^Problem (\d+):/, (match, m1) => {
          let paddedNumber = m1;
          if (m1.length === 1) {
            paddedNumber = `00${m1}`;
          }
          else if (m1.length === 2) {
            paddedNumber = `0${m1}`;
          }
          return `Problem ${paddedNumber}:`;
        });
      } else if (problemNumber <= 200) {
        subPath = pathNames[1];
      } else if (problemNumber <= 300) {
        subPath = pathNames[2];
      } else if (problemNumber <= 400) {
        subPath = pathNames[3];
      } else if (problemNumber <= 500) {
        subPath = pathNames[4];
      } else {
        subPath = pathNames[5];
      }

      fs.writeFileSync(`${outputPath}/${subPath}/${escapedTitle}.raw`, templatedChallenge);
    });
  })
  .catch(err => console.error(err));

// Helpers
// =======

function ensureDirectoryExistence(filePath) {
  if (fs.existsSync(filePath)) {
    return true;
  }
  ensureDirectoryExistence(path.dirname(filePath));
  fs.mkdirSync(filePath);
}

function toInHouseTemplate (title, description, challengeSeed, tests, id) {
  const templatedContent = `
/* eslint spaced-comment: 0 */
/* eslint no-redeclare: 0 */
/* eslint no-unused-vars: 0 */

const assert = require('chai').assert;

/// title: ${title}
/// type: project-euler

/// categories:
/// math

/// difficulty: ?

/// benchmark:
//replaceWithActualFunctionHere;

/// description:
/// ${description}

/// challengeSeed:
${challengeSeed}

/// solutions:

/// tail:
const replaceThis = 3;

/// tests:
${tests}
/// id: ${id}
`;
  return templatedContent;
}
<|endoftext|>


<commit_before>"use strict";
var mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

var userSchema = new Schema({
    name: {
        Type: String,
        required: true
    },
    email: {
        Type: String,
        required: true
    },
    username: {
        Type: String,
        required: true
    },
    DateAccessed: {
        Type: Date,
        default: Date.now
    },
    CoverPhotoURL: {
        Type: String,
        required: true
    }
    facebook: Object
});

mongoose.model('User', userSchema);
<commit_msg>Updated User to store public key<commit_after>"use strict";
var mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

var userSchema = new Schema({
    name: {
        Type: String,
        required: true
    },
    email: {
        Type: String,
        required: true
    },
    username: {
        Type: String,
        required: true
    },
    dateAccessed: {
        Type: Date,
        default: Date.now
    },
    coverPhotoURL: {
        Type: String,
        required: true
    }
    facebook: Object,
    publicKey: {
        Type: String,
        required: true
    }
});

mongoose.model('User', userSchema);
<|endoftext|>


<commit_before>const {Action, api} = require('actionhero');
const projects = require('../lib/dal/projects');
const dal = require('../lib/dal');

module.exports.ListProjectsAction = class ListProjectsAction extends Action {
    constructor() {
        super();
        this.name = 'listProjects';
        this.description = 'Retrieve a list of projects user has access to';
    }

    async run({response, params}) {

    }
};

module.exports.CreateProjectAction = class CreateProjectAction extends Action {
    constructor() {
        super();
        this.name = 'createProject';
        this.description = 'Create a new project owned by user';
    }

    async run({response, params}) {

    }
};

module.exports.GetProjectAction = class GetProjectAction extends Action {
    constructor() {
        super();
        this.name = 'getProject';
        this.description = 'Get details for a given project';
        this.inputs = {
            project_id: {
                required: true,
                //validator: async () => false,
            },
        }
    }

    async run({response, params, user}) {
        api.log('user', 'info', user);
        const project = await dal.tryCatch(async () => await projects.getProject(params.project_id));
        if (!project) {
            throw new Error(`No such project_id ${params.project_id}`);
        }
        response.data = project;
    }
};

module.exports.UpdateProjectAction = class UpdateProjectAction extends Action {
    constructor() {
        super();
        this.name = 'updateProject';
        this.description = 'Update given project';
    }

    async run({response, params}) {

    }
};

module.exports.DeleteProjectAction = class DeleteProjectAction extends Action {
    constructor() {
        super();
        this.name = 'deleteProject';
        this.description = 'Delete the given project';
    }

    async run({response, params}) {

    }
};

module.exports.CloneProjectAction = class CloneProjectAction extends Action {
    constructor() {
        super();
        this.name = 'cloneProject';
        this.description = 'Create project by cloning from the given project';
    }

    async run({response, params}) {

    }
};

module.exports.GetProjectViewsAction = class GetProjectViewsAction extends Action {
    constructor() {
        super();
        this.name = 'getProjectViews';
        this.description = 'Returns a list of the projects view';
    }

    async run({response, params}) {

    }
};

module.exports.UpdateProjectViewsAction = class UpdateProjectViewsAction extends Action {
    constructor() {
        super();
        this.name = 'updateProjectViews';
        this.description = 'Update views by adding/deleting views associated with project';
    }

    async run({response, params}) {

    }
};
<commit_msg>Comment out some testing code<commit_after>const {Action, api} = require('actionhero');
const projects = require('../lib/dal/projects');
const dal = require('../lib/dal');

module.exports.ListProjectsAction = class ListProjectsAction extends Action {
    constructor() {
        super();
        this.name = 'listProjects';
        this.description = 'Retrieve a list of projects user has access to';
    }

    async run({response, params}) {

    }
};

module.exports.CreateProjectAction = class CreateProjectAction extends Action {
    constructor() {
        super();
        this.name = 'createProject';
        this.description = 'Create a new project owned by user';
    }

    async run({response, params}) {

    }
};

module.exports.GetProjectAction = class GetProjectAction extends Action {
    constructor() {
        super();
        this.name = 'getProject';
        this.description = 'Get details for a given project';
        this.inputs = {
            project_id: {
                required: true,
                //validator: async () => false,
            },
        }
    }

    async run({response, params, user}) {
        //api.log('user', 'info', user);
        //console.log('api', api.redis);
        const project = await dal.tryCatch(async () => await projects.getProject(params.project_id));
        if (!project) {
            throw new Error(`No such project_id ${params.project_id}`);
        }
        response.data = project;
    }
};

module.exports.UpdateProjectAction = class UpdateProjectAction extends Action {
    constructor() {
        super();
        this.name = 'updateProject';
        this.description = 'Update given project';
    }

    async run({response, params}) {

    }
};

module.exports.DeleteProjectAction = class DeleteProjectAction extends Action {
    constructor() {
        super();
        this.name = 'deleteProject';
        this.description = 'Delete the given project';
    }

    async run({response, params}) {

    }
};

module.exports.CloneProjectAction = class CloneProjectAction extends Action {
    constructor() {
        super();
        this.name = 'cloneProject';
        this.description = 'Create project by cloning from the given project';
    }

    async run({response, params}) {

    }
};

module.exports.GetProjectViewsAction = class GetProjectViewsAction extends Action {
    constructor() {
        super();
        this.name = 'getProjectViews';
        this.description = 'Returns a list of the projects view';
    }

    async run({response, params}) {

    }
};

module.exports.UpdateProjectViewsAction = class UpdateProjectViewsAction extends Action {
    constructor() {
        super();
        this.name = 'updateProjectViews';
        this.description = 'Update views by adding/deleting views associated with project';
    }

    async run({response, params}) {

    }
};
<|endoftext|>


<commit_before>/*!
 * Validator v0.11.9 for Bootstrap 3, by @1000hz
 * Copyright 2017 Cina Saffary
 * Licensed under http://opensource.org/licenses/MIT
 *
 * https://github.com/1000hz/bootstrap-validator
 */

+function(a){"use strict";function b(b){return b.is('[type="checkbox"]')?b.prop("checked"):b.is('[type="radio"]')?!!a('[name="'+b.attr("name")+'"]:checked').length:b.is("select[multiple]")?(b.val()||[]).length:b.val()}function c(b){return this.each(function(){var c=a(this),e=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b),f=c.data("bs.validator");(f||"destroy"!=b)&&(f||c.data("bs.validator",f=new d(this,e)),"string"==typeof b&&f[b]())})}var d=function(c,e){this.options=e,this.validators=a.extend({},d.VALIDATORS,e.custom),this.$element=a(c),this.$btn=a('button[type="submit"], input[type="submit"]').filter('[form="'+this.$element.attr("id")+'"]').add(this.$element.find('input[type="submit"], button[type="submit"]')),this.update(),this.$element.on("input.bs.validator change.bs.validator focusout.bs.validator",a.proxy(this.onInput,this)),this.$element.on("submit.bs.validator",a.proxy(this.onSubmit,this)),this.$element.on("reset.bs.validator",a.proxy(this.reset,this)),this.$element.find("[data-match]").each(function(){var c=a(this),d=c.attr("data-match");a(d).on("input.bs.validator",function(){b(c)&&c.trigger("input.bs.validator")})}),this.$inputs.filter(function(){return b(a(this))&&!a(this).closest(".has-error").length}).trigger("focusout"),this.$element.attr("novalidate",!0)};d.VERSION="0.11.9",d.INPUT_SELECTOR=':input:not([type="hidden"], [type="submit"], [type="reset"], button)',d.FOCUS_OFFSET=20,d.DEFAULTS={delay:500,html:!1,disable:!0,focus:!0,custom:{},errors:{match:"Does not match",minlength:"Not long enough"},feedback:{success:"glyphicon-ok",error:"glyphicon-remove"}},d.VALIDATORS={"native":function(a){var b=a[0];return b.checkValidity?!b.checkValidity()&&!b.validity.valid&&(b.validationMessage||"error!"):void 0},match:function(b){var c=b.attr("data-match");return b.val()!==a(c).val()&&d.DEFAULTS.errors.match},minlength:function(a){var b=a.attr("data-minlength");return a.val().length<b&&d.DEFAULTS.errors.minlength}},d.prototype.update=function(){var b=this;return this.$inputs=this.$element.find(d.INPUT_SELECTOR).add(this.$element.find('[data-validate="true"]')).not(this.$element.find('[data-validate="false"]').each(function(){b.clearErrors(a(this))})),this.toggleSubmit(),this},d.prototype.onInput=function(b){var c=this,d=a(b.target),e="focusout"!==b.type;this.$inputs.is(d)&&this.validateInput(d,e).done(function(){c.toggleSubmit()})},d.prototype.validateInput=function(c,d){var e=(b(c),c.data("bs.validator.errors"));c.is('[type="radio"]')&&(c=this.$element.find('input[name="'+c.attr("name")+'"]'));var f=a.Event("validate.bs.validator",{relatedTarget:c[0]});if(this.$element.trigger(f),!f.isDefaultPrevented()){var g=this;return this.runValidators(c).done(function(b){c.data("bs.validator.errors",b),b.length?d?g.defer(c,g.showErrors):g.showErrors(c):g.clearErrors(c),e&&b.toString()===e.toString()||(f=b.length?a.Event("invalid.bs.validator",{relatedTarget:c[0],detail:b}):a.Event("valid.bs.validator",{relatedTarget:c[0],detail:e}),g.$element.trigger(f)),g.toggleSubmit(),g.$element.trigger(a.Event("validated.bs.validator",{relatedTarget:c[0]}))})}},d.prototype.runValidators=function(c){function d(a){return c.attr("data-"+a+"-error")}function e(){var a=c[0].validity;return a.typeMismatch?c.attr("data-type-error"):a.patternMismatch?c.attr("data-pattern-error"):a.stepMismatch?c.attr("data-step-error"):a.rangeOverflow?c.attr("data-max-error"):a.rangeUnderflow?c.attr("data-min-error"):a.valueMissing?c.attr("data-required-error"):null}function f(){return c.attr("data-error")}function g(a){return d(a)||e()||f()}var h=[],i=a.Deferred();return c.data("bs.validator.deferred")&&c.data("bs.validator.deferred").reject(),c.data("bs.validator.deferred",i),a.each(this.validators,a.proxy(function(a,d){var e=null;!b(c)&&!c.attr("required")||void 0===c.attr("data-"+a)&&"native"!=a||!(e=d.call(this,c))||(e=g(a)||e,!~h.indexOf(e)&&h.push(e))},this)),!h.length&&b(c)&&c.attr("data-remote")?this.defer(c,function(){var d={};d[c.attr("name")]=b(c),a.get(c.attr("data-remote"),d).fail(function(a,b,c){h.push(g("remote")||c)}).always(function(){i.resolve(h)})}):i.resolve(h),i.promise()},d.prototype.validate=function(){var b=this;return a.when(this.$inputs.map(function(){return b.validateInput(a(this),!1)})).then(function(){b.toggleSubmit(),b.focusError()}),this},d.prototype.focusError=function(){if(this.options.focus){var b=this.$element.find(".has-error :input:first");0!==b.length&&(a("html, body").animate({scrollTop:b.offset().top-d.FOCUS_OFFSET},250),b.focus())}},d.prototype.showErrors=function(b){var c=this.options.html?"html":"text",d=b.data("bs.validator.errors"),e=b.closest(".form-group"),f=e.find(".help-block.with-errors"),g=e.find(".form-control-feedback");d.length&&(d=a("<ul/>").addClass("list-unstyled").append(a.map(d,function(b){return a("<li/>")[c](b)})),void 0===f.data("bs.validator.originalContent")&&f.data("bs.validator.originalContent",f.html()),f.empty().append(d),e.addClass("has-error has-danger"),e.hasClass("has-feedback")&&g.removeClass(this.options.feedback.success)&&g.addClass(this.options.feedback.error)&&e.removeClass("has-success"))},d.prototype.clearErrors=function(a){var c=a.closest(".form-group"),d=c.find(".help-block.with-errors"),e=c.find(".form-control-feedback");d.html(d.data("bs.validator.originalContent")),c.removeClass("has-error has-danger has-success"),c.hasClass("has-feedback")&&e.removeClass(this.options.feedback.error)&&e.removeClass(this.options.feedback.success)&&b(a)&&e.addClass(this.options.feedback.success)&&c.addClass("has-success")},d.prototype.hasErrors=function(){function b(){return!!(a(this).data("bs.validator.errors")||[]).length}return!!this.$inputs.filter(b).length},d.prototype.isIncomplete=function(){function c(){var c=b(a(this));return!("string"==typeof c?a.trim(c):c)}return!!this.$inputs.filter("[required]").filter(c).length},d.prototype.onSubmit=function(a){this.validate(),(this.isIncomplete()||this.hasErrors())&&a.preventDefault()},d.prototype.toggleSubmit=function(){this.options.disable&&this.$btn.toggleClass("disabled",this.isIncomplete()||this.hasErrors())},d.prototype.defer=function(b,c){return c=a.proxy(c,this,b),this.options.delay?(window.clearTimeout(b.data("bs.validator.timeout")),void b.data("bs.validator.timeout",window.setTimeout(c,this.options.delay))):c()},d.prototype.reset=function(){return this.$element.find(".form-control-feedback").removeClass(this.options.feedback.error).removeClass(this.options.feedback.success),this.$inputs.removeData(["bs.validator.errors","bs.validator.deferred"]).each(function(){var b=a(this),c=b.data("bs.validator.timeout");window.clearTimeout(c)&&b.removeData("bs.validator.timeout")}),this.$element.find(".help-block.with-errors").each(function(){var b=a(this),c=b.data("bs.validator.originalContent");b.removeData("bs.validator.originalContent").html(c)}),this.$btn.removeClass("disabled"),this.$element.find(".has-error, .has-danger, .has-success").removeClass("has-error has-danger has-success"),this},d.prototype.destroy=function(){return this.reset(),this.$element.removeAttr("novalidate").removeData("bs.validator").off(".bs.validator"),this.$inputs.off(".bs.validator"),this.options=null,this.validators=null,this.$element=null,this.$btn=null,this.$inputs=null,this};var e=a.fn.validator;a.fn.validator=c,a.fn.validator.Constructor=d,a.fn.validator.noConflict=function(){return a.fn.validator=e,this},a(window).on("load",function(){a('form[data-toggle="validator"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery);<commit_msg>Delete validator.min.js<commit_after><|endoftext|>


<commit_before>/* eslint-disable no-unused-vars */
/* eslint-disable max-len */

import React from 'react';
import { Card, CardActions } from 'material-ui/Card';
import { MdComment, MdForum, MdGroup } from 'react-icons/lib/md';
import { yellow200, fullBlack, deepOrange100, indigo100, amber100, teal100 } from 'material-ui/styles/colors';
import RaisedButton from 'material-ui/RaisedButton';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import marked from 'marked';
import { StyleSheet, css } from 'aphrodite';
import moment from 'moment';
import { Link } from 'react-router';
import Upvote from './Upvote';

const styles = StyleSheet.create({
  flexContainer: {
    display: 'flex',
    paddingLeft: 30,
    paddingTop: 10,
    paddingRight: 30,
    '@media (max-width: 1000px)': {
      flexDirection: 'column',
    },
  },
  articleTitleBar: {
    paddingLeft: 30,
    paddingTop: 10,
    paddingRight: 10,
  },
  leftFlex: {
    flex: 2,
  },
  rightFlex: {
    flex: 1,
    display: 'flex',
    flexDirection: 'column',
    maxHeight: '300px',
  },
  imageStyle: {
    alignSelf: 'center',
  },
  usersAndAnnotations: {
    display: 'flex',
    justifyContent: 'space-around',
    '@media (max-width: 1000px)': {
      display: 'none',
    },
  },
  cardButtons: {
    paddingTop: 40,
    display: 'flex',
    alignSelf: 'center',
    justifyContent: 'center',
    '@media (max-width: 900px)': {
      flexDirection: 'column',
      margin: 10,
      alignItems: 'center',
    },
  },
  bottomButton: {
    '@media (max-width: 900px)': {
      marginTop: 15,
    },
  },
  annotationTextStyle: {
    position: 'relative',
    fontSize: 21,
  },
  articleTitleTextStyle: {
    fontWeight: 700,
    fontSize: 33,
  },
  articleTextStyle: {
    fontSize: 24,
    fontStyle: 'italic',
    fontWeight: 100,
    backgroundColor: yellow200,
  },
  domainTextStyle: {
    fontSize: 17,
    textDecoration: 'underline',
  },
  publishedTime: {
    fontSize: 15,
  },
});

class ArticleCard extends React.Component {
  render() {
    const {
      title,
      domain,
      numUsers,
      numReplies,
      username,
      points,
      currentVotes,
      image,
      slug,
    } = this.props;
    const timeSince = moment(this.props.timeSince).fromNow();
    let cardStyle;
    if (this.props.narrowCard) {
      cardStyle = {
        maxWidth: '40%',
        margin: 30,
      };
    } else {
      cardStyle = {
        margin: 30,
      };
    }
    const publishedAgo = moment(this.props.datePublished).fromNow();
    const readingTime = Math.floor(this.props.wordCount / 275);
    return (
      <MuiThemeProvider>
        <Card style={cardStyle}>

            {title ||  No title gotten from article. Submit a bug report?}
            {/*
{readingTime} min read • Article originally published {publishedAgo}
 */}
            {/*
              maybe on hover of title, show article excerpt
              {this.props.excerpt}
             */}

                {domain}

              {this.props.numAnnotations > 0 &&

                "{this.props.subtitle}"

}
              {this.props.numAnnotations > 0 &&

                {username}
                {timeSince}

}

            {this.props.narrowCard ? '' :

                {/* <MdGroup /> {numUsers} users */}
                <MdComment /> {this.props.numAnnotations} {this.props.numAnnotations > 1 ? 'annotations' : 'annotation'}

 }

          <CardActions className={css(styles.cardButtons)}>
            <Link
              to={{
                pathname: `/discussion/${slug}`,
                state: {
                  articleId: slug,
                  articleURI: domain,
                },
              }}
            >
              <RaisedButton backgroundColor={'#FFCC80'} className={css(styles.bottomButton)} label="See Discussion" />
            </Link>
            <RaisedButton backgroundColor={'#b6d3d9'} className={css(styles.bottomButton)} label="View Original Article" />
          </CardActions>
        </Card>
      </MuiThemeProvider>
    );
  }
}

ArticleCard.propTypes = {
  title: React.PropTypes.string.isRequired,
  domain: React.PropTypes.string.isRequired,
  subtitle: React.PropTypes.string.isRequired,
  annotationContent: React.PropTypes.string.isRequired,
  numUsers: React.PropTypes.number,
  numAnnotations: React.PropTypes.number.isRequired,
  numReplies: React.PropTypes.number,
  username: React.PropTypes.string.isRequired,
  timeSince: React.PropTypes.string.isRequired,
  image: React.PropTypes.string.isRequired,
  currentVotes: React.PropTypes.number.isRequired,
  slug: React.PropTypes.string.isRequired,
};

ArticleCard.defaultProps = {
  numUsers: 0,
  numReplies: 0,
};
export default ArticleCard;
<commit_msg>show excerpt if no annotations<commit_after>/* eslint-disable no-unused-vars */
/* eslint-disable max-len */

import React from 'react';
import { Card, CardActions } from 'material-ui/Card';
import { MdComment, MdForum, MdGroup } from 'react-icons/lib/md';
import { yellow200, fullBlack, deepOrange100, indigo100, amber100, teal100 } from 'material-ui/styles/colors';
import RaisedButton from 'material-ui/RaisedButton';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import marked from 'marked';
import { StyleSheet, css } from 'aphrodite';
import moment from 'moment';
import { Link } from 'react-router';
import Upvote from './Upvote';

const styles = StyleSheet.create({
  flexContainer: {
    display: 'flex',
    paddingLeft: 30,
    paddingTop: 10,
    paddingRight: 30,
    '@media (max-width: 1000px)': {
      flexDirection: 'column',
    },
  },
  articleTitleBar: {
    paddingLeft: 30,
    paddingTop: 10,
    paddingRight: 10,
  },
  leftFlex: {
    flex: 2,
  },
  rightFlex: {
    flex: 1,
    display: 'flex',
    flexDirection: 'column',
    maxHeight: '300px',
  },
  imageStyle: {
    alignSelf: 'center',
  },
  usersAndAnnotations: {
    display: 'flex',
    justifyContent: 'space-around',
    '@media (max-width: 1000px)': {
      display: 'none',
    },
  },
  cardButtons: {
    paddingTop: 40,
    display: 'flex',
    alignSelf: 'center',
    justifyContent: 'center',
    '@media (max-width: 900px)': {
      flexDirection: 'column',
      margin: 10,
      alignItems: 'center',
    },
  },
  bottomButton: {
    '@media (max-width: 900px)': {
      marginTop: 15,
    },
  },
  annotationTextStyle: {
    position: 'relative',
    fontSize: 21,
  },
  articleTitleTextStyle: {
    fontWeight: 700,
    fontSize: 33,
  },
  articleTextStyle: {
    fontSize: 24,
    fontStyle: 'italic',
    fontWeight: 100,
    backgroundColor: yellow200,
  },
  domainTextStyle: {
    fontSize: 17,
    textDecoration: 'underline',
  },
  publishedTime: {
    fontSize: 15,
  },
});

class ArticleCard extends React.Component {
  render() {
    const {
      title,
      domain,
      numUsers,
      numReplies,
      username,
      points,
      currentVotes,
      image,
      excerpt,
      slug,
    } = this.props;
    const timeSince = moment(this.props.timeSince).fromNow();
    let cardStyle;
    if (this.props.narrowCard) {
      cardStyle = {
        maxWidth: '40%',
        margin: 30,
      };
    } else {
      cardStyle = {
        margin: 30,
      };
    }
    const publishedAgo = moment(this.props.datePublished).fromNow();
    const readingTime = Math.floor(this.props.wordCount / 275);
    return (
      <MuiThemeProvider>
        <Card style={cardStyle}>

            {title ||  No title gotten from article. Submit a bug report?}
            {/*
{readingTime} min read • Article originally published {publishedAgo}
 */}
            {/*
              maybe on hover of title, show article excerpt
              {this.props.excerpt}
             */}

                {domain}

              {this.props.numAnnotations > 0 &&

                "{this.props.subtitle}"

}
              {this.props.numAnnotations > 0 &&

                {username}
                {timeSince}

}
              {this.props.numAnnotations === 0 && excerpt &&

                  {excerpt}

              }

            {this.props.narrowCard ? '' :

                {/* <MdGroup /> {numUsers} users */}
                <MdComment /> {this.props.numAnnotations} {this.props.numAnnotations > 1 ? 'annotations' : 'annotation'}

 }

          <CardActions className={css(styles.cardButtons)}>
            <Link
              to={{
                pathname: `/discussion/${slug}`,
                state: {
                  articleId: slug,
                  articleURI: domain,
                },
              }}
            >
              <RaisedButton backgroundColor={'#FFCC80'} className={css(styles.bottomButton)} label="See Discussion" />
            </Link>
            <RaisedButton backgroundColor={'#b6d3d9'} className={css(styles.bottomButton)} label="View Original Article" />
          </CardActions>
        </Card>
      </MuiThemeProvider>
    );
  }
}

ArticleCard.propTypes = {
  title: React.PropTypes.string.isRequired,
  domain: React.PropTypes.string.isRequired,
  subtitle: React.PropTypes.string.isRequired,
  annotationContent: React.PropTypes.string.isRequired,
  numUsers: React.PropTypes.number,
  numAnnotations: React.PropTypes.number.isRequired,
  numReplies: React.PropTypes.number,
  username: React.PropTypes.string.isRequired,
  timeSince: React.PropTypes.string.isRequired,
  image: React.PropTypes.string.isRequired,
  currentVotes: React.PropTypes.number.isRequired,
  slug: React.PropTypes.string.isRequired,
};

ArticleCard.defaultProps = {
  numUsers: 0,
  numReplies: 0,
};
export default ArticleCard;
<|endoftext|>


<commit_before>#!/usr/bin/env node

var path = require("path"),
	fs = require("fs"),
	spawn = require('child_process').spawn;

var SCRIPT_BAT = "/tools/deploy.bat",
	SCRIPT_SH = "/tools/deploy.sh",
	DEPLOY_DIR = "/deploy",
	WWW = "www";

function findBootplate() {
	var deploy = SCRIPT_SH;
	if(process.platform.indexOf("win")===0) {
		deploy = SCRIPT_BAT;
	}
	try {
		if(fs.existsSync(WWW + deploy)) {
			return WWW;
		} else { //search for non-www bootplate
			var root = fs.readdirSync("./");
			for(var i=0; i<root.length; i++) {
				if(fs.existsSync(root[i] + deploy)) {
					return root[i];
				}
			}
		}
	} catch(e) {
		console.log("Error encountered while searching for bootplate\n" + e.message);
	}
}

function runBuildScript(bpDir, success) {
	var rootDir = process.cwd();
	process.chdir(bpDir);
	var child;
	if(process.platform.indexOf("win")===0) {
		child = spawn(process.env.COMSPEC || "cmd.exe", ["/c", path.join(bpDir, SCRIPT_BAT)],
				{stdio: "inherit"});
	} else {
		child = spawn(process.env.SHELL || "sh", [bpDir + SCRIPT_SH], {stdio: "inherit"});
	}
	child.on("close", function(code) {
		process.chdir(rootDir);
		if(code===0) {
			success();
		} else {
			updateWWW(bpDir);
		}
	});
}

function findDeployed(bpDir) {
	try {
		if(fs.existsSync(bpDir + DEPLOY_DIR + "/index.html")) {
			return bpDir + DEPLOY_DIR;
		} else if(fs.existsSync(bpDir + DEPLOY_DIR)) {
			//search for old-format and non-standard deploy output locations
			var bpContents = fs.readdirSync(bpDir + DEPLOY_DIR);
			// sort by age and some previous bootplates that allow multiple deploy directories
			bpContents.sort(function(a, b) {
				return fs.statSync(bpDir + DEPLOY_DIR + b).mtime.getTime() -
						fs.statSync(bpDir + DEPLOY_DIR + a).mtime.getTime();
			});
			for(var i=0; i<bpContents.length; i++) {
				if(fs.existsSync(bpDir + DEPLOY_DIR + "/" + bpContents[i] + "/index.html")) {
					return bpDir + DEPLOY_DIR + "/" + bpContents[i];
				}
			}
		}
	} catch(e) {
		console.log("Error encountered while searching for deployed bootplate\n" + e.message);
	}
}

function updateWWW(dir) {
	if(fs.lstatSync(WWW).isSymbolicLink()) {
		fs.unlinkSync(WWW);
	} else if(fs.readdirSync(WWW).length===0) {
		fs.rmdirSync(WWW);
	}
	if(!fs.existsSync(WWW)) {
		fs.symlinkSync(path.resolve(dir), WWW, 'dir');
	}
}

var bootplate = findBootplate();
if(bootplate) {
	var realBootplate = fs.realpathSync(bootplate);
	var cmdline = process.env.CORDOVA_CMDLINE || "";
	if(cmdline.indexOf("-no-deploy")>=0 || cmdline.indexOf("-enyo-debug")>=0
			|| (process.env.CORDOVA_PLATFORMS || process.env.CORDOVA_PLATFORMS==="webos")) {
		updateWWW(realBootplate);
	} else {
		runBuildScript(realBootplate, function() {
			var deploy = findDeployed(realBootplate);
			if(deploy) {
				updateWWW(deploy);
			} else {
				updateWWW(realBootplate);
			}
		});
	}
}
<commit_msg>Syntax fix<commit_after>#!/usr/bin/env node

var path = require("path"),
	fs = require("fs"),
	spawn = require('child_process').spawn;

var SCRIPT_BAT = "/tools/deploy.bat",
	SCRIPT_SH = "/tools/deploy.sh",
	DEPLOY_DIR = "/deploy",
	WWW = "www";

function findBootplate() {
	var deploy = SCRIPT_SH;
	if(process.platform.indexOf("win")===0) {
		deploy = SCRIPT_BAT;
	}
	try {
		if(fs.existsSync(WWW + deploy)) {
			return WWW;
		} else { //search for non-www bootplate
			var root = fs.readdirSync("./");
			for(var i=0; i<root.length; i++) {
				if(fs.existsSync(root[i] + deploy)) {
					return root[i];
				}
			}
		}
	} catch(e) {
		console.log("Error encountered while searching for bootplate\n" + e.message);
	}
}

function runBuildScript(bpDir, success) {
	var rootDir = process.cwd();
	process.chdir(bpDir);
	var child;
	if(process.platform.indexOf("win")===0) {
		child = spawn(process.env.COMSPEC || "cmd.exe", ["/c", path.join(bpDir, SCRIPT_BAT)],
				{stdio: "inherit"});
	} else {
		child = spawn(process.env.SHELL || "sh", [bpDir + SCRIPT_SH], {stdio: "inherit"});
	}
	child.on("close", function(code) {
		process.chdir(rootDir);
		if(code===0) {
			success();
		} else {
			updateWWW(bpDir);
		}
	});
}

function findDeployed(bpDir) {
	try {
		if(fs.existsSync(bpDir + DEPLOY_DIR + "/index.html")) {
			return bpDir + DEPLOY_DIR;
		} else if(fs.existsSync(bpDir + DEPLOY_DIR)) {
			//search for old-format and non-standard deploy output locations
			var bpContents = fs.readdirSync(bpDir + DEPLOY_DIR);
			// sort by age and some previous bootplates that allow multiple deploy directories
			bpContents.sort(function(a, b) {
				return fs.statSync(bpDir + DEPLOY_DIR + b).mtime.getTime() -
						fs.statSync(bpDir + DEPLOY_DIR + a).mtime.getTime();
			});
			for(var i=0; i<bpContents.length; i++) {
				if(fs.existsSync(bpDir + DEPLOY_DIR + "/" + bpContents[i] + "/index.html")) {
					return bpDir + DEPLOY_DIR + "/" + bpContents[i];
				}
			}
		}
	} catch(e) {
		console.log("Error encountered while searching for deployed bootplate\n" + e.message);
	}
}

function updateWWW(dir) {
	if(fs.lstatSync(WWW).isSymbolicLink()) {
		fs.unlinkSync(WWW);
	} else if(fs.readdirSync(WWW).length===0) {
		fs.rmdirSync(WWW);
	}
	if(!fs.existsSync(WWW)) {
		fs.symlinkSync(path.resolve(dir), WWW, 'dir');
	}
}

var bootplate = findBootplate();
if(bootplate) {
	var realBootplate = fs.realpathSync(bootplate);
	var cmdline = process.env.CORDOVA_CMDLINE || "";
	if(cmdline.indexOf("-no-deploy")>=0 || cmdline.indexOf("-enyo-debug")>=0
			|| (process.env.CORDOVA_PLATFORMS && process.env.CORDOVA_PLATFORMS==="webos")) {
		updateWWW(realBootplate);
	} else {
		runBuildScript(realBootplate, function() {
			var deploy = findDeployed(realBootplate);
			if(deploy) {
				updateWWW(deploy);
			} else {
				updateWWW(realBootplate);
			}
		});
	}
}
<|endoftext|>