code:
#version 330
uniform sampler2D textureIn;
in vec4 colorPipe;
in vec2 texCoordPipe;
out vec4 colorOut;
void main() {
    //colorOut = vec4(0.0, 0.0, 0.0, texture(textureIn, texCoordPipe).r); // TODO blend alpha
    colorOut = vec4(colorPipe.rgb, texture(textureIn, texCoordPipe).r); // TODO blend alpha
}

repo_name: vaartis/netrunner

license: mit


code:
#version 330
uniform mat4 transform;
layout (location = 0) in vec3 vertexIn;
layout (location = 1) in vec4 colorIn;
layout (location = 2) in vec2 texCoordIn;

out vec4 colorPipe;
out vec2 texCoordPipe;
void main() {
    gl_Position = transform * vec4(vertexIn, 1.0);
    colorPipe = colorIn;
    texCoordPipe = texCoordIn;
}


code:
#version 330
out vec4 colorOut;
in vec2 texCoordPipe;
uniform sampler2D textureIn;
void main() {
    colorOut = texture(textureIn, texCoordPipe);
}


code:
#version 330
uniform mat4 transform;
layout (location = 0) in vec3 vertexIn;
layout (location = 1) in vec2 texCoordIn;
out vec2 texCoordPipe;
void main() {
    gl_Position = transform * vec4(vertexIn, 1.0);
    texCoordPipe = texCoordIn;
}


code:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by netrunner.rc
//
#define IDI_ICON1                       102
#define IDI_ICON2                       104

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        105
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


code:
#include "BrowsingHistory.h"

BrowsingHistory::BrowsingHistory(std::function<void(URL const&)> onGotoPage_) :
    history(),
    currentPosition(history.end()),
    onGotoPage(onGotoPage_) {}

size_t BrowsingHistory::length() const {
    return history.size();
}

void BrowsingHistory::back() {
    go(-1);
}

void BrowsingHistory::forward() {
    go(1);
}

void BrowsingHistory::go() {
    go(0);
}

void BrowsingHistory::go(int diff) {
    if (history.size() == 0) {
        return;
    }
    std::vector<URL>::iterator newPos = currentPosition + diff;
    if (newPos >= history.begin() && newPos < history.end()) {
        currentPosition = newPos;
        onGotoPage(*currentPosition);
    }
}

void BrowsingHistory::pushState(void* stateObj, std::string const& title, URL const& url) {
    if (history.size() == 0) {
        history.push_back(url);
        currentPosition = history.begin();
        return;
    }
    URL currentURL = *currentPosition;
    history.erase(++currentPosition, history.end());
    history.push_back(currentURL.merge(url));
    currentPosition = history.end() - 1;
}

void BrowsingHistory::replaceState(void* stateObj, std::string const& title, URL const& url) {
    if (history.size() == 0) {
        return;
    }
    *currentPosition = (*currentPosition).merge(url);
}

void BrowsingHistory::print() {
    int i = 0;
    for(std::vector<URL>::iterator it=history.begin(); it!=history.end(); ++it) {
        std::cout << "BrowsingHistory::print " << i << ": " << it->toString() << std::endl;
        i++;
    }
}


code:
#ifndef BROWSINGHISTORY_H
#define BROWSINGHISTORY_H

#include <vector>
#include "URL.h"
#include <functional>

struct pointInHistory {
    URL realURL;
    URL displayURL;
    std::string title;
    void* stateObj; // limit of 640k here
};

/*
  NOTE: The first parameter of the constructor is the callback to be called when
        calling go(int) (or back or forward or whatever). Should be a
        function that navigates to the specified URL.
 */

// modelled after https://developer.mozilla.org/en-US/docs/Web/API/History
class BrowsingHistory {
public:
    BrowsingHistory(std::function<void(URL const&)> onGotoPage_);

    size_t length() const;
    //void scrollRestoration(); // TODO: Implement this when it's relevant
    //void* state() const; // TODO: Implement when we have Javascript objects.

    void back();
    void forward();
    void go();
    void go(int diff);

    // stateObj: void* should be a Javascript object once we have those. Just
    //           give it nullptr for the time being.
    // title: "Firefox currently ignores this parameter, although it may
    //         use it in the future.". So i guess we ignore it too? well we'll make plans to properly use it.
    void pushState(void* stateObj, std::string const& title, URL const& url);
    void replaceState(void* stateObj, std::string const& title, URL const& url);
    // onGotoPage can call onPopState... or maybe it should be renamed?
    // onPopState <- call when fwd/bck are used and sends the state associated with that point in history
    void print();

private:
    // push overrides the real URL with a display URL, we'll need to store both
    // also need to store a state
    std::vector<URL> history;
    std::vector<URL>::iterator currentPosition;
    std::function<void(URL const&)> onGotoPage;
};

#endif


code:
// ©2008-2017 despair <despair@netrunner.cc>
#include "CFGFileParser.h"
#include <climits>
#include "slre.h"

CFGFileParser::CFGFileParser(const char* filename){
    // Open the config file, get its size,
    // allocate the buffer, read it into
    // the buffer, close the file
    cfg_file = fopen(filename, "rb"); // On NT, opening in text mode translates \n into \r\n
    stat(filename, cfg_fileinfo);
    buffer = static_cast<char*>(tlsf_malloc(cfg_fileinfo->st_size & INT_MAX));
    if (cfg_file) {
        bytesRead = fread(buffer, sizeof(char) & INT_MAX, cfg_fileinfo->st_size & INT_MAX, cfg_file);
        if (!bytesRead) {
            std::cout << "can't read config" << std::endl;
        }
    } else {
        std::cout << "can't open config" << std::endl;
    }
    fclose(cfg_file);
}

CFGFileParser::CFGFileParser(ntr::fast_string in_buf){
    buffer = static_cast<char*>(tlsf_malloc(strlen(in_buf.c_str()) & INT_MAX));
    strcpy(buffer, in_buf.c_str());
}

CFGFileParser::~CFGFileParser(){
    // clean up!
    // This buffer has been written to and co-opted by
    // libc itself, but it stands that we initially allocated
    // it. So we'll free it ourselves.
    tlsf_free(buffer);
    // We've already copied the collected config directives
    // into a global class, we can safely delete the scratch
    // object, and its file info block.
    tlsf_free(cfg_fileinfo);
    delete cfg;
}

bool CFGFileParser::ParseText() {
    // Initial buffer for pass 1. tmp holds the config file (fmr. buffer)
    // token holds the actual token. pass1_length to keep track of memory,
    // reallocate as needed.
    char *tmp, *token;
    size_t pass1_length = 0;
    struct slre regex; // final validation before adding to second-pass buffer
    // Second pass. All comments and .tags stripped out. Starts off at 512 bytes, increases if necessary.
    char* directives = static_cast<char*>(tlsf_malloc(512));
    char* tmp2;
    token = strtok_r(buffer, "\n", &tmp);
    while (token != nullptr) {
        if (token[0] == '#' || token[0] == '.' || token[0] == '\n'){ // Comment, Perl directive, or single <LF> found, skip over
            token = strtok_r(nullptr, "\n", &tmp);
        }
        else if (!isalpha(token[0])){ // The leading character is NOT a #, ., <LF>, or a letter. NB: Leading numbers are also invalid.
            printf("ERROR: Config file is invalid: ");
            printf("\"%s\"\n",token);
            return false;
        }
        else {
            if (!slre_compile(&regex, "([A-Z0-9_]*)(:)(\\[.*\\])")){
                printf("Internal error.\n");
                return false;
            }
            if (!slre_match(&regex, token, strlen(token), nullptr)){
                printf("Invalid directive: \"%s\"\n", token); // regex matches DiReC[_]T[I1]VE:[token], it simply cannot start with a number or symbol.
                return false;
            }
            // Config directive found, add to second-pass buffer
            // once we reach the 0.5KB mark, realloc exactly enough to keep going
            if (pass1_length >= 512){
                tlsf_realloc(directives, pass1_length+strlen(token)+2);
            }
            strcat(directives, token);
            strcat(directives, "\t");
            pass1_length = strlen(directives);
            token = strtok_r(nullptr, "\n", &tmp); // continue
        }
    }
    // First pass complete, second pass: break up into single directives and <key, value> pairs
    char *newScope;
    token = strtok_r(directives, "\t", &newScope);
    ntr::fast_string key, value;
    while (token != nullptr){
        key=strtok_r(token, ":", &tmp2);
        value=strtok_r(nullptr, "[]", &tmp2);
        // strip ending bracket
        cfg->Settings.insert({key,value});
        token = strtok_r(nullptr, "\t", &newScope);
    }
    #ifdef DEBUG
    std::cout << "Settings stringmap contains, in no particular order:\n";
    for ( auto it = cfg->Settings.begin(); it != cfg->Settings.end(); ++it ){
        std::cout << it->first << ":" << it->second << std::endl;
    }
    std::cout << std::endl;
    #endif
    // clean up, we're done parsing
    tlsf_free(directives);
    return true;
}

// Writes the fully parsed configuration to a BrowserConfiguration
// object supplied by the user.
void CFGFileParser::WriteConfig(BrowserConfiguration &config){
    config.Settings = cfg->Settings;
}


code:
// ©2008-2017 despair <despair@netrunner.cc>
#ifndef __CFGFILEPARSER_H__
#define __CFGFILEPARSER_H__
#include "netrunner.h"
#include <sys/stat.h>
#include <cstdint>

// Class which holds the parsed configuration options after
// being processed by CFGFileParser. No longer a "trivial class"
// just select by cfg->GlobalSettings....
struct BrowserConfiguration{
    ntr::stringmap Settings;
    // fast allocation!
    static void* operator new(std::size_t n){
         if (void *mem = tlsf_malloc(n)) {
             return mem;
         } throw std::bad_alloc {};
    }
    static void operator delete(void *p) {
        tlsf_free(p);
    }
    void clear();
};

class CFGFileParser {
public:
    // Opens config from external file.
    CFGFileParser(const char* filename);
    // Opens config from memory.
    CFGFileParser(ntr::fast_string in_buf);
    ~CFGFileParser();
    bool ParseText();
    void WriteConfig(BrowserConfiguration &config);
    static void* operator new(std::size_t n) {
        if (void *mem = tlsf_malloc(n)){
             return mem;
         } throw std::bad_alloc {};
    }
    static void operator delete(void *p) {
        tlsf_free(p);
    }
private:
    FILE *cfg_file;
    char *buffer;
    struct stat *cfg_fileinfo = reinterpret_cast<struct stat*>(tlsf_malloc(sizeof(struct stat)));
    BrowserConfiguration *cfg = new BrowserConfiguration();
    size_t bytesRead;
};
#endif // __CFGFILEPARSER_H__


code:
#include "CommandLineParams.h"

#include <vector>

namespace {
    std::vector<std::string> params;
}

void initCLParams(int argc, char *argv[]) {
    params = std::vector<std::string> (argv, argv + argc);
}

bool getCLParam(std::string const& param) {
    for (auto const& p : params) {
        if (p == param) {
            return true;
        }
    }
    return false;
}

std::string getCLParamByIndex(unsigned int i) {
    if (i < params.size()) {
        return params[i];
    }
    return "";
}

std::string getRelativeCLParam(std::string const& param, int i) {
    for (unsigned int index = 0; index < params.size(); ++index) {
        if (params[index] == param) {
            int si = static_cast<int>(index);
            unsigned int finalPos = static_cast<unsigned int>(si + i);
            if (finalPos < params.size()) {
                return params[finalPos];
            }
            return "";
        }
    }
    return "";
}


code:
#ifndef COMMANDLINEPARAMS_H
#define COMMANDLINEPARAMS_H

#include <string>

void initCLParams(int argc, char *argv[]);

// USAGE: getCLParam("-example")
bool getCLParam(std::string const& param);

