<commit_before>var load = function() {
    if (document.querySelectorAll === undefined) {
        return;
    }

    var elements = document.querySelectorAll(".line.opener");
    for (var index = 0; index < elements.length; index++) {
        var element = elements[index];
        element.addEventListener("click", function() {
            var targetId = this.getAttribute("data-id");
            var target = document.querySelector(".lines-extra[data-id=\"" + targetId + "\"]");
            var style = target.style;
            if (style.display === "block") {
                target.style.display = "none";
            } else {
                target.style.display = "block";
            }
        });
    }

    highlightAll(".lines-extra");
};

var highlightAll = function(selector) {
    var targets = document.querySelectorAll(selector);
    for (var index = 0; index < targets.length; index++) {
        var target = targets[index];
        var elements = target.querySelectorAll(".line > .text");
        highlightLibraries(elements);
    }
};

var highlightLibraries = function(elements) {
    highlightPrism(elements);
    highlightHighlightJS(elements);
};

var highlightPrism = function(elements, language) {
    if (window.Prism === undefined) {
        return;
    }

    if (elements.length === 0) {
        return;
    }

    language = language || Prism.languages.python;

    var codeBuffer = [];

    for (var index = 0; index < elements.length; index++) {
        var element = elements[index];
        var textS = element.textContent;
        codeBuffer.push(textS);
    }

    var codeS = codeBuffer.join("\n");
    var tokens = Prism.tokenize(codeS, language);

    var partsBuffer = [];

    for (var index = 0; index < tokens.length; index++) {
        var token = tokens[index];
        var isString = typeof token === "string";
        var tokenContent = isString ? token : token.content;
        var tokenType = isString ? null : token.type;
        var tokenAlias = isString ? null : token.alias;
        var tokenClass = isString ? "" : "token ";
        tokenClass += token.type ? token.type + " " : "";
        tokenClass += token.alias ? token.alias + " " : "";

        var tokenParts = tokenContent.split("\n");
        for (var _index = 0; _index < tokenParts.length; _index++) {
            var tokenPart = tokenParts[_index];
            var isFirst = _index === 0;
            var partS = (isFirst ? "" : "\n") + "" + tokenPart + "";
            partsBuffer.push(partS);
        }
    }

    var partsS = partsBuffer.join("");
    var parts = partsS.split("\n");

    for (var index = 0; index < parts.length; index++) {
        var part = parts[index];
        var element = elements[index];
        element.innerHTML = part;
    }
};

var highlightHighlightJS = function(elements, language) {
    if (window.hljs === undefined) {
        return;
    }

    if (elements.length === 0) {
        return;
    }

    language = language || "python";

    var codeBuffer = [];

    for (var index = 0; index < elements.length; index++) {
        var element = elements[index];
        var textS = element.textContent;
        codeBuffer.push(textS);
    }

    var codeS = codeBuffer.join("\n");
    var highlighted = hljs.highlight(language, codeS);

    var partsS = highlighted.value;
    var parts = partsS.split("\n")

    for (var index = 0; index < parts.length; index++) {
        var part = parts[index];
        var element = elements[index];
        var invalid = part.indexOf("<span") != -1 && part.indexOf("") == -1;
        part += invalid ? "" : "";
        element.innerHTML = part;
    }
};

window.addEventListener !== undefined && window.addEventListener("load", function() {
    load();
});
<commit_msg>better support for escaping<commit_after>var load = function() {
    if (document.querySelectorAll === undefined) {
        return;
    }

    var elements = document.querySelectorAll(".line.opener");
    for (var index = 0; index < elements.length; index++) {
        var element = elements[index];
        element.addEventListener("click", function() {
            var targetId = this.getAttribute("data-id");
            var target = document.querySelector(".lines-extra[data-id=\"" + targetId + "\"]");
            var style = target.style;
            if (style.display === "block") {
                target.style.display = "none";
            } else {
                target.style.display = "block";
            }
        });
    }

    highlightAll(".lines-extra");
};

var highlightAll = function(selector) {
    var targets = document.querySelectorAll(selector);
    for (var index = 0; index < targets.length; index++) {
        var target = targets[index];
        var elements = target.querySelectorAll(".line > .text");
        highlightLibraries(elements);
    }
};

var highlightLibraries = function(elements) {
    highlightPrism(elements);
    highlightHighlightJS(elements);
};

var highlightPrism = function(elements, language) {
    if (window.Prism === undefined) {
        return;
    }

    if (elements.length === 0) {
        return;
    }

    language = language || Prism.languages.python;

    var codeBuffer = [];

    for (var index = 0; index < elements.length; index++) {
        var element = elements[index];
        var textS = element.textContent;
        codeBuffer.push(textS);
    }

    var codeS = codeBuffer.join("\n");
    var tokens = Prism.tokenize(codeS, language);

    var partsBuffer = [];

    for (var index = 0; index < tokens.length; index++) {
        var token = tokens[index];
        var isString = typeof token === "string";
        var tokenContent = isString ? token : token.content;
        var tokenType = isString ? null : token.type;
        var tokenAlias = isString ? null : token.alias;
        var tokenClass = isString ? "" : "token ";
        tokenClass += token.type ? token.type + " " : "";
        tokenClass += token.alias ? token.alias + " " : "";

        var tokenParts = tokenContent.split("\n");
        for (var _index = 0; _index < tokenParts.length; _index++) {
            var tokenPart = tokenParts[_index];
            var isFirst = _index === 0;
            tokenPart = escapeHtml(tokenPart);
            var partS = (isFirst ? "" : "\n") + "" + tokenPart + "";
            partsBuffer.push(partS);
        }
    }

    var partsS = partsBuffer.join("");
    var parts = partsS.split("\n");

    for (var index = 0; index < parts.length; index++) {
        var part = parts[index];
        var element = elements[index];
        element.innerHTML = part;
    }
};

var highlightHighlightJS = function(elements, language) {
    if (window.hljs === undefined) {
        return;
    }

    if (elements.length === 0) {
        return;
    }

    language = language || "python";

    var codeBuffer = [];

    for (var index = 0; index < elements.length; index++) {
        var element = elements[index];
        var textS = element.textContent;
        codeBuffer.push(textS);
    }

    var codeS = codeBuffer.join("\n");
    var highlighted = hljs.highlight(language, codeS);

    var partsS = highlighted.value;
    var parts = partsS.split("\n")

    for (var index = 0; index < parts.length; index++) {
        var part = parts[index];
        var element = elements[index];
        var invalid = part.indexOf("<span") != -1 && part.indexOf("") == -1;
        part += invalid ? "" : "";
        element.innerHTML = part;
    }
};

var escapeHtml = function(unsafe) {
    return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g,
        ">").replace(/"/g, """).replace(/'/g, "'");
};

window.addEventListener !== undefined && window.addEventListener("load", function() {
    load();
});
<|endoftext|>


<commit_before>#!/usr/bin/env node
/**
 * This module is started with bin/run.sh. It sets up a Express HTTP and a Socket.IO Server.
 * Static file Requests are answered directly from this module, Socket.IO messages are passed
 * to MessageHandler and minfied requests are passed to minified.
 */

/*
 * 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var log4js = require('log4js')
  , async = require('async')
  , stats = require('./stats')
  , NodeVersion = require('./utils/NodeVersion')
  ;

log4js.replaceConsole();

stats.gauge('memoryUsage', function() {
  return process.memoryUsage().rss
})

var settings
  , db
  , plugins
  , hooks;
var npm = require("npm/lib/npm.js");

async.waterfall([
  function(callback)
  {
    NodeVersion.enforceMinNodeVersion('6.9.0', callback);
  },

  function(callback)
  {
    NodeVersion.checkDeprecationStatus('8.9.0', '1.8.0', callback);
  },

  // load npm
  function(callback) {
    npm.load({}, function(er) {
      callback(er)
    })
  },

  // load everything
  function(callback) {
    settings = require('./utils/Settings');
    db = require('./db/DB');
    plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins");
    hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");
    hooks.plugins = plugins;
    callback();
  },

  //initalize the database
  function (callback)
  {
    db.init(callback);
  },

  function(callback) {
    plugins.update(callback)
  },

  function (callback) {
    console.info("Installed plugins: " + plugins.formatPluginsWithVersion());
    console.debug("Installed parts:\n" + plugins.formatParts());
    console.debug("Installed hooks:\n" + plugins.formatHooks());

    // Call loadSettings hook
    hooks.aCallAll("loadSettings", { settings: settings });
    callback();
  },

  //initalize the http server
  function (callback)
  {
    hooks.callAll("createServer", {});
    callback(null);
  }
]);
<commit_msg>server.js: group together the loading of the stats system<commit_after>#!/usr/bin/env node
/**
 * This module is started with bin/run.sh. It sets up a Express HTTP and a Socket.IO Server.
 * Static file Requests are answered directly from this module, Socket.IO messages are passed
 * to MessageHandler and minfied requests are passed to minified.
 */