// 0 is the program name
std::string getCLParamByIndex(unsigned int i);

// USAGE: getRelativeCLParam("-example", 1) gives the param after -example
std::string getRelativeCLParam(std::string const& param, int i);

#endif


code:
#include "FormData.h"

// how do we match up nodes to components to extract the current values out of the components?
// well we link input components back to node (and/or nodes back to components)
// or we create a form UI component
std::unique_ptr<std::map<std::string, std::string>> buildFormData(std::shared_ptr<Node> formNode, std::unique_ptr<std::map<std::string, std::string>> result) {
    if (!result) {
        result = std::make_unique<std::map<std::string, std::string>>();
    }
    // check this node
    TagNode *tagNode = dynamic_cast<TagNode*>(formNode.get());
    if (tagNode) {
        auto typePropIter = tagNode->properties.find("type");
        bool skip = false;
        std::cout << "formData.buildFormData - tagNode element " << tagNode->tag << std::endl;
        if (typePropIter != tagNode->properties.end()) {
            if (typePropIter->second == "submit") {
                skip = true;
            }
            if (typePropIter->second == "checkbox") {
                // FIXME: see if it's checked or not
                skip = true;
            }
            if (typePropIter->second == "radio") {
                // FIXME: see if it's checked or not
                skip = true;
            }
        }
        auto namePropIter = tagNode->properties.find("name");
        if (!skip && namePropIter != tagNode->properties.end()) {
            auto valuePropIter = tagNode->properties.find("value");
            if (valuePropIter != tagNode->properties.end()) {
                auto it = result->find(namePropIter->second);
                if (it == result->end()) {
                    std::cout << "INPUTElement.buildFormData - setting " << namePropIter->second << " to [" << valuePropIter->second << "]" << std::endl;
                    result->insert(std::pair<std::string, std::string>(namePropIter->second, valuePropIter->second));
                } else {
                    (*result)[namePropIter->second] = valuePropIter->second;
                }
            }
        }
    }

    // check children noedes
    for (auto &child : formNode->children) {
        //std::shared_ptr<TagNode> tagNode = std::dynamic_pointer_cast<TagNode>(child);
        //if (tagNode) {
        result = buildFormData(child, std::move(result));
        //}
    }
    return result;
}

std::unique_ptr<std::vector<Node>> searchNodeTree(const std::shared_ptr<Node> &node, std::unique_ptr<std::vector<Node>> ret) {
    return ret;
}

FormData::FormData(std::shared_ptr<TagNode> start) {
    // recurse down find all interesting tags
    std::unique_ptr<std::vector<Node>> formNodes = std::make_unique<std::vector<Node>>();
    searchNodeTree(start, std::move(formNodes));
}


code:
#ifndef FORMDATA_H
#define FORMDATA_H

#include <string>
#include <map>
#include "parsers/markup/TagNode.h"

std::unique_ptr<std::map<std::string, std::string>> buildFormData(std::shared_ptr<Node> formNode, std::unique_ptr<std::map<std::string, std::string>> result);
std::unique_ptr<std::vector<Node>> searchNodeTree(const std::shared_ptr<Node> &node, std::unique_ptr<std::vector<Node>> ret);

class FormData {
public:
    FormData();
    FormData(std::shared_ptr<TagNode>);
    // creates arrays out of values
    //void append(std::string name, std::string value);
    //void append(std::string name, std::string value, str::string filename);
    // overwrites value
    //void set(std::string name, std::string value);
    //void set(std::string name, std::string value, str::string filename);
    // gets value (list if array)
    //void getAll(std::string name);
    std::map<std::string, std::string>::iterator entries();
    //std::map<std::string, std::string>::iterator keys();
    //std::map<std::string, std::string>::iterator values();
    std::map<std::string, std::string> data;
};

#endif


code:
#pragma once
#ifndef CACHE_H
#define CACHE_H

#include <unordered_map>
#include <list>
#include <utility>
#include <iostream>
#include <memory>
#include <mutex>

template<class K, class V>
class LRUCache {
	using Key_list = std::list<std::pair<K, std::shared_ptr<V>>>;
	Key_list key_values;
	std::unordered_map<K, typename Key_list::iterator> positions;
	size_t max_values;
	std::mutex cache_mutex;

	void remove_oldest() {
		while (positions.size() >= max_values) {
			positions.erase(key_values.back().first);
			key_values.pop_back();
		}
	}

public:
	LRUCache(const size_t max_values) : max_values(max_values) {
	}

	void insert(const K& key, std::shared_ptr<V> value) {
		std::lock_guard<std::mutex> lock(cache_mutex);
		remove_oldest();
		key_values.emplace_front(key, value);
		positions[key] = key_values.begin();
	}

	void insert(const K& key, V* value) {
		insert(key, std::shared_ptr<V>(value));
	}

	void insert(const K& key, V&& value) {
		insert(key, std::make_shared<V>(std::move(value)));
	}

	void insert(const K& key, V& value) {
		insert(key, std::make_shared<V>(value));
	}

	std::shared_ptr<V> find(const K& key) {
		std::lock_guard<std::mutex> lock(cache_mutex);
		auto p = positions.find(key);
		if (p == positions.end())
			return {};
		auto iter = p->second;
		auto& value = iter->second;
		// If the iterator is not at the front of the list, move it there.
		if (iter != key_values.begin()) {
			key_values.splice(
					key_values.begin(),
					key_values,
					iter,
					std::next(iter));
		}
		positions[key] = key_values.begin();
		return iter->second;
	}

	void debug_cache() {
		std::lock_guard<std::mutex> lock(cache_mutex);
		for (auto& p : key_values) {
			std::cout << p.first << ":" << *p.second << std::endl;
		}
	}
};

#endif


code:
#include "Log.h"

#include "CommandLineParams.h"
#include <streambuf>
#include <map>
#include "StringUtils.h"

namespace {
    class NoStream : public std::streambuf {
    public:
        int overflow(int c) { return c; }
    };

    NoStream noStream;
    std::ostream nothing (&noStream);
    std::map<LogType, Logger> loggerCache;

    std::ostream& log(LogType lt) {
        auto cacheEntry = loggerCache.find(lt);
        if (cacheEntry == loggerCache.end()) {
            auto& outStream = ((lt == LogType::ERROR) ? std::cerr : std::cout);
            auto newEntry = std::make_pair(lt, Logger(lt, outStream));
            loggerCache.insert(newEntry);
        }
        return loggerCache[lt]();
    }
}

Logger::Logger() :
    logType(LogType::DBUG),
    outStream(std::cout),
    shouldLog(true) {
}

Logger::Logger(LogType lt, std::ostream& out) :
    logType(lt),
    outStream(out),
    shouldLog(false) {
    std::string specifiedLog = toLowercase(getRelativeCLParam("-log", 1));
    LogType logLevel = LogType::ERROR;
    if (specifiedLog == "warning") {
        logLevel = LogType::WARNING;
    } else if (specifiedLog == "info") {
        logLevel = LogType::INFO;
    } else if (specifiedLog == "debug") {
        logLevel = LogType::DBUG;
    }

    if (lt <= logLevel) {
        shouldLog = true;
    }
}

std::ostream& Logger::operator()() {
    if (shouldLog) {
        return outStream;
    }
    return nothing;
}

std::ostream& logError() {
    return log(LogType::ERROR);
}

std::ostream& logWarning() {
    return log(LogType::WARNING);
}

std::ostream& logNotice() {
    return log(LogType::NOTICE);
}

std::ostream& logInfo() {
    return log(LogType::INFO);
}

std::ostream& logDebug() {
    return log(LogType::DBUG);
}


code:
#ifndef LOG_H
#define LOG_H

#include <iostream>

/*
  USAGE:
    The functions provided returns standard output/error streams depending
    on the log level specified at startup. Example usage:

    logWarning() << "WARNING WARNING" << std::endl;

    You can create custom logger objects if you want different output
    streams. You can then invoke these objects with function syntax:

    Logger loggg (LogLevel::INFO, std::cerr);
    loggg() << "HEY" << std::endl;

    Use the command line flag "-log <logtype>" to print logtypes higher
    than error. For example, if you specify "-log info", then the program
    will print out info, and all lower prioritized logtypes (warning+error).
 */
// I'd like logging macro(s) that auto include __FILE__ and __LINE__ for where the output is coming from (would be cool if we could include function/method name (and class if method)) - odilitime 17/08/02
enum class LogType {
    ERROR, // issue
    WARNING, // potential issue
    NOTICE, // basic user messages, Default include NOTICE, WARNING, ERROR
    INFO, // basic 10k ft overview of what's going on
    DBUG, // can't be DEBUG in clang
    CALLLIST // track each function call entrance and exit
};
// network logging should be separate

class Logger {
public:
    Logger();
    Logger(LogType lt, std::ostream& out);
    std::ostream& operator()();
private:
    LogType logType;
    std::ostream& outStream;
    bool shouldLog;
};

// can we have some sort of macro to make these noOPs if compile as such
// no need to compile in all the debug logging calls in release
std::ostream& logError();
std::ostream& logWarning();
std::ostream& logNotice();
std::ostream& logInfo();
std::ostream& logDebug();

#endif


code:
//-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.

// Note - The x86 and x64 versions do _not_ produce the same results, as the
// algorithms are optimized for their respective platforms. You can still
// compile and run any of them on any platform, but your performance with the
// non-native version will be less than optimal.

#include "Murmur3.h"

//-----------------------------------------------------------------------------
// Platform-specific functions and macros

// Microsoft Visual Studio

#if defined(_MSC_VER)

#define FORCE_INLINE	__forceinline

#include <stdlib.h>

#define ROTL32(x,y)	_rotl(x,y)
#define ROTL64(x,y)	_rotl64(x,y)

#define BIG_CONSTANT(x) (x)

// Other compilers

#else	// defined(_MSC_VER)

#define	FORCE_INLINE inline __attribute__((always_inline))

inline uint32_t rotl32 ( uint32_t x, int8_t r )
{
  return (x << r) | (x >> (32 - r));
}

inline uint64_t rotl64 ( uint64_t x, int8_t r )
{
  return (x << r) | (x >> (64 - r));
}

#define	ROTL32(x,y)	rotl32(x,y)
#define ROTL64(x,y)	rotl64(x,y)

#define BIG_CONSTANT(x) (x##LLU)

#endif // !defined(_MSC_VER)

//-----------------------------------------------------------------------------
// Block read - if your platform needs to do endian-swapping or can only
// handle aligned reads, do the conversion here

FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i )
{
  return p[i];
}

FORCE_INLINE uint64_t getblock64 ( const uint64_t * p, int i )
{
  return p[i];
}

//-----------------------------------------------------------------------------
// Finalization mix - force all bits of a hash block to avalanche

FORCE_INLINE uint32_t fmix32 ( uint32_t h )
{
  h ^= h >> 16;
  h *= 0x85ebca6b;
  h ^= h >> 13;
  h *= 0xc2b2ae35;
  h ^= h >> 16;

  return h;
}

//----------

FORCE_INLINE uint64_t fmix64 ( uint64_t k )
{
  k ^= k >> 33;
  k *= BIG_CONSTANT(0xff51afd7ed558ccd);
  k ^= k >> 33;
  k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
  k ^= k >> 33;

  return k;
}

//-----------------------------------------------------------------------------