/*
 * 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var log4js = require('log4js')
  , async = require('async')
  , NodeVersion = require('./utils/NodeVersion')
  ;

log4js.replaceConsole();

/*
 * start up stats counting system
 */
var stats = require('./stats');
stats.gauge('memoryUsage', function() {
  return process.memoryUsage().rss
})

var settings
  , db
  , plugins
  , hooks;
var npm = require("npm/lib/npm.js");

async.waterfall([
  function(callback)
  {
    NodeVersion.enforceMinNodeVersion('6.9.0', callback);
  },

  function(callback)
  {
    NodeVersion.checkDeprecationStatus('8.9.0', '1.8.0', callback);
  },

  // load npm
  function(callback) {
    npm.load({}, function(er) {
      callback(er)
    })
  },

  // load everything
  function(callback) {
    settings = require('./utils/Settings');
    db = require('./db/DB');
    plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins");
    hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");
    hooks.plugins = plugins;
    callback();
  },

  //initalize the database
  function (callback)
  {
    db.init(callback);
  },

  function(callback) {
    plugins.update(callback)
  },

  function (callback) {
    console.info("Installed plugins: " + plugins.formatPluginsWithVersion());
    console.debug("Installed parts:\n" + plugins.formatParts());
    console.debug("Installed hooks:\n" + plugins.formatHooks());

    // Call loadSettings hook
    hooks.aCallAll("loadSettings", { settings: settings });
    callback();
  },

  //initalize the http server
  function (callback)
  {
    hooks.callAll("createServer", {});
    callback(null);
  }
]);
<|endoftext|>


<commit_before>"use strict";

/*
https://twitter.com/kangax/status/435478540210147328

"Would be nice if @github showed average response time on issues for each repo; OSS projects w. quick responses always look more promising"

"I'm talking about any activity (comment, status change, etc.) not resolutions. I have multi-year issues as well, but w. response."

- "What's response time? Time till being closed? Time till first comment?"
- "First comment. "Till closed" would cause more harm than good."

Open questions :
- Response from whom? could be anyone or repo collaborators. Choosing anyone
- How much time should be considered for issues without answers? Could be time-since-open or issues could be treated separatedly. Choosing separatedly
- Should all issues be considered in the average count (response time might vary over time)? Choosing first 100 open issues (for impl simplicity. can only get 100 issues per request and have to choose between open and closed).

Among open issues :
- % without response from anyone
- For those with a response, average response time from someone else than issue creator

*/

const { all } = require('sdk/core/promise');
var Widget = require("sdk/widget").Widget;
var Panel = require("sdk/panel").Panel;
var PageMod = require("sdk/page-mod").PageMod;

var data = require("sdk/self").data;

var storage = require("sdk/simple-storage").storage;

var Github = require('githubAPI');

exports.main = function(){

    var pageMod;
    var gh;

    var tokenPanel = Panel({
        width: 600,
        height: 100,

        position: {
            top: 0,
            right: 0
        },

        contentURL: data.url('token.html'),
        contentScriptFile: [
            data.url('token.js')
        ],
        contentScriptWhen: "ready"
    });

    tokenPanel.on('error', function(err){
        console.log('tokenPanel error', err);
    });

    var githubWidget = Widget({
        id: "kang-4354",
        label: "enter personal token",
        content: "PR Time",
        width: 60,

        panel: tokenPanel
    });

    tokenPanel.port.on('test-token', token => {
        // send a dummy request (hits the quota a bit, but it's for a good cause)
        // https://api.github.com/repositories
        console.log('test-token', token);

        gh = new Github(token);
        gh.testToken()
            .then( token => tokenPanel.port.emit('test-token-result', token) ,
                   err => {
                        tokenPanel.port.emit('test-token-result', err);
                        gh = undefined;
                   }
            );
    });

    tokenPanel.port.on('persist-token', token => {
        storage.token = token;
        tokenPanel.hide();
        getReadyForGithubRepoPages(token);
    });

    function getReadyForGithubRepoPages(token){
        if(pageMod)
            pageMod.destroy();

        pageMod = PageMod({
            include: /^https:\/\/github\.com\/([^\/]+\/[^\/]+)\/?$/,

            contentScriptFile: data.url("github-repo-response-stats.js"),
            contentScriptWhen: "start", // so that the attach event triggers as soon as possible

            contentStyleFile: data.url("github-repo-response-stats.css")
        });

        pageMod.on('attach', worker => {
            var matches = worker.url.match(/^https:\/\/github\.com\/([^\/]+\/[^\/]+)\/?$/i);
            var repo;

            if(!Array.isArray(matches) || matches.length < 2)
                return;

            repo = matches[1];
            console.log('repo', repo);

            var commentlessIssuesCount = 0;
            var issuesNb;

            console.time('average repo response time');

            gh.getLatestRepoIssues(repo)
                .then( issues => {
                    issues = issues.slice(0, 50); // refraining from making too many HTTP requests

                    console.log('issues', issues);
                    issuesNb = issues.length;

                    return all(issues.map( issue => {
                            var issueCreatorId;
                            var issueCreationDate;

                            // TODO omit issues by repo collaborators

                            if(issue.comments === 0)
                                commentlessIssuesCount++;
                            else{
                                issueCreatorId = issue.user.id;
                                issueCreationDate = Date.parse(issue.created_at);

                                return gh.getIssueComments(repo, issue.number).then( comments => {
                                    // find first comment not from issue creator
                                    var firstComment = comments.find( c => c.user.id !== issueCreatorId );
                                    var firstCommentDate;

                                    if(firstComment){
                                        firstCommentDate = Date.parse(firstComment.created_at);

                                        // difference in days
                                        return firstCommentDate - issueCreationDate;
                                    }
                                    else
                                        commentlessIssuesCount++;
                                });
                            }
                        })
                    );

                })
                .then(
                    delays => {
                        console.timeEnd('average repo response time');
                        delays = delays.filter(x => x !== undefined);
                        console.log('delays', delays);

                        worker.port.emit('repo-response-stats', {
                            issuesConsidered: issuesNb,
                            commentlessIssues: commentlessIssuesCount,
                            responseTimes: delays
                        })
                    },
                    err => console.log('err', err)
                );

            // fetch relevant repo info
            // Likely comments too http://developer.github.com/v3/issues/comments/

        })
    }

    if(storage.token){
        getReadyForGithubRepoPages(storage.token);
    }
    else{ // no token stored. Ask one to user
        tokenPanel.show();
    }
};<commit_msg>Change widget title until getting a proper icon<commit_after>"use strict";

/*
https://twitter.com/kangax/status/435478540210147328

"Would be nice if @github showed average response time on issues for each repo; OSS projects w. quick responses always look more promising"

"I'm talking about any activity (comment, status change, etc.) not resolutions. I have multi-year issues as well, but w. response."

- "What's response time? Time till being closed? Time till first comment?"
- "First comment. "Till closed" would cause more harm than good."

Open questions :
- Response from whom? could be anyone or repo collaborators. Choosing anyone
- How much time should be considered for issues without answers? Could be time-since-open or issues could be treated separatedly. Choosing separatedly
- Should all issues be considered in the average count (response time might vary over time)? Choosing first 100 open issues (for impl simplicity. can only get 100 issues per request and have to choose between open and closed).

Among open issues :
- % without response from anyone
- For those with a response, average response time from someone else than issue creator

*/

const { all } = require('sdk/core/promise');
var Widget = require("sdk/widget").Widget;
var Panel = require("sdk/panel").Panel;
var PageMod = require("sdk/page-mod").PageMod;

var data = require("sdk/self").data;

var storage = require("sdk/simple-storage").storage;

var Github = require('githubAPI');

exports.main = function(){

    var pageMod;
    var gh;

    var tokenPanel = Panel({
        width: 600,
        height: 100,

        position: {
            top: 0,
            right: 0
        },

        contentURL: data.url('token.html'),
        contentScriptFile: [
            data.url('token.js')
        ],
        contentScriptWhen: "ready"
    });

    tokenPanel.on('error', function(err){
        console.log('tokenPanel error', err);
    });

    var githubWidget = Widget({
        id: "kang-4354",
        label: "Enter personal access token",
        content: "RepoReact",
        width: 80,

        panel: tokenPanel
    });

    tokenPanel.port.on('test-token', token => {
        // send a dummy request (hits the quota a bit, but it's for a good cause)
        // https://api.github.com/repositories
        console.log('test-token', token);

        gh = new Github(token);
        gh.testToken()
            .then( token => tokenPanel.port.emit('test-token-result', token) ,
                   err => {
                        tokenPanel.port.emit('test-token-result', err);
                        gh = undefined;
                   }
            );
    });

    tokenPanel.port.on('persist-token', token => {
        storage.token = token;
        tokenPanel.hide();
        getReadyForGithubRepoPages(token);
    });

    function getReadyForGithubRepoPages(token){
        if(pageMod)
            pageMod.destroy();

        pageMod = PageMod({
            include: /^https:\/\/github\.com\/([^\/]+\/[^\/]+)\/?$/,

            contentScriptFile: data.url("github-repo-response-stats.js"),
            contentScriptWhen: "start", // so that the attach event triggers as soon as possible

            contentStyleFile: data.url("github-repo-response-stats.css")
        });

        pageMod.on('attach', worker => {
            var matches = worker.url.match(/^https:\/\/github\.com\/([^\/]+\/[^\/]+)\/?$/i);
            var repo;

            if(!Array.isArray(matches) || matches.length < 2)
                return;

            repo = matches[1];
            console.log('repo', repo);

            var commentlessIssuesCount = 0;
            var issuesNb;

            console.time('average repo response time');

            gh.getLatestRepoIssues(repo)
                .then( issues => {
                    issues = issues.slice(0, 50); // refraining from making too many HTTP requests

                    console.log('issues', issues);
                    issuesNb = issues.length;

                    return all(issues.map( issue => {
                            var issueCreatorId;
                            var issueCreationDate;

                            // TODO omit issues by repo collaborators

                            if(issue.comments === 0)
                                commentlessIssuesCount++;
                            else{
                                issueCreatorId = issue.user.id;
                                issueCreationDate = Date.parse(issue.created_at);

                                return gh.getIssueComments(repo, issue.number).then( comments => {
                                    // find first comment not from issue creator
                                    var firstComment = comments.find( c => c.user.id !== issueCreatorId );
                                    var firstCommentDate;

                                    if(firstComment){
                                        firstCommentDate = Date.parse(firstComment.created_at);

                                        // difference in days
                                        return firstCommentDate - issueCreationDate;
                                    }
                                    else
                                        commentlessIssuesCount++;
                                });
                            }
                        })
                    );

                })
                .then(
                    delays => {
                        console.timeEnd('average repo response time');
                        delays = delays.filter(x => x !== undefined);
                        console.log('delays', delays);

                        worker.port.emit('repo-response-stats', {
                            issuesConsidered: issuesNb,
                            commentlessIssues: commentlessIssuesCount,
                            responseTimes: delays
                        })
                    },
                    err => console.log('err', err)
                );

            // fetch relevant repo info
            // Likely comments too http://developer.github.com/v3/issues/comments/

        })
    }

    if(storage.token){
        getReadyForGithubRepoPages(storage.token);
    }
    else{ // no token stored. Ask one to user
        tokenPanel.show();
    }
};<|endoftext|>


<commit_before>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * 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.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * 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.
 */

import {AbstractClassError, AbstractMethodError} from '../errors/AbstractError';
import OAuthToken from './OAuthToken';
import StateContainer from './StateContainer';
import {isNode} from '../utils/node';
import StorageManager from '../storage/StorageManager';

/**
 * OAuth base class
 * @abstract
 */
export default class OAuth {
  /**
   * @param {String} clientId - OAuth client id
   * @param {Array<String>} scopes - A list of required scopes
   * @returns {void}
   */
  constructor(clientId, scopes = ['*']) {
    if (this.constructor === OAuth) {
      throw new AbstractClassError();
    }

    this.clientId = String(clientId);
    this.scopes = scopes;
    this.token = OAuthToken.recover();
    this.host = process.env.HOST;
    this.path = '/';
  }

  /**
   * If the current instance has a valid token
   * @returns {Boolean} - if a valid token is availble
   */
  get authenticated() {
    return this.token !== null && !this.token.expired;
  }

  /**
   * Authenticate
   * @returns {Promise} - Promise resolves with OAuthToken and rejects with OAuthError
   * @abstract
   */
  authenticate() {
    throw new AbstractMethodError();
  }

  /**
   * Forget the current session
   * Empty the session token store and forget the api token
   * @returns {void}
   */
  forget() {
    StateContainer.clean();
    StorageManager.secure.remove(OAuthToken.storageName);

    this.token = null;
  }
}
<commit_msg>Remove unused import<commit_after>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * 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.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * 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.
 */

import {AbstractClassError, AbstractMethodError} from '../errors/AbstractError';
import OAuthToken from './OAuthToken';
import StateContainer from './StateContainer';
import StorageManager from '../storage/StorageManager';

/**
 * OAuth base class
 * @abstract
 */
export default class OAuth {
  /**
   * @param {String} clientId - OAuth client id
   * @param {Array<String>} scopes - A list of required scopes
   * @returns {void}
   */
  constructor(clientId, scopes = ['*']) {
    if (this.constructor === OAuth) {
      throw new AbstractClassError();
    }

    this.clientId = String(clientId);
    this.scopes = scopes;
    this.token = OAuthToken.recover();
    this.host = process.env.HOST;
    this.path = '/';
  }

  /**
   * If the current instance has a valid token
   * @returns {Boolean} - if a valid token is availble
   */
  get authenticated() {
    return this.token !== null && !this.token.expired;
  }

  /**
   * Authenticate
   * @returns {Promise} - Promise resolves with OAuthToken and rejects with OAuthError
   * @abstract
   */
  authenticate() {
    throw new AbstractMethodError();
  }

  /**
   * Forget the current session
   * Empty the session token store and forget the api token
   * @returns {void}
   */
  forget() {
    StateContainer.clean();
    StorageManager.secure.remove(OAuthToken.storageName);

    this.token = null;
  }
}
<|endoftext|>


<commit_before>/**********************************************************************************

 Copyright (C) 2012 - 2016 satoshi okami

 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in
 the Software without restriction, including without limitation the rights to
 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 of the Software, and to permit persons to whom the Software is furnished to do
 so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.

 **********************************************************************************/

/**
 * @class
 * @classdesc
 * @augments THREE.Mesh
 * @param {Object} options
 */
 exports.THREE.Bird = function (options)
 {
  function v(x, y, z)
  {
    geometry.vertices.push(new THREE.Vector3(x, y, z));
  }

  function f3(a, b, c)
  {
    geometry.faces.push( new THREE.Face3( a, b, c ) );
  }

  options = options || {};
  options.color = (options.color == undefined) ? 0x646464 : options.color;
  options.position = (options.position == undefined) ? new exports.Vector3( 0, 0, 0 ) : options.position;

  var geometry = new THREE.Geometry();
  var material = new THREE.MeshBasicMaterial({
    color: new THREE.Color( options.color ),
    side: THREE.DoubleSide
  });

  v(   5,   0,   0 );
  v( - 5, - 2,   1 );
  v( - 5,   0,   0 );
  v( - 5, - 2, - 1 );

  v(   0,   2, - 6 );
  v(   0,   2,   6 );
  v(   2,   0,   0 );
  v( - 3,   0,   0 );

  f3( 0, 2, 1 ); // body
  f3( 0, 3, 2 );

  f3( 4, 7, 6 ); // right wing
  f3( 5, 6, 7 ); //　left wing

  THREE.Mesh.call(this, geometry, material);

  this.phase = Math.floor(Math.random() * 62.83);

  this.behavior = new exports.SteeredVehicle(
    options.position.x,
    options.position.y,
    options.position.z
  );

  this.behavior.maxForce = .15;
}