void MurmurHash3_x86_32 ( const void * key, int len,
                          uint32_t seed, void * out ) {
  const uint8_t * data = static_cast<const uint8_t*>(key);
  const int nblocks = len / 4;

  uint32_t h1 = seed;

  const uint32_t c1 = 0xcc9e2d51;
  const uint32_t c2 = 0x1b873593;

  //----------
  // body

  const uint32_t * blocks = reinterpret_cast<const unsigned int *>(data + nblocks*4);

  for(int i = -nblocks; i; i++)
  {
    uint32_t k1 = getblock32(blocks,i);

    k1 *= c1;
    k1 = ROTL32(k1,15);
    k1 *= c2;

    h1 ^= k1;
    h1 = ROTL32(h1,13);
    h1 = h1*5+0xe6546b64;
  }

  //----------
  // tail

  const uint8_t * tail = (data + nblocks*4);

  uint32_t k1 = 0;

  switch(len & 3) {
    case 3: k1 ^= static_cast<unsigned int>(tail[2]) << 16;
    case 2: k1 ^= static_cast<unsigned int>(tail[1]) << 8;
    case 1: k1 ^= static_cast<unsigned int>(tail[0]);
            k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
    break;
    default:
    break;
  };

  //----------
  // finalization

  h1 ^= static_cast<unsigned int>(len);

  h1 = fmix32(h1);

  *static_cast<uint32_t*>(out) = h1;
}

//-----------------------------------------------------------------------------

void MurmurHash3_x86_128 ( const void * key, const int len,
                           uint32_t seed, void * out )
{
  const uint8_t * data = static_cast<const uint8_t*>(key);
  const int nblocks = len / 16;

  uint32_t h1 = seed;
  uint32_t h2 = seed;
  uint32_t h3 = seed;
  uint32_t h4 = seed;

  const uint32_t c1 = 0x239b961b;
  const uint32_t c2 = 0xab0e9789;
  const uint32_t c3 = 0x38b34ae5;
  const uint32_t c4 = 0xa1e38b93;

  //----------
  // body

  const uint32_t * blocks = reinterpret_cast<const uint32_t *>(data + nblocks*16);

  for(int i = -nblocks; i; i++)
  {
    uint32_t k1 = getblock32(blocks,i*4+0);
    uint32_t k2 = getblock32(blocks,i*4+1);
    uint32_t k3 = getblock32(blocks,i*4+2);
    uint32_t k4 = getblock32(blocks,i*4+3);

    k1 *= c1; k1  = ROTL32(k1,15); k1 *= c2; h1 ^= k1;

    h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;

    k2 *= c2; k2  = ROTL32(k2,16); k2 *= c3; h2 ^= k2;

    h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;

    k3 *= c3; k3  = ROTL32(k3,17); k3 *= c4; h3 ^= k3;

    h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;

    k4 *= c4; k4  = ROTL32(k4,18); k4 *= c1; h4 ^= k4;

    h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
  }

  //----------
  // tail

  const uint8_t * tail = (data + nblocks*16);

  uint32_t k1 = 0;
  uint32_t k2 = 0;
  uint32_t k3 = 0;
  uint32_t k4 = 0;

  switch(len & 15) {
  case 15: k4 ^= static_cast<unsigned int>(tail[14]) << 16;
  case 14: k4 ^= static_cast<unsigned int>(tail[13]) << 8;
  case 13: k4 ^= static_cast<unsigned int>(tail[12]) << 0;
           k4 *= c4; k4  = ROTL32(k4,18); k4 *= c1; h4 ^= k4;

  case 12: k3 ^= static_cast<unsigned int>(tail[11]) << 24;
  case 11: k3 ^= static_cast<unsigned int>(tail[10]) << 16;
  case 10: k3 ^= static_cast<unsigned int>(tail[ 9]) << 8;
  case  9: k3 ^= static_cast<unsigned int>(tail[ 8]) << 0;
           k3 *= c3; k3  = ROTL32(k3,17); k3 *= c4; h3 ^= k3;

  case  8: k2 ^= static_cast<unsigned int>(tail[ 7]) << 24;
  case  7: k2 ^= static_cast<unsigned int>(tail[ 6]) << 16;
  case  6: k2 ^= static_cast<unsigned int>(tail[ 5]) << 8;
  case  5: k2 ^= static_cast<unsigned int>(tail[ 4]) << 0;
           k2 *= c2; k2  = ROTL32(k2,16); k2 *= c3; h2 ^= k2;

  case  4: k1 ^= static_cast<unsigned int>(tail[ 3]) << 24;
  case  3: k1 ^= static_cast<unsigned int>(tail[ 2]) << 16;
  case  2: k1 ^= static_cast<unsigned int>(tail[ 1]) << 8;
  case  1: k1 ^= static_cast<unsigned int>(tail[ 0]) << 0;
           k1 *= c1; k1  = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
  break;
  default:
  break;
  };

  //----------
  // finalization

  h1 ^= static_cast<unsigned int>(len); h2 ^= static_cast<unsigned int>(len); h3 ^= static_cast<unsigned int>(len); h4 ^= static_cast<unsigned int>(len);

  h1 += h2; h1 += h3; h1 += h4;
  h2 += h1; h3 += h1; h4 += h1;

  h1 = fmix32(h1);
  h2 = fmix32(h2);
  h3 = fmix32(h3);
  h4 = fmix32(h4);

  h1 += h2; h1 += h3; h1 += h4;
  h2 += h1; h3 += h1; h4 += h1;

  (static_cast<uint32_t*>(out))[0] = h1;
  (static_cast<uint32_t*>(out))[1] = h2;
  (static_cast<uint32_t*>(out))[2] = h3;
  (static_cast<uint32_t*>(out))[3] = h4;
}

//-----------------------------------------------------------------------------

void MurmurHash3_x64_128 ( const void * key, const int len,
                           const uint32_t seed, void * out ) {
  const uint8_t * data = reinterpret_cast<const uint8_t*>(key);
  const int nblocks = len / 16;

  uint64_t h1 = seed;
  uint64_t h2 = seed;

  const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
  const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);

  //----------
  // body

  const uint64_t * blocks = reinterpret_cast<const uint64_t *>(data);

  for(int i = 0; i < nblocks; i++) {
    uint64_t k1 = getblock64(blocks,i*2+0);
    uint64_t k2 = getblock64(blocks,i*2+1);

    k1 *= c1; k1  = ROTL64(k1,31); k1 *= c2; h1 ^= k1;

    h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;

    k2 *= c2; k2  = ROTL64(k2,33); k2 *= c1; h2 ^= k2;

    h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
  }

  //----------
  // tail

  const uint8_t * tail = (data + nblocks*16);

  uint64_t k1 = 0;
  uint64_t k2 = 0;

  switch(len & 15) {
    case 15: k2 ^= (static_cast<uint64_t>(tail[14])) << 48;
    case 14: k2 ^= (static_cast<uint64_t>(tail[13])) << 40;
    case 13: k2 ^= (static_cast<uint64_t>(tail[12])) << 32;
    case 12: k2 ^= (static_cast<uint64_t>(tail[11])) << 24;
    case 11: k2 ^= (static_cast<uint64_t>(tail[10])) << 16;
    case 10: k2 ^= (static_cast<uint64_t>(tail[ 9])) << 8;
    case  9: k2 ^= (static_cast<uint64_t>(tail[ 8])) << 0;
             k2 *= c2; k2  = ROTL64(k2,33); k2 *= c1; h2 ^= k2;

    case  8: k1 ^= (static_cast<uint64_t>(tail[ 7])) << 56;
    case  7: k1 ^= (static_cast<uint64_t>(tail[ 6])) << 48;
    case  6: k1 ^= (static_cast<uint64_t>(tail[ 5])) << 40;
    case  5: k1 ^= (static_cast<uint64_t>(tail[ 4])) << 32;
    case  4: k1 ^= (static_cast<uint64_t>(tail[ 3])) << 24;
    case  3: k1 ^= (static_cast<uint64_t>(tail[ 2])) << 16;
    case  2: k1 ^= (static_cast<uint64_t>(tail[ 1])) << 8;
    case  1: k1 ^= (static_cast<uint64_t>(tail[ 0])) << 0;
             k1 *= c1; k1  = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
    break;
    default:
    break;
  };

  //----------
  // finalization

  h1 ^= static_cast<unsigned int>(len); h2 ^= static_cast<unsigned int>(len);

  h1 += h2;
  h2 += h1;

  h1 = fmix64(h1);
  h2 = fmix64(h2);

  h1 += h2;
  h2 += h1;

  (static_cast<uint64_t*>(out))[0] = h1;
  (static_cast<uint64_t*>(out))[1] = h2;
}

//-----------------------------------------------------------------------------


code:
//-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.

#ifndef _MURMURHASH3_H_
#define _MURMURHASH3_H_

//-----------------------------------------------------------------------------
// Platform-specific functions and macros

// Microsoft Visual Studio

#if defined(_MSC_VER) && (_MSC_VER < 1600)

typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned __int64 uint64_t;

// Other compilers

#else	// defined(_MSC_VER)

#include <stdint.h>

#endif // !defined(_MSC_VER)

//-----------------------------------------------------------------------------

void MurmurHash3_x86_32  ( const void * key, int len, uint32_t seed, void * out );

void MurmurHash3_x86_128 ( const void * key, int len, uint32_t seed, void * out );

void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out );

//-----------------------------------------------------------------------------

#endif // _MURMURHASH3_H_


code:
#include "StringUtils.h"

#include <algorithm>
#include <iostream>

#include <iterator>
#include <algorithm>

/**
 * get an extension from a filename
 * @param fileName a filename string
 * @return '' or a string with the found extension
 */
const std::string getFilenameExtension(std::string const& fileName) {
    auto dotPos = fileName.find_last_of('.');
    if (dotPos != std::string::npos && dotPos + 1 != std::string::npos) {
        //std::cout << "StringUtils::getFilenameExtension - " << fileName.substr(dotPos + 1) << std::endl;
        return fileName.substr(dotPos + 1);
    }
    return "";
}

/**
 * get an document from a url
 * @param url url string
 * @return '' or a string with the found document
 */
const std::string getDocumentFromURL(const std::string &url) {
    int slashes = 0;
    for (unsigned int i = 0; i < url.length(); i++) {
        if (url[i] == '/') {
            slashes++;
            if (slashes == 3) {
                return url.substr(i, url.length() - i);
            }
        }
    }
    return "";
}

/**
 * get host from a url
 * @param url url string
 * @return '' or a string with the found host
 */
const std::string getHostFromURL(const std::string &url) {
    auto colonDoubleSlash = url.find("://");
    if (colonDoubleSlash != std::string::npos) {
        auto startPos = colonDoubleSlash + 3;
        auto thirdSlash = url.find("/", startPos);
        if (thirdSlash == std::string::npos) {
            return url.substr(startPos);
        }
        return url.substr(startPos, thirdSlash - startPos);
    }
    return "";
}

/**
 * get scheme from a url
 * @param url url string
 * @return '' or a string with the found protocol
 */
const std::string getSchemeFromURL(const std::string &url) {
    auto colonDoubleSlash = url.find("://");
    if (colonDoubleSlash != std::string::npos) {
        return url.substr(0, colonDoubleSlash);
    }
    return "";
}

/**
 * convert string to lowercase
 * @param str string
 * @return lowercased version of str
 */
const std::string toLowercase(const std::string &str) {
    std::string returnString = "";
    std::transform(str.begin(),
                   str.end(),
                   back_inserter(returnString),
                   tolower);
    return returnString;
/*
    std::string stringToLower(std::string s) {
    std::transform(s.begin(), s.end(), s.begin(),
            [](unsigned char c){ return std::tolower(c); });
    return s;
*/
}


code:
#ifndef STRINGUTILS_H
#define STRINGUTILS_H

#include <string>
const std::string toLowercase(const std::string &str);

// should be moved to URL
const std::string getFilenameExtension(std::string const& fileName);
const std::string getDocumentFromURL(const std::string &url);
const std::string getHostFromURL(const std::string &url);
const std::string getSchemeFromURL(const std::string &url);

#endif


code:
#include "TextBlock.h"
#include <iostream>

void TextBlock::print() {
    std::cout << "TextBlock::print - " << this->lines.size() << std::endl;
    for(auto line: this->lines) {
        std::cout << "[" << line << "]" << std::endl;
    }
    std::cout << "TextBlock::print - trailingNewline " << this->trailingNewline << std::endl;
}

// only safe to one char rn
void TextBlock::insertAt(std::string str, size_t x, size_t y) {
    if (y > this->lines.size()) {
        std::cout << "TextArea::insertCharAt - y: " << y << " lines: " << this->lines.size() << std::endl;
        return;
    }
    if (!this->lines.size()) {
        this->lines.push_back(str);
        return;
    }
    if (x > this->lines[y].size()) {
        this->lines[y] += str;
        return;
    }
    // FIXME: search entire str for any \r
    if (str == "\r") {
        this->lines.push_back("");
        // does this mean we need to insert one
        // or that we have one?
        // what problem was it originally trying to solve?
        //this->trailingNewline = true;
        // not sure what up's with this shit but it breaks a lot...
        //this->setValue(this->getValue()); // in-efficient but should give a correctish algorithm
    } else {
        //std::cout << "TextBlock::insertAt - [" << this->lines[y].substr(0, x) << "]+[" << str << "]+[" << this->lines[y].substr(x) << "]" << std::endl;
        this->lines[y] = this->lines[y].substr(0, x) + str + this->lines[y].substr(x);
    }
    //std::cout << "TextBlock::insertAt - lines: " << this->lines.size() << std::endl;
}

// only tested to one char atm
void TextBlock::deleteAt(size_t x, size_t y, size_t count) {
    //std::cout << "TextBlock::deleteAt - start delete " << count << " chars @ " << x << "," << y << std::endl;
    if (y > this->lines.size()) {
        std::cout << "TextArea::deleteAt - y: " << y << " lines: " << this->lines.size() << std::endl;
        return;
    }
    // could we just remove the if?
    if (count) {
        if (x == 0) {
            if (y) {
                //std::cout << "TextArea::deleteAt - y!=0, lines: " << this->lines.size() << std::endl;
                if (y < this->lines.size()) {
                    //std::cout << "TextArea::deleteAt - nuked line [" << y << "] chars on line: " << this->lineLength(y) << std::endl;
                    if (this->lineLength(y)) {
                        this->trailingNewline = true;
                    }
                    this->lines.erase(this->lines.begin() + static_cast<int>(y));
                    return;
                } else {
                    if (y == this->lines.size()) {
                        // delete the last line
                        if (this->trailingNewline) {
                            this->trailingNewline = false;
                        } else {
                            std::cout << "TextArea::deleteAt - do nothing" << std::endl;
                        }
                        return;
                    }
                    std::cout << "TextArea::deleteAt - requesting deletion at " << x << "," << y << " but only " << this->lines.size() << std::endl;
                }
            } // else we're at 0,0 deleting a char
        }
        if (x >= this->lineLength(y)) {
            //std::cout << "TextArea::deleteAt - x == lineLength [" << this->lines[y].substr(0, x - 1) << "]" << std::endl;
            if (x) {
                this->lines[y] = this->lines[y].substr(0, x - 1);
            } else {
                this->trailingNewline = false;
            }
        } else {
            if (x) {
                if (x == this->lineLength(y) - 1) {
                    //std::cout << "TextArea::deleteAt - endOfLine [" << this->lines[y].substr(0, x) << "]+[" << this->lines[y].substr(x + count) << "]" << std::endl;
                    this->lines[y] = this->lines[y].substr(0, x) + this->lines[y].substr(x + count);
                } else {
                    //std::cout << "TextArea::deleteAt - x>0 [" << this->lines[y].substr(0, x) << "]+[" << this->lines[y].substr(x + count) << "]" << std::endl;
                    this->lines[y] = this->lines[y].substr(0, x) + this->lines[y].substr(x + count);
                }
            } else {
                //std::cout << "TextArea::deleteAt - x@0 [" << this->lines[y].substr(x + count) << "]" << std::endl;
                this->lines[y] = this->lines[y].substr(x + count);
            }
        }
    } else {
        this->lines[y] = this->lines[y].substr(0, x);
    }
}

size_t TextBlock::setValue(std::string value) {
    // split on \n
    std::string delimiter(1, '\r');
    this->lines.clear();

    size_t pos = 0;
    size_t lPos = 0;
    size_t tLines = 0; // start at 0 (should this start at 1?)
    this->trailingNewline = false;
    while ((pos = value.find(delimiter, lPos)) != std::string::npos) {
        //std::cout << "TextBlock::setValue pushing [" << value.substr(lPos, pos) << "]" << std::endl;
        std::string token = value.substr(lPos, pos);
        if (token[token.size() - 1] == '\r') {
            token.pop_back();
            //std::cout << "TextBlock::setValue adjusted [" << token << "]" << std::endl;
            //std::cout << "lPos: " << lPos << " pos: " << pos << " size: " << value.size() << std::endl;
            if (pos == value.size() - 1) {
                //std::cout << "TextBlock::setValue - enabling trailing line" << std::endl;
                this->trailingNewline = true;
            }
        }
        this->lines.push_back(token);
        //value.erase(0, pos + delimiter.length());
        lPos = pos + delimiter.length();
        tLines++;
    }
    //std::cout << "lPos: " << lPos << " pos: " << pos << " size: " << value.size() << " last: [" << value.substr(lPos) << "]" << std::endl;
    // if we're not at the end of the string, we don't end on a trailing slash
    // lPos == value.size()? true :
    if (value.substr(lPos) != "") {
        //std::cout << "Pushing back [" << value.substr(lPos) << "]" << std::endl;

        this->lines.push_back(value.substr(lPos));
    } else {
        if (lPos == 1) {
            this->trailingNewline = true;
        }
    }
    return tLines;
}

std::string TextBlock::getValue() {
    std::string value = "";
    for(auto s : this->lines) {
        value += s + '\r';
    }
    //std::cout << "TextBlock::getValue before strip[" << !this->trailingNewline << "] [" << value << "] lines[" << this->lines.size() << "]" << std::endl;
    if (!this->trailingNewline && this->lines.size()) {
        value.pop_back();
    }
    return value;
}

std::string TextBlock::getValueUpTo(size_t x, size_t y) {
    std::string value;
    if (y && this->lines.size()) {
        for(size_t cy = 0; cy < y; cy++) {
            value += this->lines[cy] + "\r";
        }
        // we don't want this because 1st char on the 2nd line breaks
        /*
        // if we're not at the start of a new line, remove the newline
        if (this->lineLength(y)) {
            std::cout << "TextBlock::getValueUpTo - removing added newline because this line has text" << std::endl;
            value.pop_back();
        }
        */
        //std::cout << "TextBlock::getValueUpTo y read[" << value << "]" << std::endl;
    }
    if (x) {
        //std::cout << "TextBlock::getValueUpTo value pre x [" << value << "]" << std::endl;
        value += this->lines[y].substr(0, x);
        //std::cout << "TextBlock::getValueUpTo x [" << value << "]" << std::endl;
    }
    //std::cout << "TextBlock::getValueUpTo trailingNewline: " << this->trailingNewline << std::endl;
    /*
    if (this->trailingNewline) {
        // and we're at the end of the last line
        //std::cout << "lines: " << this->lines.size() << " lineLength: " << this->lineLength(y) << std::endl;
        // x can't be at the start of the line
        //if (y == this->lines.size() - 1 && x == this->lineLength(y) && x) {
            //std::cout << "TextBlock::getValueUpTo adding a trailingNewline" << std::endl;
            //value += "\r";
        //}
    }
    */
    return value;
}

size_t TextBlock::lineLength(size_t y) {
    if (y >= this->lines.size()) {
        if (!this->trailingNewline) {
            std::cout << "TextArea::lengthLine - y: " << y << " lines: " << this->lines.size() << std::endl;
        }
        return 0;
    }
    return this->lines[y].size();
}


code:
#ifndef TEXTBLOCK_H
#define TEXTBLOCK_H

#include <string>
#include <vector>

class TextBlock {
public:
    // methods
    void print();
    void insertAt(std::string str, size_t x, size_t y);
    void deleteAt(size_t x, size_t y, size_t count);
    std::string getValue();
    std::string getValueUpTo(size_t x, size_t y);
    size_t setValue(std::string value);
    size_t lineLength(size_t y);
    // properties
    std::vector<std::string> lines;
    bool trailingNewline = false;
};

#endif


code:
#include "URL.h"

#include "StringUtils.h"

/*
 * From the following RFC: https://tools.ietf.org/html/rfc3986
 *
 * pct-encoded = "%" HEXDIG HEXDIG
 * reserved    = gen-delims / sub-delims
 * gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@"
 * sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
 *                / "*" / "+" / "," / ";" / "="
 *
 */

enum URIParseState {
    SCHEME,
    FIRST_SLASH,
    SECOND_SLASH_OR_ELSE,
    AUTHORITY,
    AUTHORITY_USERINFO, /* The part before '@' */
    AUTHORITY_PASSWORD, /* RFC states that we should either reject or ignore it (We ignore it) */
    AUTHORITY_HOST,
    AUTHORITY_PORT,
    PATH,
    QUERY,
    FRAGMENT,
};

// TODO
bool isValidCharacter(char c);
bool isValidCharacter(char c) {
    return true;
}