exports.THREE.Bird.prototype = Object.create(THREE.Mesh.prototype);

/**
 * @access public
 * @param {Vector3} target position that you want to approach to
 * @see {@link SteeredVehicle#seek}
 */
exports.THREE.Bird.prototype.seek = function(target)
{
  this.behavior.seek(target);
}

/**
 * @access public
 * @param {Vector3} target position that you want to move away
 * @see {@link SteeredVehicle#flee}
 */
exports.THREE.Bird.prototype.flee = function(target)
{
  this.behavior.flee(target);
}

/**
 * @access public
 * @param {Vector3} target position that you want to arrive
 * @see {@link SteeredVehicle#arrive}
 */
exports.THREE.Bird.prototype.arrive = function(target)
{
  this.behavior.arrive(target);
}

/**
 * @access public
 * @param {Array} paths array of Vector3. position list that you want to approach to
 * @see {@link SteeredVehicle#patrol}
 */
exports.THREE.Bird.prototype.patrol = function(paths, loop)
{
  this.behavior.patrol(paths, loop);
}

/**
 * @access public
 * @param {Array} vehicles vehicle array list
 * @see {@link SteeredVehicle#flock}
 */
exports.THREE.Bird.prototype.flock = function(vehicles)
{
  this.behavior.flock(boids);
}

/**
 * @access public
 * @param {Vector3} target
 * @see {@link SteeredVehicle#tooClose}
 */
exports.THREE.Bird.prototype.tooClose = function(target)
{
  this.behavior.tooClose(target);
}

/**
 * @access public
 * @param {int} width
 * @param {int} height
 * @param {int} depth
 */
exports.THREE.Bird.prototype.wrap = function(width, height, depth)
{
  this.behavior.wrap(width, height, depth);
}

/**
 * @access public
 * @param {int} width
 * @param {int} height
 * @param {int} depth
 */
exports.THREE.Bird.prototype.bounce = function(width, height, depth)
{
  this.behavior.bounce(width, height, depth);
}

/**
 * @access public
 */
exports.THREE.Bird.prototype.wander = function()
{
  this.behavior.wander();
}

/**
 * @access public
 * @param {Vector3} target
 * @see {@link SteeredVehicle#inSight}
 */
exports.THREE.Bird.prototype.inSight = function(target)
{
  return this.behavior.inSight(target);
}

/**
 * @access public
 * @param {Object} options
 */
exports.THREE.Bird.prototype.update = function(options)
{
  options = options || {};

  this.behavior.update();
  // this.behavior.wrap(500, 500, 400);
  this.flap();

  this.position.x = this.behavior.position.x;
  this.position.y = this.behavior.position.y;
  this.position.z = this.behavior.position.z;

  if (options.color != undefined) {
    this.material.color = new THREE.Color(options.color);
  }
}

/**
 * @access private
 */
exports.THREE.Bird.prototype.flap = function()
{
  this.rotation.y = Math.atan2(-this.behavior.velocity.z, this.behavior.velocity.x);
  this.rotation.z = Math.asin(this.behavior.velocity.y / this.behavior.velocity.length());

  this.phase = (this.phase + (Math.max(0, this.rotation.z) + 0.1)) % 62.83;
  this.geometry.verticesNeedUpdate = true;

  this.geometry.vertices[5].y =
  this.geometry.vertices[4].y = Math.sin(this.phase) * 5;
}
<commit_msg>added functions. pursue, evade, randomWalk and inSight.<commit_after>/**********************************************************************************

 Copyright (C) 2012 - 2016 satoshi okami

 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in
 the Software without restriction, including without limitation the rights to
 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 of the Software, and to permit persons to whom the Software is furnished to do
 so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.

 **********************************************************************************/

/**
 * @class
 * @classdesc
 * @augments THREE.Mesh
 * @param {Object} options
 */
exports.THREE.Bird = function (options)
{
  function v(x, y, z)
  {
    geometry.vertices.push(new THREE.Vector3(x, y, z));
  }

  function f3(a, b, c)
  {
    geometry.faces.push( new THREE.Face3( a, b, c ) );
  }

  options = options || {};
  options.color = (options.color == undefined) ? 0x646464 : options.color;
  options.position = (options.position == undefined) ? new exports.Vector3( 0, 0, 0 ) : options.position;

  var geometry = new THREE.Geometry();
  var material = new THREE.MeshBasicMaterial({
    color: new THREE.Color( options.color ),
    side: THREE.DoubleSide
  });

  v(   5,   0,   0 );
  v( - 5, - 2,   1 );
  v( - 5,   0,   0 );
  v( - 5, - 2, - 1 );

  v(   0,   2, - 6 );
  v(   0,   2,   6 );
  v(   2,   0,   0 );
  v( - 3,   0,   0 );

  f3( 0, 2, 1 ); // body
  f3( 0, 3, 2 );

  f3( 4, 7, 6 ); // right wing
  f3( 5, 6, 7 ); //　left wing

  THREE.Mesh.call(this, geometry, material);

  this.phase = Math.floor(Math.random() * 62.83);

  this.behavior = new exports.SteeredVehicle(
    options.position.x,
    options.position.y,
    options.position.z
  );

  this.behavior.maxForce = .15;
}

exports.THREE.Bird.prototype = Object.create(THREE.Mesh.prototype);

/**
 * @access public
 * @param {Vector3} target position that you want to approach to
 * @see {@link SteeredVehicle#seek}
 */
exports.THREE.Bird.prototype.seek = function(target)
{
  this.behavior.seek(target);
}

/**
 * @access public
 * @param {Vector3} target position that you want to move away
 * @see {@link SteeredVehicle#flee}
 */
exports.THREE.Bird.prototype.flee = function(target)
{
  this.behavior.flee(target);
}

/**
 * @access public
 * @param {Vector3} target position that you want to arrive
 * @see {@link SteeredVehicle#arrive}
 */
exports.THREE.Bird.prototype.arrive = function(target)
{
  this.behavior.arrive(target);
}

/**
 * @access public
 * @param {Vehicle} target
 * @see {@link SteeredVehicle#pursue}
 */
exports.THREE.Bird.prototype.pursue = function(target)
{
  this.behavior.pursue(target);
}

/**
 * @access public
 * @param {Vehicle} target
 * @see {@link SteeredVehicle#evade}
 */
exports.THREE.Bird.prototype.evade = function(target)
{
  this.behavior.evade(target);
}

/**
 * @access public
 * @param {Array} paths array of Vector3. position list that you want to approach to
 * @see {@link SteeredVehicle#patrol}
 */
exports.THREE.Bird.prototype.patrol = function(paths, loop)
{
  this.behavior.patrol(paths, loop);
}

/**
 * @access public
 * @param {Array} vehicles vehicle array list
 * @see {@link SteeredVehicle#flock}
 */
exports.THREE.Bird.prototype.flock = function(vehicles)
{
  this.behavior.flock(boids);
}

/**
 * @access public
 * @see {@link SteeredVehicle#randomWalk}
 */
exports.THREE.Bird.prototype.randomWalk = function()
{
  this.behavior.randomWalk();
}

/**
 * @access public
 * @param {Vector3} target
 * @see {@link SteeredVehicle#inSight}
 */
exports.THREE.Bird.prototype.inSight = function(target)
{
  this.behavior.inSight(target);
}

/**
 * @access public
 * @param {Vector3} target
 * @see {@link SteeredVehicle#tooClose}
 */
exports.THREE.Bird.prototype.tooClose = function(target)
{
  this.behavior.tooClose(target);
}

/**
 * @access public
 * @param {int} width
 * @param {int} height
 * @param {int} depth
 */
exports.THREE.Bird.prototype.wrap = function(width, height, depth)
{
  this.behavior.wrap(width, height, depth);
}

/**
 * @access public
 * @param {int} width
 * @param {int} height
 * @param {int} depth
 */
exports.THREE.Bird.prototype.bounce = function(width, height, depth)
{
  this.behavior.bounce(width, height, depth);
}

/**
 * @access public
 */
exports.THREE.Bird.prototype.wander = function()
{
  this.behavior.wander();
}

/**
 * @access public
 * @param {Vector3} target
 * @see {@link SteeredVehicle#inSight}
 */
exports.THREE.Bird.prototype.inSight = function(target)
{
  return this.behavior.inSight(target);
}

/**
 * @access public
 * @param {Object} options
 */
exports.THREE.Bird.prototype.update = function(options)
{
  options = options || {};

  this.behavior.update();
  // this.behavior.wrap(500, 500, 400);
  this.flap();

  this.position.x = this.behavior.position.x;
  this.position.y = this.behavior.position.y;
  this.position.z = this.behavior.position.z;

  if (options.color != undefined) {
    this.material.color = new THREE.Color(options.color);
  }
}

/**
 * @access private
 */
exports.THREE.Bird.prototype.flap = function()
{
  this.rotation.y = Math.atan2(-this.behavior.velocity.z, this.behavior.velocity.x);
  this.rotation.z = Math.asin(this.behavior.velocity.y / this.behavior.velocity.length());

  this.phase = (this.phase + (Math.max(0, this.rotation.z) + 0.1)) % 62.83;
  this.geometry.verticesNeedUpdate = true;

  this.geometry.vertices[5].y =
  this.geometry.vertices[4].y = Math.sin(this.phase) * 5;
}
<|endoftext|>


<commit_before>'use strict';

const gulp = require('gulp');
const path = require('path');

function watch(conf) {

  const root = conf.themeConfig.root;

  /**
   * STYLE WATCHING.
   */
  gulp.watch(
    path.join(root, conf.themeConfig.sass.src, '**', '*.scss'),
    gulp.series('build:styles')
  );

  /**
   * SCRIPT WATCHING.
   */
  gulp.watch(
    path.join(root, conf.themeConfig.js.src, '**', '*.js'),
    gulp.series(
      'lint:scripts',
      'build:scripts'
    )
  );

  /**
   * IMAGE WATCHING.
   */
  gulp.watch(
    path.join(root, conf.themeConfig.images.src, '**', '*'),
    gulp.series('build:images')
  );

  /**
   * FONT WATCHING.
   */
  gulp.watch(
    path.join(root, conf.themeConfig.fonts.src, '**', '*'),
    gulp.series('build:fonts')
  );

}

watch.displayName = 'watch';

module.exports = watch;<commit_msg>Added holograph to SASS watch task.<commit_after>'use strict';

const gulp = require('gulp');
const path = require('path');

function watch(conf) {

  const root = conf.themeConfig.root;

  /**
   * STYLE WATCHING.
   */
  gulp.watch(
    path.join(root, conf.themeConfig.sass.src, '**', '*.scss'),
    gulp.series(
      'build:styles',
      'build:holograph'
    )
  );

  /**
   * SCRIPT WATCHING.
   */
  gulp.watch(
    path.join(root, conf.themeConfig.js.src, '**', '*.js'),
    gulp.series(
      'lint:scripts',
      'build:scripts'
    )
  );

  /**
   * IMAGE WATCHING.
   */
  gulp.watch(
    path.join(root, conf.themeConfig.images.src, '**', '*'),
    gulp.series('build:images')
  );

  /**
   * FONT WATCHING.
   */
  gulp.watch(
    path.join(root, conf.themeConfig.fonts.src, '**', '*'),
    gulp.series('build:fonts')
  );

}

watch.displayName = 'watch';

module.exports = watch;<|endoftext|>


<commit_before>import React from 'react'
import { graphql, Link } from 'gatsby'
import Img from 'gatsby-image'
import { Fade } from 'react-reveal'
import Spin from 'react-reveal/Spin'

import Layout from '../components/structure/layout'
import PostsList from '../components/PostsList'
import TinyLetterSignup from '../components/TinyLetterSignUp'
import Announcement from '../components/Announcement'

import svgReact from '../../static/react.svg'
import svgJS from '../../static/javascript.svg'

class BlogIndex extends React.Component {
  state = {
    loadVideo: true,
  }

  handleVideoClick = e => {
    e.target.pause()
    e.target.currentTime = 0
    e.target.load()
    e.target.play()
  }

  componentDidMount() {
    // If user is on a dodgy connection, don't load the video at all
    if ('connection' in navigator) {
      if (navigator.connection.downlink < 5) {
        this.setState({ loadVideo: false })
      }
    }
  }

  render() {
    const posts = this.props.data.allMarkdownRemark.edges
    const { loadVideo } = this.state

    return (
      <Layout location={this.props.location}>

          {/* TODO: */}
          {/*
Most popular this month:
 */}
          <Announcement
            shortContent={
              <>

                  🔥 Announcement 🔥

                  <Spin>

                  </Spin>

                    I'm starting a new YouTube channel called{' '}

                      FPVTIPS

                    , to do much of the same I do here - try to help people new
                    to FPV. Come check it out 👀 and subscribe 📫.

              </>
            }
          >

              I've been really humbled to help so many people over the years and
              to answer the questions I myself had. I'll continue doing that,
              and believing in the wonderful hobby of FPV racing drones, and
              will also from now on do that on YouTube in addition to this blog.

              Here's how you (yes, YOU) can help -{' '}

                👉{' '}

                  come over

                , 📺 watch the first couple of videos, 📫 subscribe :), and
                leave me some 💬 feedback in the comments section

              . I would REALLY appreciate this, and thank you in advance 💙.

              I'm sure I have a lot to learn about video production, but hey,
              you gotta start somewhere, right? And I'm definitely excited about
              learning and sharing knowledge! Happy flying!

            <time dateTime="2019-06-23">Georgi, 23 June 2019</time>
          </Announcement>

Categories:

            <Link to="/news/">
              <button className="category all">All posts</button>
            </Link>
            <Link to="/news/?category=fpv">
              <button className="category fpv">FPV Drones</button>
            </Link>
            <Link to="/news/?category=projects">
              <button className="category projects">Projects</button>
            </Link>
            <Link to="/news/?category=learning">
              <button className="category learning">Learnings</button>
            </Link>
            <Link to="/news/?category=smarthome">
              <button className="category smarthome">Smarthome</button>
            </Link>
            <Link to="/news/?category=stories">
              <button className="category stories">Stories</button>
            </Link>

Last 10 posts:

          <PostsList
            showChevron="yes"
            showImage="yes"
            showCategories="no"
            showSearch="no"
            posts={posts}
          />
          <Link style={{ marginRight: '0.5rem' }} to="/news/">
            <button className="category fpv">More blog posts</button>
          </Link>

            For more FPV related goodness, make sure you checkout{' '}

              Fpvtips.com
            {' '}
            which I launched a while back!

              Hi, I'm Georgi and I build things on the web with{` `}

              {` `}
              JavaScript and{` `}

              {` `}
              React

            I work as a {` `}
            <Link to="/learning/how-i-got-into-software-development/">
              web developer
            </Link>
            {` `}
            at{` `}
            F-Secure.
            You can find me on{' '}
            twitter,{' '}
            github,{' '}
            twitch and{' '}
            linkedin
            .

            In this blog you will find posts about{' '}

                🚁
              {' '}
              FPV racing drones
            {' '}
            (building, repairing and flying),{' '}

                💻
              {' '}
              web development

            ,{' '}

                🏠
              {' '}
              smart home automation

            ,{' '}

                👨🎓
              {' '}
              life-long learning, goals and stories
            {' '}
            as well as who knows what else in the future.

            I write code, solve problems and sometimes stream
            on twitch. I love flying FPV drones, 3D printing,
            contributing to open source, and working on fpvtips.com.

            Most of my projects are open source and{' '}

              available on GitHub

            . You can also{' '}
            follow me on
            twitter.

            This blog was a joy to build using{' '}

              Gatsby

                💜

            {' '}
            (a React based static site generator) and is hosted on{' '}

              Netlify

                🔥

            .

          {loadVideo && (

              <Fade>
                <video
                  autoPlay
                  muted
                  playsInline
                  onClick={this.handleVideoClick}
                >
                  <source
                    src="https://www.georgi-yanev.com/static/landing-a-quad-1-d1a878f7ef756c703ebeeecdb529e63e.webm"
                    type="video/webm"
                  />
                  <source
                    src="https://www.georgi-yanev.com/static/landing-a-quad-1-a959ba1dfed1d6abe2e8052b61bfb0b4.mp4"
                    type="video/mp4"
                  />
                  Tap to play video
                </video>
              </Fade>

          )}

            Additionally, the Eternal Archives section of the blog hosts a bunch
            of unedited content (fan fiction, music and programming) from the
            early 2000s when I was only 15 - 17.

          <TinyLetterSignup />

      </Layout>
    )
  }
}