// put inside URL(std::string) constructor?
std::tuple<std::unique_ptr<URL>,enum URIParseError> parseUri(std::string raw) {
    std::unique_ptr<URL> uri = std::make_unique<URL>();
    uri->path = "/";
    unsigned int cursor = 0;
    unsigned int last = 0;
    unsigned int lastSemicolon = 0;
    bool isPercentEncoded = true;
    enum URIParseState state = SCHEME;
    // TODO Validate at the end that every field were defined (ie: http:// is valid, but it's clearly not)
    // Remember file:// doesn't need a port (for end validation)

    if (raw[cursor] == '/') {
        // unless it's / for relative URLs
        // need this so we can merge URLs

        // make sure it's not // (relative scheme)
        if (raw.length() > 1) {
            if (raw[cursor + 1] == '/') {
                // there is a host
                uri->scheme = "relative";
                state = AUTHORITY;
            } else {
                 // relative path
                uri->scheme = ""; // probably doesn't matter if it's "" or "relative"
                state = PATH;
            }
        }
    } else {
        // First character of scheme MUST be alphabetic
        if (!isalpha(raw[cursor])) {
            std::cerr << "invalid scheme: '" << raw.substr(last, cursor - last+1) << "'" << std::endl;
            return std::make_tuple(std::move(uri), URI_PARSE_ERROR_SCHEME);
        }
    }
    for (cursor = 1; cursor < raw.length(); cursor++) {
        /* TODO
         * Allow scheme-less uri (and fallback to https/http) */
        if (state == SCHEME) {
            if (raw[cursor] == ':') {
                uri->scheme = toLowercase(raw.substr(0, cursor));
                /* TODO
                 * Put default port now (Should use a table for that but
                 * I don't know C++ enough) */
                if (uri->scheme == "http") {
                    uri->port = 80;
                }
				if (uri->scheme == "https"){
					uri->port = 443;
				}
                state = FIRST_SLASH;
            } else if (!isalpha(raw[cursor]) && !isdigit(raw[cursor]) && raw[cursor] != '+' &&
                       raw[cursor] != '-' && raw[cursor] != '.'  && raw[cursor] != '/') { // why this exception list?
                /* URI MUST have a scheme  */
                std::cerr << "invalid scheme: '" << raw.substr(last, cursor - last+1) << "' cursor: " << cursor << std::endl;
                return std::make_tuple(std::move(uri), URI_PARSE_ERROR_SCHEME);
            }
        } else if (state == FIRST_SLASH) {
            if (raw[cursor] == '/') {
                state = SECOND_SLASH_OR_ELSE;
            } else {
                std::cerr << "missing '/' after scheme" << std::endl;
                return std::make_tuple(std::move(uri), URI_PARSE_ERROR_SCHEME);
            }
        } else if (state == SECOND_SLASH_OR_ELSE) {
            if (raw[cursor] == '/') {
                last = cursor + 1;
                state = AUTHORITY;
                if (uri->scheme == "file") {
                    //std::cout << "URL:::parseUri file scheme, current path[" << uri->path << "]" << std::endl;
                    state = PATH;
                }
            } else {
                // TODO Handle this, URI may have only one slash
                std::cerr << "URI with only one '/' not currently supported" << std::endl;
                return std::make_tuple(std::move(uri), URI_PARSE_ERROR_SCHEME);
            }
        } else if (state == AUTHORITY) {
            /* At this point, this could either be the semi colon for
             * the password or for the port*/
            if (raw[cursor] == ':') {
                lastSemicolon = cursor;
            } else if (raw[cursor] == '@') {
                uri->userinfo = raw.substr(last, cursor - last);
                last = cursor + 1;
                state = AUTHORITY_HOST;
                // Authority is finished, everything should be considered as the host[port].
                //  TODO terminated by the next slash ("/"), question mark ("?"), or number sign ("#") character, or by the end of the URI.
                //  What to do when ? and # ?
            } else if (raw[cursor] == '/') {
                if (lastSemicolon > 0) {
                    // TODO Validate port
                    if (cursor - lastSemicolon - 1 > 0) {
                        uri->port = std::stoi(raw.substr(lastSemicolon+1, cursor - lastSemicolon+1));
                    }
                    uri->host = raw.substr(last, lastSemicolon - last);
                } else {
                    uri->host = raw.substr(last, cursor - last);
                }
                last = cursor;
                cursor--;
                state = PATH;
            } else if (raw[cursor] == '?' || raw[cursor] == '#') {
                uri->host = raw.substr(last, cursor - last);
                last = cursor;
                if (raw[cursor] == '?') {
                    state = QUERY;
                } else {
                    state = FRAGMENT;
                }
            } else if (cursor + 1 == raw.length()) {
                uri->host = raw.substr(last, lastSemicolon - last);
                uri->path = "/";
                break;
            } else {
                if (isPercentEncoded && !isValidCharacter(raw[cursor])) {
                    isPercentEncoded = false;
                }
            }
        // TODO Accept Ipv weirdness (ipversion and literal)
        /* We are accepting pretty much anything here.. However not everything
         * is valid. Should we try to validate at this point ? */
        } else if (state == AUTHORITY_HOST) {
            if (raw[cursor] == ':') {
                uri->host = raw.substr(last, cursor - last);
                last = cursor+1;
                state = AUTHORITY_PORT;
            } else if (raw[cursor] == '/') {
                uri->host = raw.substr(last, cursor - last);
                last = cursor;
                cursor--;
                state = PATH;
            } else {
                if (isPercentEncoded && !isValidCharacter(raw[cursor])) {
                    isPercentEncoded = false;
                }
            }
        } else if (state == AUTHORITY_PORT) {
            if (raw[cursor] == '/') {
                if (cursor - last > 0) {
                    /* RFC doesn't specify the max port number, so at this point
                     * just accept it. */
                    uri->port = std::stoi(raw.substr(last, cursor - last));
                }
                last = cursor;
                cursor--;
                state = PATH;
            } else if (!isdigit(raw[cursor])) {
                std::cerr << "invalid port: '" << raw.substr(last, cursor - last + 1) << "'" << std::endl;
                return std::make_tuple(std::move(uri), URI_PARSE_ERROR_PORT);
            }
        } else if (state == PATH) {
            //std::cout << "state == PATH char:" << raw[cursor] << std::endl;
            if (raw[cursor] == '?' || raw[cursor] == '#') {
                uri->path = raw.substr(last, cursor - last);
                last = cursor;
                if (raw[cursor] == '?') {
                    state = QUERY;
                } else {
                    state = FRAGMENT;
                }
            } else if (cursor + 1 == raw.length()) {
                uri->path = raw.substr(last, cursor + 1 - last);
                break;
            }
        } else if (state == QUERY) {
            //std::cout << "parsing query " << raw[cursor] << " @ " << (cursor + 1) << "/" << raw.length() << std::endl;
            if (raw[cursor] == '#') {
                uri->query = raw.substr(last + 1, cursor - last - 1);
                std::cout << "Parsed query " << uri->query << std::endl;
                last = cursor;
                state = FRAGMENT;
            } else if (cursor + 1 == raw.length()) {
                uri->query = raw.substr(last + 1, cursor + 1 - last);
                std::cout << "Parsed query " << uri->query << std::endl;
                break;
            }
        } else if (state == FRAGMENT) {
            if (cursor + 1 == raw.length()) {
                uri->fragment = raw.substr(last + 1, cursor + 1 - last);
                break;
            }
        }
    }
    /*
     * File scheme allows a host (ie: file://<host>/<path>)
     * If the host is empty then be default to localhost
     */
    if (uri->host.length() == 0 && uri->scheme == "file") {
        uri->host = "127.0.0.1";
    }
    return std::make_tuple(std::move(uri), URI_PARSE_ERROR_NONE);
}

// Why do we need this ? -tomleb
URL::URL() {
    scheme = "";
    userinfo = "";
    host = "";
    port = 0;
    path = "";
    query = "";
    fragment = "";
}

URL::URL(std::string const& url) {
    construct(url);
}

std::string URL::toString() const {
    //std::cout << "scheme[" << scheme << "] host[" << host << "] path [" << path << "] query[" << query << "]" << std::endl;
    if (isRelative()) {
        return path;
    }
    /* File is most likely gonna be localhost, we don't wanna print it .. */
    if (scheme == "file") {
        return scheme + "://" + path;
    } else {
        if (query != "") {
            return scheme + "://" + host + path + "?" + query;
        }
        return scheme + "://" + host + path;
    }
}

bool URL::isRelative() const {
    return scheme.size() == 0;
}

URL URL::merge(URL const& url) const {
    if (!url.isRelative()) {
        return url.copy();
    }

    URL returnURL = copy();
    //std::cout << "URL::merge - source " << url.toString() << std::endl;
    //std::cout << "URL::merge - copy " << returnURL.toString() << std::endl;

    if (url.path[0] == '/' && url.path[1] == '/') {
        auto slashPos = url.path.find('/', 2);
        returnURL.host = url.path.substr(2, slashPos - 2);
        if (slashPos == std::string::npos) {
            returnURL.path = "/";
        } else {
            returnURL.path = url.path.substr(slashPos);
        }
    } else if (url.path[0] == '/') {
        returnURL.path = url.path;
        // FIXME: make smarter about merging query strings
        if (url.query !="") {
            returnURL.query += "&"+url.query;
        }
    } else {
        if (returnURL.path.back() != '/') {
            auto finalSlashPos = returnURL.path.find_last_of('/');
            returnURL.path.erase(finalSlashPos + 1);
        }
        returnURL.path += url.path;
    }

    /*
    auto hashPos = returnURL.document.find("#");
    if (hashPos != std::string::npos) {
        returnURL.document = returnURL.document.substr(0, hashPos);
    }
    */

    return returnURL;
}

void URL::construct(std::string const& url) {
    auto result = parseUri(url);
    if (std::get<1>(result) != URI_PARSE_ERROR_NONE) {
        // TODO We probably wanna handle this better..
        std::cerr << "error parsing uri" << std::endl;
        return;
    }
    std::unique_ptr<URL> uri = std::move(std::get<0>(result));
    scheme = uri->scheme;
    userinfo = uri->userinfo;
    host = uri->host;
    port = uri->port;
    path = uri->path;
    query = uri->query;
    fragment = uri->fragment;

    /*
    scheme = getSchemeFromURL(url);
    if (scheme.size() != 0) {
        host = getHostFromURL(url);
        path = getDocumentFromURL(url);
    } else {
        host = "";
        path = url;
    }

    if (path.size() == 0) {
        path = "/";
    }
    */
}

URL URL::copy() const {
    URL url;
    url.scheme = scheme;
    url.userinfo = userinfo;
    url.host = host;
    url.port = port;
    url.path = path;
    url.query = query;
    url.fragment = fragment;
    return url;
}

std::ostream& operator<<(std::ostream& out, URL const& url) {
    out << url.toString();
    return out;
}


code:
#ifndef URL_H
#define URL_H

#include <string>
#include <memory>
#include <iostream>

enum URIParseError {
    URI_PARSE_ERROR_SCHEME,
    URI_PARSE_ERROR_PORT,
    URI_PARSE_ERROR_NONE,
};

struct URL {
    URL();
    URL(std::string const& url);

    std::string toString() const;
    bool isRelative() const;
    URL merge(URL const& url) const;

    // switching to the official RFC names
    std::string scheme; // was protocol
    //struct Authority authority; /* Can be empty */
    std::string userinfo;
    std::string host;
    int port;
    std::string path; // was document
    std::string query; // ?blablabla=asdfasf....
    std::string fragment; // #asfawefm

private:
    void construct(std::string const& url);
    URL copy() const;
};

std::tuple<std::unique_ptr<URL>,enum URIParseError> parseUri(std::string raw);
std::ostream& operator<<(std::ostream& out, URL const& url);

#endif


code:
#include "WebResource.h"

#include <fstream>
#include <map>
#include <algorithm>
#include "networking/HTTPCommon.h"
#include "networking/HTTPRequest.h"
#include "networking/HTTPResponse.h"
#include "networking/HTTPSRequest.h"
#include "StringUtils.h"
#include <iostream>
#include "Log.h"

namespace {

    // only used for local files atm
    std::map<std::string, ResourceType> strToRT = {
        {"html", ResourceType::HTML},
        {"txt", ResourceType::TXT},
        {"text", ResourceType::TXT},
        {"css", ResourceType::CSS}, //TXT?
        {"js", ResourceType::JS} //TXT?
    };

    bool isOnlineResource(URL const& url) {
        return url.scheme != "file";
    }

}

WebResource::WebResource() {
    resourceType = ResourceType::INVALID;
    raw = "";
}

WebResource::WebResource(ResourceType rtype, std::string const& rraw) {
    resourceType = rtype;
    raw = rraw;
}

WebResource getWebResource(URL const& url) {
    if (isOnlineResource(url)) {
        //std::cout << "WebReousrce::getWebResource - isOnline" << std::endl;
        return getOnlineWebResource(url, nullptr);
    }
    //std::cout << "WebReousrce::getWebResource - isOffline" << std::endl;
    return getLocalWebResource(url);
}

WebResource getLocalWebResource(URL const& url) {
    std::string fileExtension = getFilenameExtension(url.path);
    if (fileExtension.length() == 0) {
        return WebResource(ResourceType::INVALID,
                           "Could not find any file extension");
    }

    // Convert file extension to lowercase for table lookup.
    std::transform(fileExtension.begin(),
                   fileExtension.end(),
                   fileExtension.begin(),
                   tolower);

    if (strToRT.find(fileExtension) == strToRT.end()) {
        return WebResource(ResourceType::INVALID,
                           "Local file with extension " + fileExtension + " is not supported. Did you forget a http[s]://?");
    }

    std::ifstream in(url.path, std::ios::in | std::ios::binary);
    if (in) {
        // There exists more efficient ways of doing this, but it works for the
        // time being.
        std::string contents = std::string(std::istreambuf_iterator<char>(in),
                                           std::istreambuf_iterator<char>());

        return WebResource(strToRT[fileExtension], contents);
    }

    return WebResource(ResourceType::INVALID,
                       "Could not open file " + url.path);
}

WebResource getOnlineWebResource(URL const& url, std::unique_ptr<std::map<std::string, std::string>> formData) {
    std::shared_ptr<URL> uri=std::make_shared<URL>(url);

    std::unique_ptr<std::string> ptrPostBody = nullptr;
    if (formData) {
        std::string postBody = "";
        // iterator over formData
        for (auto &entry : *formData) {
            postBody += entry.first + "=" + entry.second + "&";
        }
        // strip last & off
        postBody = postBody.substr(0, postBody.size() - 1);
        std::cout << "postBody is " << postBody << std::endl;
        // add to string
        ptrPostBody = std::make_unique<std::string>(postBody);
    }

    WebResource returnRes;
    std::string redirectLocation = "";

    std::function<void(const HTTPResponse&)> responseCallback = [&](HTTPResponse const& response){
        logDebug() << "getOnlineWebResource responseCallback got " << response.statusCode << std::endl;
        if (response.statusCode == 301 || response.statusCode == 302 || response.statusCode == 303) {
            std::string location;
            if (response.properties.find("Location")==response.properties.end()) {
                if (response.properties.find("location")==response.properties.end()) {
                    logDebug() << "getOnlineWebResource responseCallback - got " << response.statusCode << " without a location" << std::endl;
                    for(auto const &row : response.properties) {
                        logDebug() << "getOnlineWebResource responseCallback - " << row.first << "=" << response.properties.at(row.first) << std::endl;
                    }
                    redirectLocation = "_";
                } else {
                    location = response.properties.at("location");
                }
            } else {
                location = response.properties.at("Location");
            }
            logDebug() << "Redirect To: " << location << std::endl;
            redirectLocation = location;

            // download redirect
            returnRes = getOnlineWebResource(redirectLocation, nullptr);

        } else if (response.statusCode != 200) {
            returnRes.resourceType = ResourceType::INVALID;
            returnRes.raw = "Unsupported status code";
        } else {
            // TODO: Set resourceType based on Content-Type field.
            std::string contentType = "";
            if (response.properties.find("Content-type") != response.properties.end()) {
                contentType = response.properties.at("Content-type");
            }
            if (response.properties.find("Content-Type") != response.properties.end()) {
                contentType = response.properties.at("Content-Type");
            }
            std::cout << "Content-type: " << contentType << std::endl;
            if (contentType == "text/plain") {
                returnRes.resourceType = ResourceType::TXT;
            } else if (contentType == "application/json") {
                returnRes.resourceType = ResourceType::TXT;
            } else {
                returnRes.resourceType = ResourceType::HTML;
            }
            returnRes.raw = std::move(response.body);
        }
    };

    if  (uri->scheme == "https") {
        HTTPSRequest secure(uri);
        if (formData) {
            secure.method = Method::POST;
        }
        //secure.version = Version::HTTP11;
        secure.sendRequest(responseCallback, std::move(ptrPostBody));
    } else {
        HTTPRequest request(uri);
        if (formData) {
            request.method = Method::POST;
        }
        request.sendRequest(responseCallback, std::move(ptrPostBody));
    }
    return returnRes;
}


code:
#ifndef WEBRESOURCE_H
#define WEBRESOURCE_H

#include <string>
#include <map>
#include "URL.h"
#include "networking/HTTPResponse.h"
#include <functional>

enum class ResourceType {
    INVALID,
    HTML,
    CSS,
    JS,
    TXT
};

struct WebResource {
    WebResource();
    WebResource(ResourceType rtype, std::string const& rraw);

    ResourceType resourceType;
    std::string raw;
    static std::function<void(const HTTPResponse&)> responseCallback;
};

// Depending on the resourceName specified, this function will forward the call
// to either getLocalWebResource or getOnlineWebResource.
WebResource getWebResource(URL const& url);

// Loads a resource from the local file storage.
WebResource getLocalWebResource(URL const& url);

// Loads a resource from an internet address.
WebResource getOnlineWebResource(URL const& url, std::unique_ptr<std::map<std::string, std::string>> formData);

#endif


code:
#include "app.h"
#include "scheduler.h"
#include "interfaces/components/TabbedComponent.h"
#include "parsers/markup/ntrml/NTRMLParser.h"
#include "parsers/markup/html/HTMLParser.h"

#include "interfaces/components/DocumentComponent.h"
//#include "interfaces/components/ComponentBuilder.h"
#include "interfaces/components/InputComponent.h"
#include "interfaces/components/ImageComponent.h"
#include "interfaces/components/TabbedComponent.h"
#include "interfaces/components/ButtonComponent.h"

#include <fstream>
#include <glob.h>

// why can't I const this?
std::unique_ptr<Scheduler> scheduler = std::make_unique<Scheduler>();

void doOnClick(std::string type, Component *component, Window *win) {
    if (type=="back") {
        component->onClick=[win]() {
            std::cout << "Browser::doOnClick.navBackButton->onClick - Back" << std::endl;
            TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(win->tabComponent.get());
            if (pTabComponent) {
                if (pTabComponent->selectedTabId) {
                    pTabComponent->selectedTab->get()->history->print();
                    pTabComponent->selectedTab->get()->history->back();
                } else {
                    std::cout << "Browser::doOnClick.navBackButton->onClick - no tab selected" << std::endl;
                }
            } else {
                std::cout << "Browser::doOnClick.navBackButton->onClick - no tabbed component" << std::endl;
            }
        };
    } else if (type=="forward") {
        component->onClick=[win]() {
            //std::cout << "Forward" << std::endl;
            TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(win->tabComponent.get());
            if (pTabComponent) {
                if (pTabComponent->selectedTabId) {
                    pTabComponent->selectedTab->get()->history->forward();
                }
            }
        };
    } else if (type=="refresh") {
        component->onClick=[win]() {
            std::shared_ptr<DocumentComponent> docComponent = win->getActiveDocumentComponent();
            if (docComponent) {
                std::cout << "Refreshing " << docComponent->currentURL << std::endl;
                // now tell it to navigate somewhere
                docComponent->navTo(docComponent->currentURL.toString());
            }
        };
    } else if (type=="go") {
        component->onClick=[win]() {
            // get address bar value and nav away
            std::string value;
            InputComponent *p_addressComponent = dynamic_cast<InputComponent*>(win->addressComponent.get());
            if (!p_addressComponent) {
                std::cout << "Browser:zdoOnClick.navAddressBar->onClick - can't find address bar in windows " << std::endl;
                return;
            }
            //std::cout << "Browser:zdoOnClick.navAddressBar->onClick - got " << value << std::endl;

            TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(win->tabComponent.get());
            if (p_tabComponent) {
                if (!p_tabComponent->tabs.size()) {
                    p_tabComponent->addTab("Loading...");
                    p_tabComponent->selectTab(p_tabComponent->tabs.back());

                    win->renderDirty = true;
                    win->render(); // display loading tab before IO
                }
            }

            std::shared_ptr<DocumentComponent> docComponent = win->getActiveDocumentComponent();
            if (docComponent) {
                //std::cout << "Browser:zdoOnClick.navAddressBar->onClick - Found an active document component" << std::endl;
                // now tell it to navigate somewhere
                docComponent->navTo(value);
            } else {
                std::cout << "Browser:zdoOnClick.navAddressBar->onClick - No active document component" << std::endl;
            }
        };
    } else if (type=="nextTab") {
        component->onClick=[win]() {
            // get tab value and nav away
        };
    } else if (type=="prevTab") {
        component->onClick=[win]() {
            // get tab value and nav away
        };
    }
}

std::shared_ptr<Node> App::loadTheme(std::string filename) {
    std::string ntrml, line;
    std::ifstream myfile(filename);
    if (myfile.is_open()) {
        while(getline(myfile, line)) {
            ntrml += line;
        }
        myfile.close();
    } else {
        std::cout << "Couldnt read " << filename << std::endl;
    }
    NTRMLParser uiParser;
    //std::cout << "Browser read [" << ntrml << "]" << std::endl;
    return uiParser.parse(ntrml);
}

void App::destroyTheme() {
    for(auto win: this->windows) {
        if (win->tabComponent) {
            TabbedComponent *tabbedComponent = dynamic_cast<TabbedComponent *>(win->tabComponent.get());
            for(auto tab: tabbedComponent->tabs) {
                tabbedComponent->removeTab(tab->id);
            }
        }
        win->addressComponent = nullptr;
        win->tabComponent = nullptr;
        for(auto layer: win->ui->layers) {
            layer.reset();
        }
        win->ui->layers.clear();
    }
}

void App::rebuildTheme() {
    for(auto win: this->windows) {

        // convert uiRootNode into a component list
        std::shared_ptr<Component> rootComponent = std::make_shared<Component>();
        rootComponent->name = "rootComponent of browser";
        // we build one global tree that each layer has a child node in
        this->layers.clear(); // nuke any layers we have
        this->createComponentTree(this->uiRootNode, rootComponent, win);
        //Component::printComponentTree(rootComponent, 0);

        // these probably should be fatal
        if (!win->addressComponent) {
            std::cout << "ERROR: no addressComponent" << std::endl;
        }
        if (!win->tabComponent) {
            std::cout << "ERROR: no tabComponent" << std::endl;
        }

        // we want each window to has it's own component tree, so each address bar can have different values
        size_t layerCount = 0;
        for(auto layer: this->layers) {
            // set proper vertices
            layer->resize(win->windowWidth, win->windowHeight);
            // transfer layer
            win->ui->layers.push_back(layer);
            // debug
            /*
            std::cout << "layer: " << layerCount << std::endl;
            Component::printComponentTree(layer, 0);
             */
            layerCount++;
            layer.reset(); // deallocate our local layers
        }
        rootComponent.reset();

        //newWindow->rootComponent = rootComponent;
        //newWindow->ui->rootComponent = rootComponent;
        win->renderDirty = true;

    }
}