export default BlogIndex

export const HomePageQuery = graphql`
  query HomePageQuery {
    allMarkdownRemark(
      sort: { order: DESC, fields: [frontmatter___date] }
      limit: 10
      filter: {
        frontmatter: { draft: { ne: true }, category: { ne: "stories" } }
        fileAbsolutePath: { regex: "/content/posts/" }
      }
    ) {
      edges {
        node {
          excerpt(pruneLength: 350)
          id
          timeToRead
          frontmatter {
            date(formatString: "MMM DD, YYYY")
            path
            title
            author
            category
            tags
            ogImage {
              publicURL
              childImageSharp {
                fluid(maxWidth: 672) {
                  ...GatsbyImageSharpFluid_withWebp
                }
              }
            }
          }
        }
      }
    }

    georgi: allFile(
      filter: { relativePath: { regex: "/^home/georgi-face/" } }
    ) {
      edges {
        node {
          name
          childImageSharp {
            fluid(maxWidth: 100, quality: 75) {
              ...GatsbyImageSharpFluid_withWebp_tracedSVG
            }
          }
        }
      }
    }

    homeBottom: allFile(
      filter: { relativePath: { regex: "/^home/home-bottom/" } }
    ) {
      edges {
        node {
          name
          childImageSharp {
            fluid(maxWidth: 928, quality: 75) {
              ...GatsbyImageSharpFluid_withWebp
            }
          }
        }
      }
    }

    fpvtips: allFile(filter: { relativePath: { regex: "/^home/fpvtips/" } }) {
      edges {
        node {
          name
          childImageSharp {
            fluid(maxWidth: 124, quality: 81) {
              ...GatsbyImageSharpFluid_withWebp
            }
          }
        }
      }
    }

    youtubeFpvtips: file(relativePath: { regex: "/^home/home-fpvtips/" }) {
      childImageSharp {
        fluid(maxWidth: 362, quality: 100) {
          ...GatsbyImageSharpFluid
        }
      }
    }

    youtubeGeorgiFpv: file(relativePath: { regex: "/^home/home-georgifpv/" }) {
      childImageSharp {
        fluid(maxWidth: 362, quality: 100) {
          ...GatsbyImageSharpFluid
        }
      }
    }
  }
`
<commit_msg>Add extra word<commit_after>import React from 'react'
import { graphql, Link } from 'gatsby'
import Img from 'gatsby-image'
import { Fade } from 'react-reveal'
import Spin from 'react-reveal/Spin'

import Layout from '../components/structure/layout'
import PostsList from '../components/PostsList'
import TinyLetterSignup from '../components/TinyLetterSignUp'
import Announcement from '../components/Announcement'

import svgReact from '../../static/react.svg'
import svgJS from '../../static/javascript.svg'

class BlogIndex extends React.Component {
  state = {
    loadVideo: true,
  }

  handleVideoClick = e => {
    e.target.pause()
    e.target.currentTime = 0
    e.target.load()
    e.target.play()
  }

  componentDidMount() {
    // If user is on a dodgy connection, don't load the video at all
    if ('connection' in navigator) {
      if (navigator.connection.downlink < 5) {
        this.setState({ loadVideo: false })
      }
    }
  }

  render() {
    const posts = this.props.data.allMarkdownRemark.edges
    const { loadVideo } = this.state

    return (
      <Layout location={this.props.location}>

          {/* TODO: */}
          {/*
Most popular this month:
 */}
          <Announcement
            shortContent={
              <>

                  🔥 Announcement 🔥

                  <Spin>

                  </Spin>

                    I'm starting a new YouTube channel called{' '}

                      FPVTIPS

                    , to do much of the same I do here - try to help people new
                    to FPV racing drones. Come check it out 👀 and subscribe 📫.

              </>
            }
          >

              I've been really humbled to help so many people over the years and
              to answer the questions I myself had. I'll continue doing that,
              and believing in the wonderful hobby of FPV racing drones, and
              will also from now on do that on YouTube in addition to this blog.

              Here's how you (yes, YOU) can help -{' '}

                👉{' '}

                  come over

                , 📺 watch the first couple of videos, 📫 subscribe :), and
                leave me some 💬 feedback in the comments section

              . I would REALLY appreciate this, and thank you in advance 💙.

              I'm sure I have a lot to learn about video production, but hey,
              you gotta start somewhere, right? And I'm definitely excited about
              learning and sharing knowledge! Happy flying!

            <time dateTime="2019-06-23">Georgi, 23 June 2019</time>
          </Announcement>

Categories:

            <Link to="/news/">
              <button className="category all">All posts</button>
            </Link>
            <Link to="/news/?category=fpv">
              <button className="category fpv">FPV Drones</button>
            </Link>
            <Link to="/news/?category=projects">
              <button className="category projects">Projects</button>
            </Link>
            <Link to="/news/?category=learning">
              <button className="category learning">Learnings</button>
            </Link>
            <Link to="/news/?category=smarthome">
              <button className="category smarthome">Smarthome</button>
            </Link>
            <Link to="/news/?category=stories">
              <button className="category stories">Stories</button>
            </Link>

Last 10 posts:

          <PostsList
            showChevron="yes"
            showImage="yes"
            showCategories="no"
            showSearch="no"
            posts={posts}
          />
          <Link style={{ marginRight: '0.5rem' }} to="/news/">
            <button className="category fpv">More blog posts</button>
          </Link>

            For more FPV related goodness, make sure you checkout{' '}

              Fpvtips.com
            {' '}
            which I launched a while back!

              Hi, I'm Georgi and I build things on the web with{` `}

              {` `}
              JavaScript and{` `}

              {` `}
              React

            I work as a {` `}
            <Link to="/learning/how-i-got-into-software-development/">
              web developer
            </Link>
            {` `}
            at{` `}
            F-Secure.
            You can find me on{' '}
            twitter,{' '}
            github,{' '}
            twitch and{' '}
            linkedin
            .

            In this blog you will find posts about{' '}

                🚁
              {' '}
              FPV racing drones
            {' '}
            (building, repairing and flying),{' '}

                💻
              {' '}
              web development

            ,{' '}

                🏠
              {' '}
              smart home automation

            ,{' '}

                👨🎓
              {' '}
              life-long learning, goals and stories
            {' '}
            as well as who knows what else in the future.

            I write code, solve problems and sometimes stream
            on twitch. I love flying FPV drones, 3D printing,
            contributing to open source, and working on fpvtips.com.

            Most of my projects are open source and{' '}

              available on GitHub

            . You can also{' '}
            follow me on
            twitter.

            This blog was a joy to build using{' '}

              Gatsby

                💜

            {' '}
            (a React based static site generator) and is hosted on{' '}

              Netlify

                🔥

            .

          {loadVideo && (

              <Fade>
                <video
                  autoPlay
                  muted
                  playsInline
                  onClick={this.handleVideoClick}
                >
                  <source
                    src="https://www.georgi-yanev.com/static/landing-a-quad-1-d1a878f7ef756c703ebeeecdb529e63e.webm"
                    type="video/webm"
                  />
                  <source
                    src="https://www.georgi-yanev.com/static/landing-a-quad-1-a959ba1dfed1d6abe2e8052b61bfb0b4.mp4"
                    type="video/mp4"
                  />
                  Tap to play video
                </video>
              </Fade>

          )}

            Additionally, the Eternal Archives section of the blog hosts a bunch
            of unedited content (fan fiction, music and programming) from the
            early 2000s when I was only 15 - 17.

          <TinyLetterSignup />

      </Layout>
    )
  }
}

export default BlogIndex