void App::transferTheme(std::string filename) {
    // assuming we don't need the NodeTree to get our values
    this->uiRootNode.reset();
    this->uiRootNode = this->loadTheme(filename);

    for(auto win: this->windows) {
        glfwMakeContextCurrent(win->window);

        TabbedComponent *oldTabbedComponent = nullptr;
        if (win->tabComponent) {
            oldTabbedComponent = dynamic_cast<TabbedComponent *>(win->tabComponent.get());
        }
        win->tabComponent = nullptr; // release to release
        win->addressComponent = nullptr; // I think this is ok to release now

        // convert uiRootNode into a component list
        std::shared_ptr<Component> rootComponent = std::make_shared<Component>();
        rootComponent->name = "rootComponent of browser";
        this->layers.clear(); // nuke any layers we had (doesn't nuke window layers)
        // we build one global tree that each layer has a child node in
        this->createComponentTree(this->uiRootNode, rootComponent, win);
        //Component::printComponentTree(rootComponent, 0);

        // these probably should be fatal
        if (!win->addressComponent) {
            std::cout << "ERROR: no addressComponent" << std::endl;
        }
        if (!win->tabComponent) {
            std::cout << "ERROR: no tabComponent" << std::endl;
        }

        // ok now we need to transfer the tabs over
        if (oldTabbedComponent) {
            // should have been set in createComponentTree
            TabbedComponent *newTabbedComponent = dynamic_cast<TabbedComponent *>(win->tabComponent.get());
            if (newTabbedComponent) {
                size_t oldSelectedTabId = oldTabbedComponent->selectedTabId;
                std::shared_ptr<Tab> newSelectedTab = nullptr;
                std::cout << "going to copy " << oldTabbedComponent->tabs.size() << " tab(s) over to new theme" << std::endl;
                for(auto tab: oldTabbedComponent->tabs) {
                    DocumentComponent *oldDocComponent = dynamic_cast<DocumentComponent *>(tab->contents.get());
                    if (oldDocComponent) {
                        //std::cout << "Reading DOM" << std::endl;
                        //printNode(oldDocComponent->domRootNode, 0);

                        std::shared_ptr<Tab> nTab = newTabbedComponent->addTab(tab->titleBox->text);
                        newTabbedComponent->selectTab(nTab);
                        // maybe faster if we can skip the component tree rebuild
                        newTabbedComponent->loadDomIntoTab(oldDocComponent->domRootNode, tab->titleBox->text);
                        //newTabbedComponent->documentComponent.get()
                        DocumentComponent *newDocComponent = dynamic_cast<DocumentComponent *>(newTabbedComponent->tabs.back()->contents.get());
                        if (newDocComponent) {
                            // really slow and we don't need to do this for all tabs
                            // not sure why this is required...
                            //newDocComponent->setDOM(oldDocComponent->domRootNode);
                            //std::cout << "Checking DOM" << std::endl;
                            //printNode(newTabbedComponent->documentComponent->, 0);
                            newDocComponent->render(); // flush out components
                            //newDocComponent->domDirty = true; // is most accurate
                        } else {
                            std::cout << "new tab had no doc" << std::endl;
                        }
                        if (tab->id == oldSelectedTabId) {
                            // translate old selected id into new id
                            newSelectedTab = nTab;
                        }
                    } else {
                        std::cout << "old tab had no doc" << std::endl;
                    }
                    // this breaks everything
                    //oldTabbedComponent->removeTab(tab->id);
                }
                if (newSelectedTab) {
                    newTabbedComponent->selectTab(newSelectedTab);
                    /*
                    DocumentComponent *newDocComponent = dynamic_cast<DocumentComponent *>(newTabbedComponent->documentComponent.get());
                    if (newDocComponent) {
                        // FIXME: can only in render active window/ogl context
                        newDocComponent->render(); // flush out components
                    } else {
                        std::cout << "new selected tab had no doc" << std::endl;
                    }
                    */
                } else {
                    std::cout << "no new selected tab" << std::endl;
                }
                /*
                std::cout << "new tabbed component has " << newTabbedComponent->tabs.size() << " tab(s) over from old theme" << std::endl;
                for(auto tab: newTabbedComponent->tabs) {
                    DocumentComponent *newDocComponent = dynamic_cast<DocumentComponent *>(tab->contents.get());
                    std::cout << "checking tab DOM" << std::endl;
                    printNode(newDocComponent->domRootNode, 0);
                }
                if (newTabbedComponent->documentComponent) {
                    DocumentComponent *newDocComponent = dynamic_cast<DocumentComponent *>(newTabbedComponent->documentComponent.get());
                    std::cout << "checking selected DOM" << std::endl;
                    printNode(newDocComponent->domRootNode, 0);
                }
                */
            } else {
                std::cout << "new theme didnt have tab component" << std::endl;
            }
        } else {
            std::cout << "old theme didnt have tab component" << std::endl;
        }

        // now we're officially done with old layer, nuked them
        for(auto layer: win->ui->layers) {
            layer.reset();
        }
        win->ui->layers.clear();

        // we want each window to has it's own component tree, so each address bar can have different values
        size_t layerCount = 0;
        for(auto layer: this->layers) {
            // set proper vertices
            layer->resize(win->windowWidth, win->windowHeight);
            // transfer layer
            win->ui->layers.push_back(layer);
            // debug
            /*
             std::cout << "layer: " << layerCount << std::endl;
             Component::printComponentTree(layer, 0);
             */
            layerCount++;
            layer.reset(); // deallocate our local layers
        }
        rootComponent.reset();

        //newWindow->rootComponent = rootComponent;
        //newWindow->ui->rootComponent = rootComponent;
        win->renderDirty = true;
    }
}

void App::NextTheme() {
    glob_t glob_result;
    static unsigned int t = 0;
    glob("*.ntrml", GLOB_TILDE, nullptr, &glob_result);
    t++;
    if (t == glob_result.gl_pathc) t = 0;
    /*
    for(unsigned int i = 0; i < glob_result.gl_pathc; ++i){
        if (t == i) std::cout << "*";
        std::cout << glob_result.gl_pathv[i] << std::endl;
    }
    */
    // this works decently, but only destroys first window
    /*
    this->destroyTheme();
    //deleteNode(this->uiRootNode);
    this->uiRootNode.reset();
    this->uiRootNode = this->loadTheme(std::string(glob_result.gl_pathv[t]));
    this->rebuildTheme();
     */
    this->transferTheme(glob_result.gl_pathv[t]);
    // start with removing existing theme
    // and replacing with new version of existing theme
}

// Previously defined components with functionality
// 1+ image (no functionality)
// 1+ tabbed document
// 1+ back/fwd/reload/stop components
// 1+ address bar
void App::createComponentTree(const std::shared_ptr<Node> node, std::shared_ptr<Component> &parentComponent, std::shared_ptr<Window> win) {
    // FIXME: remove these 2 vars
    int winWidth = win->windowWidth;
    int winHeight = win->windowHeight;
    std::string tag;
    if (node == nullptr) {
        std::cout << "ComponentBuilder::build - node is null" << std::endl;
        return;
    }
    TagNode *tagNode = nullptr;
    if (node->nodeType == NodeType::TAG) {
        tagNode = dynamic_cast<TagNode*>(node.get());
        if (tagNode) {
            tag = tagNode->tag;
        }
    } else if (node->nodeType == NodeType::TEXT) {
        tagNode = dynamic_cast<TagNode*>(node->parent.get());
        if (tagNode) {
            tag = tagNode->tag;
        }
    }
    std::shared_ptr<Component> component = nullptr;
    if (!tagNode) {
        // usually the root node...
        //std::cout << "!tagnode" << std::endl;
        component = std::make_shared<Component>();
        for (std::shared_ptr<Node> child : node->children) {
            createComponentTree(child, component, win);
        }
        return;
    }

    //std::cout << "Looking at tag[" << tag << "]" << std::endl;
    if (tag == "layer") {
        // the layering only works for default layout due to the ordering we're doign in window.render
        component = std::make_shared<Component>();
        this->layers.push_back(component);
        //parentComponent = component;
        //std::swap(component, parentComponent);
        //std::cout << "there are now " << this->layers.size() << " layers" << std::endl;
    } else if (tag == "body") {
        if (tagNode->properties.find("bgcolor") != tagNode->properties.end()) {
            std::stringstream ss;
            ss << std::hex << tagNode->properties["bgcolor"];
            ss >> win->clearColor;
            //std::cout << "set clear color " << std::hex << win->clearColor << std::dec << std::endl;

            float r = (static_cast<float>((win->clearColor >> 24) & 0xFF)) / 255;
            float g = (static_cast<float>((win->clearColor >> 16) & 0xFF)) / 255;
            float b = (static_cast<float>((win->clearColor >>  8) & 0xFF)) / 255;
            float a = (static_cast<float>((win->clearColor >>  0) & 0xFF)) / 255;
            glClearColor(r, g, b, a);
        }
    } else if (tag == "img") {
        std::string src = "anime.pnm";
        if (tagNode->properties.find("src") != tagNode->properties.end()) {
            src = tagNode->properties["src"];
        }
        //std::shared_ptr<ImageComponent> img = std::make_unique<ImageComponent>(src, winWidth * imgSetup.left.pct * 0.01 + imgSetup.left.px, winHeight * imgSetup.top.pct * 0.01 + imgSetup.top.px, imgSetup.width.px, imgSetup.height.px, winWidth, winHeight);
        std::shared_ptr<ImageComponent> img = std::make_unique<ImageComponent>(src, 0, 0, 512, 512, winWidth, winHeight);
        img->boundToPage = false; // have to set this before setUpUI
        img->setUpUI(tagNode->properties, win.get());
        // set up interactivity
        if (tagNode->properties.find("onClick") != tagNode->properties.end()) {
            img->isPickable = true;
            //doOnClick(std::string type, Component &component, Window *win)
            doOnClick(tagNode->properties["onClick"], img.get(), win.get());
        }
        img->isPickable = false;
        img->name = "img";
        if (tagNode->properties.find("name") != tagNode->properties.end()) {
            img->name = tagNode->properties["name"];
        }
        component = img;
        //std::cout << "Added img component to ui" << std::endl;
    } else if (tag == "font") {
        // TextComponent(const std::string &rawText, const int rawX, const int rawY, const unsigned int size, const bool bolded, const unsigned int hexColor, const int passedWindowWidth, const int passedWindowHeight);
        std::shared_ptr<TextComponent> text = std::make_unique<TextComponent>("NeTRunner", 0, 0, 12, false, 0x000000FF, winWidth, winHeight);
        text->boundToPage = false; // have to set this before setUpUI
        text->win = win;
        // this may not work for the text shader...
        text->setUpUI(tagNode->properties, win.get());
        text->isPickable = false;
        // set up interactivity
        if (tagNode->properties.find("onClick") != tagNode->properties.end()) {
            text->isPickable = true;
            //doOnClick(std::string type, Component &component, Window *win)
            doOnClick(tagNode->properties["onClick"], text.get(), win.get());
        }
        component = text;
    } else if (tag == "box") {
        unsigned int color = 0x888888FF;
        if (tagNode->properties.find("color") != tagNode->properties.end()) {
            std::stringstream ss;
            ss << std::hex << tagNode->properties["color"];
            ss >> color;
            //std::cout << "read color " << tagNode->properties["color"] << " as " << std::hex << color << std::dec << std::endl;
        }

        // winHeight minus because box coordinates? yes
        //std::cout << "placing box at " << cX << "," << cY << " " << cW << "x" << cH << " color: " << std::hex << color << std::dec << std::endl;
        //std::shared_ptr<BoxComponent> box = std::make_unique<BoxComponent>(cX, winHeight - cH - cY, cW, cH, color, winWidth, winHeight);
        std::shared_ptr<BoxComponent> box = nullptr;
        if (node->children.size()) {
            box = std::make_unique<ButtonComponent>(0, 0, 1, 1, color, winWidth, winHeight);
        } else {
            box = std::make_unique<BoxComponent>(0, 0, 1, 1, color, winWidth, winHeight);
        }
        box->boundToPage = false; // have to set this before setUpUI
        box->setUpUI(tagNode->properties, win.get());
        box->isPickable = false;
        box->name = "box";
        if (tagNode->properties.find("name") != tagNode->properties.end()) {
            box->name = tagNode->properties["name"];
        }
        // set up interactivity
        if (tagNode->properties.find("hover") != tagNode->properties.end()) {
            box->isPickable = true;
            unsigned int hoverColor = 0;
            std::stringstream ss;
            ss << std::hex << tagNode->properties["hover"];
            ss >> hoverColor;
            //std::cout << "setHover Color " << std::hex << hoverColor << std::dec << std::endl;
            box->onMouseover = [box, win, hoverColor]() {
                //std::cout << "box->onMouseover" << std::endl;
                box->changeColor(hoverColor);
                win->renderDirty = true;
            };
            box->onMouseout = [box, win, color]() {
                //std::cout << "box->onMouseout" << std::endl;
                box->changeColor(color);
                win->renderDirty = true;
            };
        }
        if (tagNode->properties.find("onClick") != tagNode->properties.end()) {
            box->isPickable = true;
            //doOnClick(std::string type, Component &component, Window *win)
            doOnClick(tagNode->properties["onClick"], box.get(), win.get());
        }
        component = box;
        // if this button node has children, extract the text before discarding it
        if (node->children.size()) {
            TextNode *textNode = dynamic_cast<TextNode*>(node->children.front().get());
            if (textNode) {
                // so now cast back to a ButtonComponent and set it
                std::cout << "Box text: " << textNode->text << std::endl;
                /*
                ButtonComponent *buttonComponent = dynamic_cast<ButtonComponent*>(component.get());
                if (buttonComponent) {
                    buttonComponent->value = textNode->text;
                    buttonComponent->resizeToTextSize();
                    buttonComponent->updateText();
                }
                */
            }
        }
        // manual quick add before we bail
        component->setParent(parentComponent);
        parentComponent->children.push_back(component);
        // skip my children
        return;
    } else if (tag == "input") {
        //std::shared_ptr<InputComponent> navAddressBar = std::make_unique<InputComponent>(192.0f, winHeight - 48.0f, winWidth - 384.0f, 24.0f, winWidth, winHeight);
        std::shared_ptr<InputComponent> navAddressBar = std::make_unique<InputComponent>(0, 0, 384.0f, 24.0f, winWidth, winHeight);
        navAddressBar->boundToPage = false; // have to set this before setUpUI
        navAddressBar->setUpUI(tagNode->properties, win.get());
        navAddressBar->name = "navAddressBar";

        navAddressBar->onEnter=[win](std::string value) {
            std::cout << "navAddressBar::onEnter got " << value << std::endl;

            TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(win->tabComponent.get());
            if (p_tabComponent) {
                if (!p_tabComponent->tabs.size()) {
                    p_tabComponent->addTab("Loading...");
                    p_tabComponent->selectTab(p_tabComponent->tabs.back());

                    win->renderDirty = true;
                    win->render(); // display loading tab before IO
                }
            }

            std::shared_ptr<DocumentComponent> docComponent = win->getActiveDocumentComponent();
            if (docComponent) {
                std::cout << "Found an active document component" << std::endl;
                // now tell it to navigate somewhere
                docComponent->navTo(value);
            } else {
                std::cout << "No active document component" << std::endl;
            }
        };

        navAddressBar->win = win;
        win->addressComponent = navAddressBar;
        component = navAddressBar;
    } else if (tag == "tabselector") {
        std::shared_ptr<TabbedComponent> tabbedComponent = std::make_shared<TabbedComponent>(0, 0, static_cast<float>(winWidth), static_cast<float>(winHeight - 64), winWidth, winHeight);
        tabbedComponent->win = win;
        win->tabComponent = tabbedComponent;
        tabbedComponent->boundToPage = false; // have to set this before setUpUI
        tabbedComponent->setUpUI(tagNode->properties, win.get());
        tabbedComponent->name = "tabbedComponent";
        /*
        tabbedComponent->y = -64;
        tabbedComponent->uiControl.x = { 0  ,   0 }; // 0
        tabbedComponent->uiControl.y = { 0  , -64 }; // -64px
        tabbedComponent->uiControl.w = { 100,   0 }; // 100%
        tabbedComponent->uiControl.h = { 100, -64 }; // 100% - 64px
        tabbedComponent->boundToPage = false;
        */
        component = tabbedComponent;
        // make the UI focus on it to relay keyboard events
        win->ui->relayKeyboardComponent = tabbedComponent;
    } else {
        std::cout << "Browser::createComponentTree - unknown tag[" << tag << "]" << std::endl;
    }
    if (!component) {
        component = std::make_shared<Component>();
    }
    // add it to our parent
    component->setParent(parentComponent);
    parentComponent->children.push_back(component);
    // , children count: " << parentComponent->children.size()

    // create children elements
    for (std::shared_ptr<Node> child : node->children) {
        createComponentTree(child, component, win);
    }
    component.reset();
}