export const HomePageQuery = graphql`
  query HomePageQuery {
    allMarkdownRemark(
      sort: { order: DESC, fields: [frontmatter___date] }
      limit: 10
      filter: {
        frontmatter: { draft: { ne: true }, category: { ne: "stories" } }
        fileAbsolutePath: { regex: "/content/posts/" }
      }
    ) {
      edges {
        node {
          excerpt(pruneLength: 350)
          id
          timeToRead
          frontmatter {
            date(formatString: "MMM DD, YYYY")
            path
            title
            author
            category
            tags
            ogImage {
              publicURL
              childImageSharp {
                fluid(maxWidth: 672) {
                  ...GatsbyImageSharpFluid_withWebp
                }
              }
            }
          }
        }
      }
    }

    georgi: allFile(
      filter: { relativePath: { regex: "/^home/georgi-face/" } }
    ) {
      edges {
        node {
          name
          childImageSharp {
            fluid(maxWidth: 100, quality: 75) {
              ...GatsbyImageSharpFluid_withWebp_tracedSVG
            }
          }
        }
      }
    }

    homeBottom: allFile(
      filter: { relativePath: { regex: "/^home/home-bottom/" } }
    ) {
      edges {
        node {
          name
          childImageSharp {
            fluid(maxWidth: 928, quality: 75) {
              ...GatsbyImageSharpFluid_withWebp
            }
          }
        }
      }
    }

    fpvtips: allFile(filter: { relativePath: { regex: "/^home/fpvtips/" } }) {
      edges {
        node {
          name
          childImageSharp {
            fluid(maxWidth: 124, quality: 81) {
              ...GatsbyImageSharpFluid_withWebp
            }
          }
        }
      }
    }

    youtubeFpvtips: file(relativePath: { regex: "/^home/home-fpvtips/" }) {
      childImageSharp {
        fluid(maxWidth: 362, quality: 100) {
          ...GatsbyImageSharpFluid
        }
      }
    }

    youtubeGeorgiFpv: file(relativePath: { regex: "/^home/home-georgifpv/" }) {
      childImageSharp {
        fluid(maxWidth: 362, quality: 100) {
          ...GatsbyImageSharpFluid
        }
      }
    }
  }
`
<|endoftext|>


<commit_before>import { graphql, useStaticQuery } from 'gatsby'
import React from 'react'
import ExecPreview from '../components/execPreview'
import Layout from '../components/layout'
import SEO from '../components/seo'

const IndexPage = () => {
  const execs = useStaticQuery(graphql`
    {
      allExecsYaml {
        nodes {
          name
          role
          photo {
            childImageSharp {
              fluid(maxWidth: 200) {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  `).allExecsYaml.nodes

  return (
    <Layout>
      <SEO title="Home" />

We Are Code Network

Build anything, as long as it's awsome

        Our members are heavily involved in the local and international startup
        scene. Our goal as an organisation is to help create the best developer
        talent in the world, starting here in Brisbane. We’re based at QUT and
        are expanding to other universities and organisations in the area.

Our Team

      <ExecPreview execs={execs} />
    </Layout>
  )
}

export default IndexPage
<commit_msg>add contribute message (#13)<commit_after>import { graphql, useStaticQuery } from 'gatsby'
import React from 'react'
import ExecPreview from '../components/execPreview'
import Layout from '../components/layout'
import SEO from '../components/seo'

const IndexPage = () => {
  const execs = useStaticQuery(graphql`
    {
      allExecsYaml {
        nodes {
          name
          role
          photo {
            childImageSharp {
              fluid(maxWidth: 200) {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  `).allExecsYaml.nodes

  return (
    <Layout>
      <SEO title="Home" />

We Are Code Network

Build anything, as long as it's awsome

        Our members are heavily involved in the local and international startup
        scene. Our goal as an organisation is to help create the best developer
        talent in the world, starting here in Brisbane. We’re based at QUT and
        are expanding to other universities and organisations in the area.

        This site is a work in progress. If you'd like to contribute, you can do
        so here

Our Team

      <ExecPreview execs={execs} />
    </Layout>
  )
}

export default IndexPage
<|endoftext|>


<commit_before>import React from 'react'
import Helmet from 'react-helmet'
import Waypoint from 'react-waypoint'

import Layout from '../components/layout'
import Header from '../components/Header'
import Nav from '../components/Nav'
import Scroll from '../components/Scroll'
import Meetup from '../components/Meetup'
import Talks, { Trainings } from '../components/Talks'
import Sponsors from '../components/Sponsors'

import map from '../assets/images/map.png'
import bowSprFile from '../assets/docs/BestofWeb2019-Sponsoring-FR.pdf'
import { meetups, partners } from '../data'

class Index extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      stickyNav: false,
    }
  }

  _handleWaypointEnter = () => {
    this.setState(() => ({ stickyNav: false }))
  }

  _handleWaypointLeave = () => {
    this.setState(() => ({ stickyNav: true }))
  }

  render() {
    return (
      <Layout>
        <Helmet title="Best of Web 2019" />

        <Waypoint
          onEnter={this._handleWaypointEnter}
          onLeave={this._handleWaypointLeave}
        />

A propos de Best of Web 2019

                  Best of Web, c'est une journée de conférence le 7 juin,
                  préparée par des meetups web parisiens qui se réunissent pour
                  vous proposer un Best Of de leur talks de l'année, ainsi que
                  beaucoup d'inédits.

                  Pour ceux qui ont vraiment soif d'apprendre, nous proposons en
                  plus une journée de formation le 6 juin.

                  Le ticket conférence donne accès à la journée du 7 juin
                  uniquement.

                  <Scroll type="id" element="tickets">

                         Achetez un Ticket
                        conférence

                  </Scroll>

                  {meetups.map(({ name, logo }) => (
                    <Meetup key={name} name={name} logo={logo} />
                  ))}

Tickets Conférence

Regular (7 juin)

Même a ce prix, ça reste cadeau !

                  Acheter

Tickets Formation

              Le jeudi 6 juin, nous vous proposons des formations sur les
              dernières technos.

              Pour venir, il faut vous un inscrire à un atelier (places
              limitées).

Formation

Inscription à un atelier

                  Acheter

Programme de la journée conférence

              A chaque créneau horaire, vous avez le choix entre deux talks.

            <Talks />

Programme de la journée formation

              Les ateliers durent 3h. Pour assister à un atelier, vous devez
              acheter le ticket correspondant.

            <Trainings />

S'y rendre

Sponsors

              <Sponsors />

Vous voulez faire partie de l'aventure Best of Web 2019 ?

                 Téléchargez notre dossier
                sponsoring

Partenaires

Ils nous ont aidés...

              {partners.map(({ name, logo, backgroundColor }) => (
                <Meetup
                  isPartner
                  key={name}
                  name={name}
                  logo={logo}
                  backgroundColor={backgroundColor}
                />
              ))}

      </Layout>
    )
  }
}

export default Index
<commit_msg>Add anchor for formations<commit_after>import React from 'react'
import Helmet from 'react-helmet'
import Waypoint from 'react-waypoint'

import Layout from '../components/layout'
import Header from '../components/Header'
import Nav from '../components/Nav'
import Scroll from '../components/Scroll'
import Meetup from '../components/Meetup'
import Talks, { Trainings } from '../components/Talks'
import Sponsors from '../components/Sponsors'

import map from '../assets/images/map.png'
import bowSprFile from '../assets/docs/BestofWeb2019-Sponsoring-FR.pdf'
import { meetups, partners } from '../data'

class Index extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      stickyNav: false,
    }
  }

  _handleWaypointEnter = () => {
    this.setState(() => ({ stickyNav: false }))
  }

  _handleWaypointLeave = () => {
    this.setState(() => ({ stickyNav: true }))
  }

  render() {
    return (
      <Layout>
        <Helmet title="Best of Web 2019" />

        <Waypoint
          onEnter={this._handleWaypointEnter}
          onLeave={this._handleWaypointLeave}
        />

A propos de Best of Web 2019

                  Best of Web, c'est une journée de conférence le 7 juin,
                  préparée par des meetups web parisiens qui se réunissent pour
                  vous proposer un Best Of de leur talks de l'année, ainsi que
                  beaucoup d'inédits.

                  Pour ceux qui ont vraiment soif d'apprendre, nous proposons en
                  plus une journée de formation le 6 juin.

                  Le ticket conférence donne accès à la journée du 7 juin
                  uniquement.

                  <Scroll type="id" element="tickets">

                         Achetez un Ticket
                        conférence

                  </Scroll>

                  {meetups.map(({ name, logo }) => (
                    <Meetup key={name} name={name} logo={logo} />
                  ))}

Tickets Conférence

Regular (7 juin)

Même a ce prix, ça reste cadeau !

                  Acheter

Tickets Formation

              Le jeudi 6 juin, nous vous proposons des formations sur les
              dernières technos.

              Pour venir, il faut vous un inscrire à un atelier (places
              limitées).

Formation

Inscription à un atelier

                  Acheter

Programme de la journée conférence

              A chaque créneau horaire, vous avez le choix entre deux talks.

            <Talks />

Programme de la journée formation

              Les ateliers durent 3h. Pour assister à un atelier, vous devez
              acheter le ticket correspondant.

            <Trainings />

S'y rendre

Sponsors

              <Sponsors />

Vous voulez faire partie de l'aventure Best of Web 2019 ?

                 Téléchargez notre dossier
                sponsoring

Partenaires

Ils nous ont aidés...

              {partners.map(({ name, logo, backgroundColor }) => (
                <Meetup
                  isPartner
                  key={name}
                  name={name}
                  logo={logo}
                  backgroundColor={backgroundColor}
                />
              ))}

      </Layout>
    )
  }
}

export default Index
<|endoftext|>


<commit_before>import React, { Fragment } from 'react'
import Img from 'gatsby-image'
// import Expando from '../components/expando'

// {
/*

wines

                      <Expando clicker={
Threads
}>

Illustration

                      </Expando>

Explore

Graphic Design

Finish

Identity

Lines

Branding

Southwest

Website Design

Window

Photography

reviews

                What our customers have to say about us.

Alix Kram

                        Lorem ipsum dolor sit amet, consectetur adipisicing
                        elit. Sunt ut voluptatum eius sapiente, totam reiciendis
                        temporibus qui quibusdam, recusandae sit vero unde, sed,
                        incidunt et ea quo dolore laudantium consectetur!

Gregg

                        Lorem ipsum dolor sit amet, consectetur adipisicing
                        elit. Sunt ut voluptatum eius sapiente, totam reiciendis
                        temporibus qui quibusdam, recusandae sit vero unde, sed,
                        incidunt et ea quo dolore laudantium consectetur!

Mindy

Transition to Full Service

                        Lorem ipsum dolor sit amet, consectetur adipisicing
                        elit. Sunt ut voluptatum eius sapiente, totam reiciendis
                        temporibus qui quibusdam, recusandae sit vero unde, sed,
                        incidunt et ea quo dolore laudantium consectetur!

July 2014

Phase Two Expansion

                        Lorem ipsum dolor sit amet, consectetur adipisicing
                        elit. Sunt ut voluptatum eius sapiente, totam reiciendis
                        temporibus qui quibusdam, recusandae sit vero unde, sed,
                        incidunt et ea quo dolore laudantium consectetur!

                      Be Part

Of Our

Story!

 */
// }
// {/*

//

//

//

//
Contact Start Bootstrap

//

//                 Feel free to leave us a comment on the
//
//                   Grayscale template overview page
//
//                 on Start Bootstrap to give some feedback about this theme!
//

//

//

//
//
//                     Twitter
//
//

//

//
//
//                     Github
//
//

//

//
//
//                     Google+
//
//

//

//

//

//

//
 */}

const IndexPage = ({ data }) => {
  return (
    <Fragment>

        <Img
          style={{
            position: `absolute`,
            top: 0,
            left: 0,
            right: 0,
            zIndex: -1,
            height: '100%',
          }}
          sizes={data.mainImage.sizes}
        />

            <Img
              style={{
                position: `absolute`,
                top: 0,
                left: 0,
                right: 0,
                zIndex: -1,
                width: 200,
                height: 200,
                margin: '110px auto',
              }}
              sizes={data.logo.sizes}
            />

vine & fork

wine concierge service

about

                  At vine & fork we bring Sommelier selected wines directly
                  to the hands of the consumer. Our goal is to bring boutique
                  not commercially distributed wines to meet the everyday
                  consumers need whether it’s for personal use or a large event.

        <Img
          style={{
            position: `absolute`,
            top: 0,
            left: 0,
            right: 0,
            zIndex: -1,
            height: '100%',
            // width: this.props.windowWidth,
          }}
          sizes={data.secondaryImage.sizes}
        />

contact

Follow us on instagram or reach out by email

                    Instagram

                    Email

    </Fragment>
  )
}

export default IndexPage

export const pageQuery = graphql`
  query indexPage {
    mainImage: imageSharp(id: { regex: "/rolling/" }) {
      sizes(maxWidth: 1240) {
        ...GatsbyImageSharpSizes_withWebp
      }
    }
    secondaryImage: imageSharp(id: { regex: "/contact/" }) {
      sizes(maxWidth: 1240) {
        ...GatsbyImageSharpSizes_withWebp
      }
    }
    logo: imageSharp(id: { regex: "/logo/" }) {
      sizes(maxWidth: 441) {
        ...GatsbyImageSharpSizes_withWebp
      }
    }
  }
`
<commit_msg>Hopefully fix the weird logo issue<commit_after>import React, { Fragment } from 'react'
import Img from 'gatsby-image'
// import Expando from '../components/expando'

// {
/*

wines

                      <Expando clicker={
Threads
}>

Illustration

                      </Expando>

Explore

Graphic Design

Finish

Identity

Lines

Branding

Southwest

Website Design

Window

Photography

reviews

                What our customers have to say about us.

Alix Kram

                        Lorem ipsum dolor sit amet, consectetur adipisicing
                        elit. Sunt ut voluptatum eius sapiente, totam reiciendis
                        temporibus qui quibusdam, recusandae sit vero unde, sed,
                        incidunt et ea quo dolore laudantium consectetur!

Gregg

                        Lorem ipsum dolor sit amet, consectetur adipisicing
                        elit. Sunt ut voluptatum eius sapiente, totam reiciendis
                        temporibus qui quibusdam, recusandae sit vero unde, sed,
                        incidunt et ea quo dolore laudantium consectetur!

Mindy

Transition to Full Service

                        Lorem ipsum dolor sit amet, consectetur adipisicing
                        elit. Sunt ut voluptatum eius sapiente, totam reiciendis
                        temporibus qui quibusdam, recusandae sit vero unde, sed,
                        incidunt et ea quo dolore laudantium consectetur!

July 2014

Phase Two Expansion

                        Lorem ipsum dolor sit amet, consectetur adipisicing
                        elit. Sunt ut voluptatum eius sapiente, totam reiciendis
                        temporibus qui quibusdam, recusandae sit vero unde, sed,
                        incidunt et ea quo dolore laudantium consectetur!

                      Be Part

Of Our

Story!

 */
// }
// {/*

//

//

//

//
Contact Start Bootstrap

//

//                 Feel free to leave us a comment on the
//
//                   Grayscale template overview page
//
//                 on Start Bootstrap to give some feedback about this theme!
//

//

//

//
//
//                     Twitter
//
//

//

//
//
//                     Github
//
//

//

//
//
//                     Google+
//
//

//

//

//

//

//
 */}

const IndexPage = ({ data }) => {
  return (
    <Fragment>

        <Img
          style={{
            position: `absolute`,
            top: 0,
            left: 0,
            right: 0,
            zIndex: -1,
            height: '100%',
          }}
          sizes={data.mainImage.sizes}
        />

vine & fork

wine concierge service

about

                  At vine & fork we bring Sommelier selected wines directly
                  to the hands of the consumer. Our goal is to bring boutique
                  not commercially distributed wines to meet the everyday
                  consumers need whether it’s for personal use or a large event.

        <Img
          style={{
            position: `absolute`,
            top: 0,
            left: 0,
            right: 0,
            zIndex: -1,
            height: '100%',
            // width: this.props.windowWidth,
          }}
          sizes={data.secondaryImage.sizes}
        />

contact

Follow us on instagram or reach out by email

                    Instagram

                    Email

    </Fragment>
  )
}

export default IndexPage

export const pageQuery = graphql`
  query indexPage {
    mainImage: imageSharp(id: { regex: "/rolling/" }) {
      sizes(maxWidth: 1240) {
        ...GatsbyImageSharpSizes_withWebp
      }
    }
    secondaryImage: imageSharp(id: { regex: "/contact/" }) {
      sizes(maxWidth: 1240) {
        ...GatsbyImageSharpSizes_withWebp
      }
    }
    logo: imageSharp(id: { regex: "/logo/" }) {
      resize(width: 440) {
        src
      }
    }
  }
`
<|endoftext|>