void App::addWindow() {
    windowCounter++;
    std::shared_ptr<Window> newWindow = std::make_shared<Window>();
    newWindow->id = windowCounter;
    newWindow->windowWidth = 1024;
    newWindow->windowHeight = 640;
    newWindow->init(); // load our UI into it

    // convert uiRootNode into a component list
    std::shared_ptr<Component> p_rootComponent = std::make_shared<Component>();
    p_rootComponent->name = "rootComponent of browser";
    // we build one global tree that each layer has a child node in
    this->layers.clear(); // nuke any layers we have
    this->createComponentTree(this->uiRootNode, p_rootComponent, newWindow);
    //Component::printComponentTree(p_rootComponent, 0);

    // these probably should be fatal
    if (!newWindow->addressComponent) {
        std::cout << "ERROR: no addressComponent" << std::endl;
    }
    if (!newWindow->tabComponent) {
        std::cout << "ERROR: no tabComponent" << std::endl;
    }

    // we want each window to has it's own component tree, so each address bar can have different values
    size_t layerCount = 0;
    for(auto layer: this->layers) {
        // set proper vertices
        layer->resize(newWindow->windowWidth, newWindow->windowHeight);
        // transfer layer
        newWindow->ui->layers.push_back(layer);
        // debug
        /*
        std::cout << "layer: " << layerCount << std::endl;
        Component::printComponentTree(layer, 0);
        */
        layerCount++;
        layer.reset(); // deallocate our local layers
    }
    p_rootComponent.reset();

    //newWindow->rootComponent = rootComponent;
    //newWindow->ui->rootComponent = rootComponent;
    newWindow->renderDirty = true;

    this->windows.push_back(newWindow);
    newWindow.reset();

    if (!activeWindow) {
        //std::cout << "Browser::addWindow - setting active window" << std::endl;
        activeWindow = this->windows.back().get();
    }
}

// FIXME: put quit check in here and clean up windows
void App::render() {
    // guessing during render this iterator can be invalidated
    size_t w = this->windows.size();
    //for(std::vector<std::shared_ptr<Window>>::iterator it = this->windows.begin(); it != this->windows.end(); ++it) {
        //it->get()->render();
    // definitely safer
    for(size_t i = 0; i < w; ++i) {
        glfwMakeContextCurrent(this->windows[i]->window);
        this->windows[i]->render();
    }
}

void App::loop() {
    bool shouldQuit = false;
    while (!shouldQuit) {
        //const std::clock_t begin = clock();
        this->render();
        scheduler->fireTimers(); // render may have taken some time
        double next = scheduler->getNext();
        //if (!next) std::cout << "timer starvation, refiring" << std::endl;
        /*
        while(!next) {
            std::cout << "timer starvation, refiring" << std::endl;
            scheduler->fireTimers(); // render may have taken some time
            next = scheduler->getNext();
        }
         */
        //std::cout << "Browser::loop - next timer at " << next << std::endl;
        if (next == LONG_MAX) {
            glfwWaitEvents(); // block until something changes
        } else {
            glfwWaitEventsTimeout(next / 1000);
        }
        scheduler->fireTimers(); // check before we go into render again
        //glfwWaitEventsTimeout(1.0 / 60.0); // increase the cpu from 0% to 2% on idle
        //const std::clock_t end = clock();
        //std::cout << '\r' << std::fixed << (((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) * 1000) << std::scientific << " ms/f    " << std::flush;
        // FIXME: one close shouldn't close the app
        for(std::vector<std::shared_ptr<Window>>::iterator it = this->windows.begin(); it != this->windows.end(); ++it) {
            if (glfwWindowShouldClose(it->get()->window)) {
                shouldQuit = true;
                break;
            }
        }
    }
}


code:
#ifndef APP_H
#define APP_H

#include "interfaces/graphical/renderers/glfw/Window.h"
#include "interfaces/components/Component.h"
#include "parsers/markup/Node.h"
//#include <GL/glew.h>
//#include <GLFW/glfw3.h>
//#include <memory>
#include <vector>
//#include <algorithm>

void doOnClick(std::string type, Component *component, Window *win);

// this is the actually application that
// contains a list of windows
class App {
public:
    std::shared_ptr<Node> loadTheme(std::string filename);
    void destroyTheme();
    // maybe applyTheme?
    void rebuildTheme();
    void transferTheme(std::string filename);
    void NextTheme();
    void createComponentTree(const std::shared_ptr<Node> node, std::shared_ptr<Component> &parentComponent, std::shared_ptr<Window> win);
    void addWindow();
    void render();
    void loop();

    //
    // properties
    //
    std::shared_ptr<Node> uiRootNode;
    std::vector<std::shared_ptr<Component>> layers;

    // because components take a shared_ptr for win, this has to be shared too
    std::vector<std::shared_ptr<Window>> windows;
    Window *activeWindow = nullptr;
    size_t windowCounter = 0;
};

#endif


code:
#include "browser.h"
#include "interfaces/components/TabbedComponent.h"
#include "parsers/markup/html/HTMLParser.h"

#include "interfaces/components/InputComponent.h"
#include "interfaces/components/ImageComponent.h"
#include "interfaces/components/TabbedComponent.h"
#include "interfaces/components/ButtonComponent.h"

Browser::Browser() {
    /*
    std::string ntrml, line;
    std::ifstream myfile("browser.ntrml");
    if (myfile.is_open()) {
        while(getline(myfile, line)) {
            ntrml += line;
        }
        myfile.close();
    } else {
        std::cout << "Couldnt read browser.ntrml" << std::endl;
    }
    NTRMLParser uiParser;
    //std::cout << "Browser read [" << ntrml << "]" << std::endl;
    */
    //this->uiRootNode = uiParser.parse(ntrml);
    this->uiRootNode = this->loadTheme("browser.ntrml");
    //printNode(this->uiRootNode, 0);
}

void Browser::addWindow() {
    App::addWindow();
}

std::shared_ptr<DocumentComponent> Browser::getActiveDocumentComponent() {
    if (!activeWindow) {
        std::cout << "Browser::getActiveDocumentComponent - No active window" << std::endl;
        return nullptr;
    }
    return this->activeWindow->getActiveDocumentComponent();
}


code:
#ifndef BROWSER_H
#define BROWSER_H

#include "app.h"
//#include "interfaces/graphical/renderers/glfw/Window.h"
//#include "interfaces/components/Component.h"
#include "interfaces/components/DocumentComponent.h"
//#include "interfaces/components/ComponentBuilder.h"
//#include "parsers/markup/Node.h"
//#include <GL/glew.h>
//#include <GLFW/glfw3.h>
//#include <memory>
//#include <vector>
//#include <algorithm>
//#include "networking/HTTPResponse.h"
//#include "URL.h"
//#include "interfaces/graphical/renderers/glfw/Window.h"

// separate window functions from browser functions
class Browser : public App {
private:
public:
    Browser();
    void addWindow();
    std::shared_ptr<DocumentComponent> getActiveDocumentComponent();

    std::shared_ptr<Component> tabComponent = nullptr;
    std::shared_ptr<Component> addressComponent = nullptr;

    //URL currentURL;
};

//bool setWindowContent(URL const& url);
//void handleRequest(const HTTPResponse &response);

#endif


code:
#include "Environment.h"

#include <vector>

#include "Path.h"
#include "../Log.h"

std::string Environment::resourceDir = "";

void Environment::init() {
	if (!resourceDir.empty())
		return; // already initialized O_o

	std::vector<std::string> paths = {
		"res"
	};
#ifndef _WIN32
	// linux or osx
	paths.push_back("/usr/share/netrunner/resources");
#else
	paths.push_back(""); //TODO: place it somewhere for windows
#endif

	for (std::string &path : paths) {
		if (Path::directoryExists(path)) {
			resourceDir = path;
			logInfo() << "Found resource dir at " << path << "!" << std::endl;
			break;
		}
	}
}

std::string Environment::getResourceDir() {
	return resourceDir;
}


code:
#ifndef ENVIRONMENT_H
#define ENVIRONMENT_H

#include <string>

class Environment {
public:
	static void init();

	static std::string getResourceDir();

	static void setResourceDir();
private:
	static std::string resourceDir;
};
#endif


code:
#include "Path.h"

#include <sys/stat.h>

bool Path::directoryExists(const std::string &path) {
	struct stat sb;

	if (stat(path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) {
		return true;
	}
	return false;
}

std::string Path::fromUnixPath(const std::string &path) {
	std::string returnPath(path);
#ifdef _WIN32
	for (int i = 0; i < path.size(); i++)
		if (path.at(i) == '/') returnPath.at(i) = '\\';
#endif
	return returnPath;
}


code:
#ifndef PATH_H
#define PATH_H

#include <string>

class Path {
public:
	static bool directoryExists(const std::string &path);

	static std::string fromUnixPath(const std::string &path);
};

#endif