<commit_before>// Graph Constants
const max_graph_age = 60 * 60 * 24 * 1000; // Seconds (1 day; can probably be longer)
const rukmal_data_graph_url = 'https://raw.githubusercontent.com/rukmal/Resume/gh-pages/personal_data.rdf';

// Store-specific things
const store_mimeType = 'application/rdf+xml';
const store_base_uri = 'http://precis.rukmal.me/'

/**
 * Function to load graph data.
 *
 * This data is loaded either from the existing browser storage, or from GitHub. This
 * decision is made dynamically, first checking if browser storage exists, then if the
 * graph data exists, before finally checking if the graph data is not too old (as set by)
 * `max_graph_age`. If any of these conditions are not satisfied, `downloadGraphData` is
 * called, and the data is downloaded asynchronously from GitHub.
 *
 * Note that the "asynchronous" feature here does little to nothing, as all content
 * is coming from the graph; this is effectively a blocking call, hence the callback to
 * populate page data.
 *
 * @param {Callback} graphHandler Callback function to handle the build graph store.
 */
function loadGraphData(graphHandler) {
    // This loop misses the case of web storage NOT existing, and the variable not existing
    if(typeof(Storage) !== 'undefined') {
        // HTML5 Web storage exists, check for store
        if (typeof(sessionStorage.rukmal_data_graph) === 'undefined') {
            // Browser storage exists, but graph data does not
            // Download graph data
            downloadGraphData(buildGraph, graphHandler)
        } else {
            if ((Number(sessionStorage.rukmal_data_graph_last_updated) -
                new Date().getTime()) > max_graph_age) {
                // Graph data exists, but it is too old
                downloadGraphData(buildGraph, graphHandler);
            } else {
                // Graph data exists, just use it
                buildGraph(sessionStorage.rukmal_data_graph, graphHandler);
            }
        }
    } else {
        downloadGraphData(buildGraph, graphHandler);
    }
}

/**
 * Function to download graph data (from my Resume GitHub Page;
 * [http://github.com/rukmal/Resume]). Passes resulting data to a callback.
 *
 * @param {Callback} dataHandler Function to handle response.
 */
function downloadGraphData(dataHandler, dataHandlerArg) {
    $.get(rukmal_data_graph_url, function (data, status) {
        if (status === 'success') {
            // Check if session storage is supported; store if it is; update flags
            if (typeof(Storage) !== 'undefined') {
                sessionStorage.rukmal_data_graph = data;
                sessionStorage.rukmal_data_graph_last_updated = new Date().getTime();
            }

            // Handle data, pass thru data handler args too
            dataHandler(data, dataHandlerArg);
        }
    });
}

/**
 * Function to build a store and load graph data.
 *
 * @param {String} data String of graph data (RDF/XML).
 * @param {Callback} graphHandler Callback function to handle graph data.
 */
function buildGraph(data, graphHandler) {
    // Creating new graph
    // TODO: (this is PRIME candidate for improvement in the future;
    // this is done on each reload, on every page. There must be a better way.)
    const store = $rdf.graph();
    // Parsing data into store
    $rdf.parse(data, store, store_base_uri, store_mimeType);

    // Calling graph handler function with the graph data
    graphHandler(store);
}

/**
 * Function to get all nodes of a given type from the Precis knowledge graph.
 *
 * @param {Object} store RDFLib.js Store.
 * @param {String} type_name Target type to get.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getOfType(store, type_name, resCallback, doneCallback) {
    // SPARQL query (plain text)
    var sparql_query = `
    PREFIX precis: <http://precis.rukmal.me/ontology#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    SELECT ?name ?date
    WHERE {
        ?name rdf:type precis:${type_name} .
        ?name precis:hasDate ?date .
    }
    `;

    // Converting SPARQL query to rdflibjs query object
    // Note: RDFLib.js docs are absolute fucking garbage, so look at the source code:
    // https://linkeddata.github.io/rdflib.js/doc/sparql-to-query.js.html
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    // `resCallback` is called with each result
    // Third `false` argument is for stupid ass rdflib.js reasons
    // `doneCallback` is called when the query is complete
    // See: https://linkeddata.github.io/rdflib.js/doc/query.js.html
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given node. Also get names of the nested
 * property (if it is also an object), and the externalResource of the nested node.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllProperties(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
    PREFIX precis: <http://precis.rukmal.me/ontology#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    SELECT ?predicate ?obj ?obj_name ?obj_ext_res ?obj_website
    WHERE {
        <${individual_iri}> ?predicate ?obj .
        OPTIONAL { ?obj precis:hasName ?obj_name . }
        OPTIONAL { ?obj precis:externalResource ?obj_ext_res . }
        OPTIONAL { ?obj precis:hasWebsite ?obj_website . }
    }
    `;

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given WorkExperience. Also resolves secondary
 * and tertiary "parentOrganization"s for the "employedAt" organization.
 *
 * Note that the reason for the absolutely ridiculous amount of "OPTIONAL" statements
 * is because rdflib.js is awful and doesn't allow for nested if statements.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllWorkExperience(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
    PREFIX precis: <http://precis.rukmal.me/ontology#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    SELECT DISTINCT ?description_text ?priority ?desc ?predicate ?obj ?obj_website ?obj_name ?parentOrgName ?parentOrgResource ?parentOrgWebsite ?superParentOrgName ?superParentOrgWebsite ?superParentOrgResource
    WHERE {
        <${individual_iri}> ?predicate ?obj .
        OPTIONAL { ?obj precis:hasName ?obj_name . }
        OPTIONAL { ?obj precis:hasWebsite ?obj_website . }
        OPTIONAL {
            <${individual_iri}> precis:hasDescription ?desc .
            ?desc precis:hasPriority ?priority .
            ?desc precis:hasText ?description_text .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasName ?parentOrgName .
            ?parentOrg precis:hasWebsite ?parentOrgWebsite .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasName ?parentOrgName .
            ?parentOrg precis:hasWebsite ?parentOrgWebsite .
            ?parentOrg precis:externalResource ?parentOrgResource .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasParentOrganization ?superParentOrg .
            ?superParentOrg precis:hasName ?superParentOrgName .
            ?superParentOrg precis:hasWebsite ?superParentOrgWebsite .
            ?superParentOrg precis:externalResource ?superParentOrgResource .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasParentOrganization ?superParentOrg .
            ?superParentOrg precis:hasName ?superParentOrgName .
            ?superParentOrg precis:hasWebsite ?superParentOrgWebsite .
        }
    }
    `;

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given Project. Also resolves relatedTo
 * Activities and Skills.
 *
 * The multiple "OPTIONAL" clauses is because rdflib.js is shit.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllProjects(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
        PREFIX precis: <http://precis.rukmal.me/ontology#>
        PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
        SELECT ?description_text ?predicate ?obj ?sk_name ?sk_resource ?ac_name ?award_name
        WHERE {
            <${individual_iri}> ?predicate ?obj .
            OPTIONAL {
                <${individual_iri}> precis:hasDescription ?desc .
                ?desc precis:hasText ?description_text .
            }
            OPTIONAL {
                <${individual_iri}> precis:relatedTo ?relatedItem .
                ?relatedItem rdf:type precis:Skill .
                ?relatedItem precis:hasName ?sk_name .
                ?relatedItem precis:externalResource ?sk_resource .
            }
            OPTIONAL {
                <${individual_iri}> precis:relatedTo ?relatedItem .
                ?relatedItem rdf:type precis:Activity .
                ?relatedItem precis:hasName ?ac_name .
            }
            OPTIONAL {
                <${individual_iri}> precis:relatedTo ?relatedItem .
                ?relatedItem rdf:type precis:Activity .
                ?award precis:affiliatedWith ?relatedItem .
                ?award precis:hasName ?award_name .
            }
        }
    `

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given Talk.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllTalks(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
        PREFIX precis: <http://precis.rukmal.me/ontology#>
        PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
        SELECT ?description_text ?predicate ?obj
        WHERE {
            <${individual_iri}> ?predicate ?obj .
            OPTIONAL {
                <${individual_iri}> precis:hasDescription ?desc .
                ?desc precis:hasText ?description_text .
            }
        }
    `

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given Publication.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllPublications(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
        PREFIX precis: <http://precis.rukmal.me/ontology#>
        PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
        SELECT ?description_text ?predicate ?obj ?priority ?affiliated_name
        WHERE {
            <${individual_iri}> ?predicate ?obj .
            OPTIONAL {
                <${individual_iri}> precis:hasDescription ?desc .
                ?desc precis:hasPriority ?priority .
                ?desc precis:hasText ?description_text .
            }
        }
    `

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}
<commit_msg>Made minor change (significatn implications if fucked up; affects all content loading functions) to make date getting optional<commit_after>// Graph Constants
const max_graph_age = 60 * 60 * 24 * 1000; // Seconds (1 day; can probably be longer)
const rukmal_data_graph_url = 'https://raw.githubusercontent.com/rukmal/Resume/gh-pages/personal_data.rdf';

// Store-specific things
const store_mimeType = 'application/rdf+xml';
const store_base_uri = 'http://precis.rukmal.me/'

/**
 * Function to load graph data.
 *
 * This data is loaded either from the existing browser storage, or from GitHub. This
 * decision is made dynamically, first checking if browser storage exists, then if the
 * graph data exists, before finally checking if the graph data is not too old (as set by)
 * `max_graph_age`. If any of these conditions are not satisfied, `downloadGraphData` is
 * called, and the data is downloaded asynchronously from GitHub.
 *
 * Note that the "asynchronous" feature here does little to nothing, as all content
 * is coming from the graph; this is effectively a blocking call, hence the callback to
 * populate page data.
 *
 * @param {Callback} graphHandler Callback function to handle the build graph store.
 */
function loadGraphData(graphHandler) {
    // This loop misses the case of web storage NOT existing, and the variable not existing
    if(typeof(Storage) !== 'undefined') {
        // HTML5 Web storage exists, check for store
        if (typeof(sessionStorage.rukmal_data_graph) === 'undefined') {
            // Browser storage exists, but graph data does not
            // Download graph data
            downloadGraphData(buildGraph, graphHandler)
        } else {
            if ((Number(sessionStorage.rukmal_data_graph_last_updated) -
                new Date().getTime()) > max_graph_age) {
                // Graph data exists, but it is too old
                downloadGraphData(buildGraph, graphHandler);
            } else {
                // Graph data exists, just use it
                buildGraph(sessionStorage.rukmal_data_graph, graphHandler);
            }
        }
    } else {
        downloadGraphData(buildGraph, graphHandler);
    }
}

/**
 * Function to download graph data (from my Resume GitHub Page;
 * [http://github.com/rukmal/Resume]). Passes resulting data to a callback.
 *
 * @param {Callback} dataHandler Function to handle response.
 */
function downloadGraphData(dataHandler, dataHandlerArg) {
    $.get(rukmal_data_graph_url, function (data, status) {
        if (status === 'success') {
            // Check if session storage is supported; store if it is; update flags
            if (typeof(Storage) !== 'undefined') {
                sessionStorage.rukmal_data_graph = data;
                sessionStorage.rukmal_data_graph_last_updated = new Date().getTime();
            }

            // Handle data, pass thru data handler args too
            dataHandler(data, dataHandlerArg);
        }
    });
}

/**
 * Function to build a store and load graph data.
 *
 * @param {String} data String of graph data (RDF/XML).
 * @param {Callback} graphHandler Callback function to handle graph data.
 */
function buildGraph(data, graphHandler) {
    // Creating new graph
    // TODO: (this is PRIME candidate for improvement in the future;
    // this is done on each reload, on every page. There must be a better way.)
    const store = $rdf.graph();
    // Parsing data into store
    $rdf.parse(data, store, store_base_uri, store_mimeType);

    // Calling graph handler function with the graph data
    graphHandler(store);
}

/**
 * Function to get all nodes of a given type from the Precis knowledge graph.
 *
 * @param {Object} store RDFLib.js Store.
 * @param {String} type_name Target type to get.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getOfType(store, type_name, resCallback, doneCallback) {
    // SPARQL query (plain text)
    var sparql_query = `
    PREFIX precis: <http://precis.rukmal.me/ontology#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    SELECT ?name ?date
    WHERE {
        ?name rdf:type precis:${type_name} .
        OPTIONAL { ?name precis:hasDate ?date . }
    }
    `;

    // Converting SPARQL query to rdflibjs query object
    // Note: RDFLib.js docs are absolute fucking garbage, so look at the source code:
    // https://linkeddata.github.io/rdflib.js/doc/sparql-to-query.js.html
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    // `resCallback` is called with each result
    // Third `false` argument is for stupid ass rdflib.js reasons
    // `doneCallback` is called when the query is complete
    // See: https://linkeddata.github.io/rdflib.js/doc/query.js.html
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given node. Also get names of the nested
 * property (if it is also an object), and the externalResource of the nested node.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllProperties(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
    PREFIX precis: <http://precis.rukmal.me/ontology#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    SELECT ?predicate ?obj ?obj_name ?obj_ext_res ?obj_website
    WHERE {
        <${individual_iri}> ?predicate ?obj .
        OPTIONAL { ?obj precis:hasName ?obj_name . }
        OPTIONAL { ?obj precis:externalResource ?obj_ext_res . }
        OPTIONAL { ?obj precis:hasWebsite ?obj_website . }
    }
    `;

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given WorkExperience. Also resolves secondary
 * and tertiary "parentOrganization"s for the "employedAt" organization.
 *
 * Note that the reason for the absolutely ridiculous amount of "OPTIONAL" statements
 * is because rdflib.js is awful and doesn't allow for nested if statements.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllWorkExperience(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
    PREFIX precis: <http://precis.rukmal.me/ontology#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    SELECT DISTINCT ?description_text ?priority ?desc ?predicate ?obj ?obj_website ?obj_name ?parentOrgName ?parentOrgResource ?parentOrgWebsite ?superParentOrgName ?superParentOrgWebsite ?superParentOrgResource
    WHERE {
        <${individual_iri}> ?predicate ?obj .
        OPTIONAL { ?obj precis:hasName ?obj_name . }
        OPTIONAL { ?obj precis:hasWebsite ?obj_website . }
        OPTIONAL {
            <${individual_iri}> precis:hasDescription ?desc .
            ?desc precis:hasPriority ?priority .
            ?desc precis:hasText ?description_text .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasName ?parentOrgName .
            ?parentOrg precis:hasWebsite ?parentOrgWebsite .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasName ?parentOrgName .
            ?parentOrg precis:hasWebsite ?parentOrgWebsite .
            ?parentOrg precis:externalResource ?parentOrgResource .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasParentOrganization ?superParentOrg .
            ?superParentOrg precis:hasName ?superParentOrgName .
            ?superParentOrg precis:hasWebsite ?superParentOrgWebsite .
            ?superParentOrg precis:externalResource ?superParentOrgResource .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasParentOrganization ?superParentOrg .
            ?superParentOrg precis:hasName ?superParentOrgName .
            ?superParentOrg precis:hasWebsite ?superParentOrgWebsite .
        }
    }
    `;

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given Project. Also resolves relatedTo
 * Activities and Skills.
 *
 * The multiple "OPTIONAL" clauses is because rdflib.js is shit.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllProjects(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
        PREFIX precis: <http://precis.rukmal.me/ontology#>
        PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
        SELECT ?description_text ?predicate ?obj ?sk_name ?sk_resource ?ac_name ?award_name
        WHERE {
            <${individual_iri}> ?predicate ?obj .
            OPTIONAL {
                <${individual_iri}> precis:hasDescription ?desc .
                ?desc precis:hasText ?description_text .
            }
            OPTIONAL {
                <${individual_iri}> precis:relatedTo ?relatedItem .
                ?relatedItem rdf:type precis:Skill .
                ?relatedItem precis:hasName ?sk_name .
                ?relatedItem precis:externalResource ?sk_resource .
            }
            OPTIONAL {
                <${individual_iri}> precis:relatedTo ?relatedItem .
                ?relatedItem rdf:type precis:Activity .
                ?relatedItem precis:hasName ?ac_name .
            }
            OPTIONAL {
                <${individual_iri}> precis:relatedTo ?relatedItem .
                ?relatedItem rdf:type precis:Activity .
                ?award precis:affiliatedWith ?relatedItem .
                ?award precis:hasName ?award_name .
            }
        }
    `

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given Talk.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllTalks(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
        PREFIX precis: <http://precis.rukmal.me/ontology#>
        PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
        SELECT ?description_text ?predicate ?obj
        WHERE {
            <${individual_iri}> ?predicate ?obj .
            OPTIONAL {
                <${individual_iri}> precis:hasDescription ?desc .
                ?desc precis:hasText ?description_text .
            }
        }
    `

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given Publication.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllPublications(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
        PREFIX precis: <http://precis.rukmal.me/ontology#>
        PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
        SELECT ?description_text ?predicate ?obj ?priority ?affiliated_name
        WHERE {
            <${individual_iri}> ?predicate ?obj .
            OPTIONAL {
                <${individual_iri}> precis:hasDescription ?desc .
                ?desc precis:hasPriority ?priority .
                ?desc precis:hasText ?description_text .
            }
        }
    `

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}
<|endoftext|>


<commit_before>/* @flow */

/**
 * To import/require file from project use aliases:
 *   root, src, actions, assets, components, containers, reducers, config
 * To import/require renderer server file use relative paths.
 */

import type {
  Context,
  Request,
  Response,
  ServerPlugin,
  Logger,
} from '../types';

const path = require('path');
const express = require('express');
const compression = require('compression');
const middleware = require('./middleware');
const onFinished = require('on-finished');
const entries = require('project-entries').default;
// $FlowIgnore
const entriesConfig = require('project-entries-config');
// $FlowIgnore
const EntryWrapper = require('entry-wrapper').default;
// $FlowIgnore
const assets = require('webpack-chunks');
// $FlowIgnore
const projectHooks = require('gluestick-hooks').default;
const BodyWrapper = require('./components/Body').default;
// $FlowIgnore
const reduxMiddlewares = require('redux-middlewares').default;
// $FlowIgnore
const entriesPlugins = require('project-entries').plugins;
const hooksHelper = require('./helpers/hooks');
const prepareServerPlugins = require('../plugins/prepareServerPlugins');
const createPluginUtils = require('../plugins/utils');

// $FlowIgnore Assets should be bundled into render to serve them in production.
require.context('build-assets');

module.exports = ({ config, logger }: Context) => {
  const pluginUtils = createPluginUtils(logger);
  const serverPlugins: ServerPlugin[] = prepareServerPlugins(logger, entriesPlugins);

  // Use custom logger from plugins or default logger.
  const customLogger: ?Logger = pluginUtils.getCustomLogger(serverPlugins);

  // Merge hooks from project and plugins' hooks.
  const hooks = hooksHelper.merge(projectHooks, serverPlugins);

  // Developers can add an optional hook that
  // includes script with initialization stuff.
  if (hooks.preInitServer && typeof hooks.preInitServer === 'function') {
    hooks.preInitServer();
  }

  // Get runtime plugins that will be passed to EntryWrapper.
  const runtimePlugins: Function[] = entriesPlugins
    .filter((plugin: Object) => plugin.type === 'runtime')
    .map((plugin: Object) => plugin.ref);

  const app: Object = express();
  app.use(compression());
  app.use('/assets', express.static(
    path.join(process.cwd(), config.GSConfig.buildAssetsPath),
  ));

  if (process.env.NODE_ENV !== 'production') {
    app.get('/gluestick-proxy-poll', (req: Request, res: Response) => {
      // allow requests from our client side loading page
      res.header('Access-Control-Allow-Origin', '*');
      res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
      res.status(200).json({ up: true });
    });
  }

  app.use((req: Request, res: Response) => {
    if (customLogger) {
      customLogger.info({ req });
      onFinished(res, (err, response) => {
        if (err) {
          customLogger.error(err);
        } else {
          customLogger.info({ res: response });
        }
      });
    }
    middleware(
      { config, logger },
      req, res,
      { entries, entriesConfig, entriesPlugins: runtimePlugins },
      { EntryWrapper, BodyWrapper },
      assets,
      {
        reduxMiddlewares,
        envVariables: [],
        httpClient: {},
        entryWrapperConfig: {},
      },
      { hooks, hooksHelper: hooksHelper.call },
      serverPlugins,
    );
  });

  const server: Object = app.listen(config.GSConfig.ports.server);

  // Call express App Hook which accept app as param.
  hooksHelper.call(hooks.postServerRun, app);

  logger.success(`Renderer listening on port ${config.GSConfig.ports.server}.`);
  process.on('exit', () => {
    server.close();
  });
  process.on('SIGINT', () => {
    server.close();
  });
};
<commit_msg>fix lint<commit_after>/* @flow */

/**
 * To import/require file from project use aliases:
 *   root, src, actions, assets, components, containers, reducers, config
 * To import/require renderer server file use relative paths.
 */

import type {
  Context,
  Request,
  Response,
  ServerPlugin,
  Logger,
} from '../types';

const path = require('path');
const express = require('express');
const compression = require('compression');
const middleware = require('./middleware');
const onFinished = require('on-finished');
const entries = require('project-entries').default;
// $FlowIgnore
const entriesConfig = require('project-entries-config');
// $FlowIgnore
const EntryWrapper = require('entry-wrapper').default;
// $FlowIgnore
const assets = require('webpack-chunks');
// $FlowIgnore
const projectHooks = require('gluestick-hooks').default;
const BodyWrapper = require('./components/Body').default;
// $FlowIgnore
const reduxMiddlewares = require('redux-middlewares').default;
// $FlowIgnore
const entriesPlugins = require('project-entries').plugins;
const hooksHelper = require('./helpers/hooks');
const prepareServerPlugins = require('../plugins/prepareServerPlugins');
const createPluginUtils = require('../plugins/utils');

// $FlowIgnore Assets should be bundled into render to serve them in production.
require.context('build-assets');

module.exports = ({ config, logger }: Context) => {
  const pluginUtils = createPluginUtils(logger);
  const serverPlugins: ServerPlugin[] = prepareServerPlugins(logger, entriesPlugins);

  // Use custom logger from plugins or default logger.
  const customLogger: ?Logger = pluginUtils.getCustomLogger(serverPlugins);

  // Merge hooks from project and plugins' hooks.
  const hooks = hooksHelper.merge(projectHooks, serverPlugins);

  // Developers can add an optional hook that
  // includes script with initialization stuff.
  if (hooks.preInitServer && typeof hooks.preInitServer === 'function') {
    hooks.preInitServer();
  }

  // Get runtime plugins that will be passed to EntryWrapper.
  const runtimePlugins: Function[] = entriesPlugins
    .filter((plugin: Object) => plugin.type === 'runtime')
    .map((plugin: Object) => plugin.ref);

  const app: Object = express();
  app.use(compression());
  app.use('/assets', express.static(
    path.join(process.cwd(), config.GSConfig.buildAssetsPath),
  ));

  if (process.env.NODE_ENV !== 'production') {
    app.get('/gluestick-proxy-poll', (req: Request, res: Response) => {
      // allow requests from our client side loading page
      res.header('Access-Control-Allow-Origin', '*');
      res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
      res.status(200).json({ up: true });
    });
  }

  app.use((req: Request, res: Response) => {
    if (customLogger) {
      customLogger.info({ req });
      onFinished(res, (err, response) => {
        if (err) {
          customLogger.error(err);
        } else {
          customLogger.info({ res: response });
        }
      });
    }
    middleware(
      { config, logger },
      req, res,
      { entries, entriesConfig, entriesPlugins: runtimePlugins },
      { EntryWrapper, BodyWrapper },
      assets,
      {
        reduxMiddlewares,
        envVariables: [],
        httpClient: {},
        entryWrapperConfig: {},
      },
      { hooks, hooksHelper: hooksHelper.call },
      serverPlugins,
    );
  });

  const server: Object = app.listen(config.GSConfig.ports.server);

  // Call express App Hook which accept app as param.
  hooksHelper.call(hooks.postServerRun, app);

  logger.success(`Renderer listening on port ${config.GSConfig.ports.server}.`);
  process.on('exit', () => {
    server.close();
  });
  process.on('SIGINT', () => {
    server.close();
  });
};
<|endoftext|>


<commit_before>var CustomCompiler;
( function () {

    var osgShader = window.OSG.osgShader;
    var osg = window.OSG.osg;
    var factory = osgShader.nodeFactory;

    // this compiler use basic lighting and add a node to demonstrate how to
    // customize the shader compiler
    CustomCompiler = function () {
        osgShader.Compiler.apply( this, arguments );
    };

    CustomCompiler.prototype = osg.objectInherit( osgShader.Compiler.prototype, {

        // check if there is a attribute ramp
        getRamp: function () {

            for ( var i = 0; i < this._attributes.length; i++ ) {
                if ( this._attributes[ i ].getType() === 'Ramp' )
                    return this._attributes[ i ];
            }
            return undefined;

        },

        // check if there is a attribute negatif
        getNegatif: function () {

            for ( var i = 0; i < this._attributes.length; i++ ) {
                if ( this._attributes[ i ].getType() === 'Negatif' )
                    return this._attributes[ i ];
            }
            return undefined;

        },

        // this is the main code that instanciate and link nodes together
        // it's a simplified version of the curent osgjs compiler
        // it could also be simpler
        createFragmentShaderGraph: function () {

            // no material then return a default shader
            // you could do whatever you want here
            // if you want to return a debug color
            // just to be sure that you always have
            // valid material in your scene, in our case we suppose
            // it exists in the scene
            // if ( !this._material )
            //     return this.createDefaultFragmentShaderGraph();

            var materialUniforms = this.getOrCreateStateAttributeUniforms( this._material );

            // that's the final result of the shader graph
            var fragColor = factory.getNode( 'FragColor' );

            // diffuse color
            // use texture if we have some, check code of Compiler
            // to see the default behaviour
            var diffuseColor = this.getDiffuseColorFromTextures();

            // no texture then we use the material diffuse value
            if ( diffuseColor === undefined ) {

                diffuseColor = materialUniforms.diffuse;

            } else {

                factory.getNode( 'InlineCode' )
                    .code( '%color.rgb *= %diffuse.rgb;' )
                    .inputs( {
                        diffuse: materialUniforms.diffuse
                    } )
                    .outputs( {
                        'color': diffuseColor
                    } );

            }

            if ( this._lights.length > 0 ) {

                // creates lights nodes
                var lightedOutput = this.createLighting();

                // ======================================================
                // my custom attribute ramp
                // it's here I connect ouput of light result with my ramp
                // ======================================================
                var rampAttribute = this.getRamp();
                var rampResult = this.getOrCreateVariable( 'vec4' );
                if ( rampAttribute && rampAttribute.getAttributeEnable() ) {

                    factory.getNode( 'Ramp' )
                        .inputs( lightedOutput )
                        .outputs( rampResult );

                } else {
                    rampResult = lightedOutput;
                }
                // ======================================================

                // ======================================================
                // my custom attribute negatif
                // it's here I connect ouput of light result with my ramp
                // ======================================================
                var negatifResult = this.getOrCreateVariable( 'vec4' );
                var negatifAttribute = this.getNegatif();
                if ( negatifAttribute ) {

                    factory.getNode( 'Negatif' )
                        .inputs( {
                            input: rampResult,
                            enable: this.getOrCreateUniform( negatifAttribute.getOrCreateUniforms().enable )
                        } )
                        .outputs( negatifResult );

                } else {
                    negatifResult = rampResult;
                }
                // ======================================================

                // get final color
                // use the rampResult from previous node
                factory.getNode( 'Add' ).inputs( materialUniforms.emission, negatifResult ).outputs( fragColor );

            } else {

                // no lights use a default behaviour
                factory.getNode( 'InlineCode' )
                    .code( '%color.rgb = %diffuse.rgb;' )
                    .inputs( {
                        diffuse: diffuseColor
                    } )
                    .outputs( {
                        'color': fragColor
                    } );

            }

            return fragColor;
        }

    } );

} )();
<commit_msg>Simplifies code using getAttributeType<commit_after>var CustomCompiler;
( function () {

    var osgShader = window.OSG.osgShader;
    var osg = window.OSG.osg;
    var factory = osgShader.nodeFactory;

    // this compiler use basic lighting and add a node to demonstrate how to
    // customize the shader compiler
    CustomCompiler = function () {
        osgShader.Compiler.apply( this, arguments );
    };

    CustomCompiler.prototype = osg.objectInherit( osgShader.Compiler.prototype, {

        // this is the main code that instanciate and link nodes together
        // it's a simplified version of the curent osgjs compiler
        // it could also be simpler
        createFragmentShaderGraph: function () {

            // no material then return a default shader
            // you could do whatever you want here
            // if you want to return a debug color
            // just to be sure that you always have
            // valid material in your scene, in our case we suppose
            // it exists in the scene
            // if ( !this._material )
            //     return this.createDefaultFragmentShaderGraph();

            var materialUniforms = this.getOrCreateStateAttributeUniforms( this._material );

            // that's the final result of the shader graph
            var fragColor = factory.getNode( 'FragColor' );

            // diffuse color
            // use texture if we have some, check code of Compiler
            // to see the default behaviour
            var diffuseColor = this.getDiffuseColorFromTextures();

            // no texture then we use the material diffuse value
            if ( diffuseColor === undefined ) {

                diffuseColor = materialUniforms.diffuse;

            } else {

                factory.getNode( 'InlineCode' )
                    .code( '%color.rgb *= %diffuse.rgb;' )
                    .inputs( {
                        diffuse: materialUniforms.diffuse
                    } )
                    .outputs( {
                        'color': diffuseColor
                    } );

            }

            if ( this._lights.length > 0 ) {

                // creates lights nodes
                var lightedOutput = this.createLighting();

                // ======================================================
                // my custom attribute ramp
                // it's here I connect ouput of light result with my ramp
                // ======================================================
                var rampResult = this.getOrCreateVariable( 'vec4' );
                var rampAttribute = this.getAttributeType( 'Ramp' );
                if ( rampAttribute && rampAttribute.getAttributeEnable() ) {

                    factory.getNode( 'Ramp' )
                        .inputs( lightedOutput )
                        .outputs( rampResult );

                } else {
                    rampResult = lightedOutput;
                }
                // ======================================================

                // ======================================================
                // my custom attribute negatif
                // it's here I connect ouput of light result with my ramp
                // ======================================================
                var negatifResult = this.getOrCreateVariable( 'vec4' );
                var negatifAttribute = this.getAttributeType( 'Negatif' );
                if ( negatifAttribute ) {

                    factory.getNode( 'Negatif' )
                        .inputs( {
                            input: rampResult,
                            enable: this.getOrCreateUniform( negatifAttribute.getOrCreateUniforms().enable )
                        } )
                        .outputs( negatifResult );

                } else {
                    negatifResult = rampResult;
                }
                // ======================================================

                // get final color
                // use the rampResult from previous node
                factory.getNode( 'Add' ).inputs( materialUniforms.emission, negatifResult ).outputs( fragColor );

            } else {

                // no lights use a default behaviour
                factory.getNode( 'InlineCode' )
                    .code( '%color.rgb = %diffuse.rgb;' )
                    .inputs( {
                        diffuse: diffuseColor
                    } )
                    .outputs( {
                        'color': fragColor
                    } );

            }

            return fragColor;
        }

    } );

} )();
<|endoftext|>


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

    var players = document.getElementsByClassName('youtube-loader');

    if (players.length > 0) {
        var style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML =
            '.youtube-loader {' +
                'background-color:#000;' +
                'max-width:100%;' +
                'overflow:hidden;' +
                'position:relative;' +
                'cursor:hand;' +
                'cursor:pointer;' +
            '}' +
            '.youtube-loader .thumb {' +
                'bottom:0;' +
                'display:block;' +
                'left:0;' +
                'margin:auto;' +
                'max-width:100%;' +
                'position:absolute;' +
                'right:0;' +
                'top:0;' +
                'width:100%;' +
                'height:auto;' +
            '}' +
            '.youtube-loader .play {' +
                'transform:scale(0.858333333333333);' +
                'filter:alpha(opacity=80);' +
                'opacity:.9;' +
                'height:60px;' +
                'left:50%;' +
                'margin-left:-38px;' +
                'margin-top:-38px;' +
                'position:absolute;' +
                'top:50%;' +
                'width:85px;' +
                'background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFUAAAA8CAYAAAAXDvbIAAAEmklEQVR4Xu2cv0/bQBTHa0BC6pAiuiAlgFm6tYUOnSo1/AVNNjaoWEBCKl1AQkiFv4BEsDdsbKUSsIDUIHVOwgIjCSQVEyV0AgHp9zk5K7YT7GAn9tl3UiSM757vPvfu3buf0rMWQzgcHpUk6QVL1tXVNYq/+xqIkRFPtig+ajGeWbQcIlyZRapUKnnEoZ8+XD08PJAMJSBeuVQqqc9mctl7ySzi0NDQJODEEI/gWYVkJpa391QBOUDePjs72zTLfFOogPkFMOcDDLIZuyvATQDuarMIBqiyLPehCawB6JRZjQT8fRqM4vl83mBuNFAJKGrhV62pB5yZpeLnAHZcD1YDdXh4+AdEkf0UwTqBdKFQGK+PrkKNRCLR7u5u0lIRWiQAc/n5/Pw8Zej9oaUENNqiPBG9SiAPbR3RQB0YGJB7e3tPBaGnE7i7uxtjPq3S/OE+zcPgrj1dpEiJDj4JN4tc0GcM6jagfhJobBHIwQSMqVBhT//iodFQ09ZXgpYYUBUllYQ9da7q7+/vx4vFYloSrpRzUOFaxeFabUuDg4MxzDSR0y+CTQLorFbRWa1I6PlX0El9sylPJAcBAbUNagCoP6GpMdLUFDR1sg3fsCwyFApdI4QsJ/BuxEN4AFEJ7lQaefzoZj4zmczRxcXFv9nZ2RFkKuxmXmx+21tQ+/v731KBjo+Pf09MTLzhVHOVAYBnNJVBJbC0NrS7u5tdWlp6xxtcGgAQVJpIkW2qva3k1PzroTJhPMJlUCu2iDiQuBlUPdy5ubmoA59rqwhuoDIKmF4rJhKJ/MbGxoe2krEhnDuoPMDlFmo93OXl5T9bW1vvbSiXo0lvbm5GqKPyvE01K/Xl5WVuYWFBOjg4UNwyNwPNVPkCKoPoBbi+g1oPd3p6+nk2m33Vaa31LVQGcmdn5xBuWEeH4L6FCterlEwmT9fX1zvuevkOqpswWevwDVQMZ6/39vYymCsYK5fL6t7ZTttT+h73UL0Es853VmapuPNTvQiTQeVyRHVyckLzra/dbubNTAtXUAnmzMyMjExH3LCVVr/JoNJOYFeN+2NTf7zA1Df/NP7RUQdZX+uNoGLIeRSPx196XTP1ZWGa6imoBHNxcbGyv79Pp2F4C2BakD2zRkX0OIbJKr+6mop1f9e3UcKtK/LWzJs0IRWq2PbjnJERUJ1jWZVUv5dKbE13iK4KVexPdYhoVVO/YoNaQqJT0T09PVnnRAdXkrqTmhB4YVLFD1VBK6nYaJdXNv57YeefD6Aqjj+VQ5yjcqg2DeeoxAkV+2SZPVU1VZgA21DVpq+BilMqUzil8t22+AAKYP4pK7r+vH8aL1ydBuSwTjRaqtFUeqjZVrrdxtVJa47AlrEsHtXfCGS4Q6U2GEihYK5v9vI43CMAnWp0xVLD235ql9PQnAAdtRZaq6td2NBN9D/zjS6lMTR/vWYQXLgKMbqXCr9owAEXADN1e3ubolHTY63I9LIvfWKagAHgPvzqlzv0z/pkXun8CsiYAQhg0eKn5qY0/I8u91KuR6KT0a2YopahtiLcibhUiY3kUIGfcmWcE3kyk/EfxreyUB6zPb0AAAAASUVORK5CYII=") no-repeat' +
            '}' +
            '.youtube-loader img:hover + .play,' +
            '.youtube-loader .play:hover {' +
                'background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFUAAAA8CAYAAAAXDvbIAAAE+0lEQVR4Xu2czW8bRRTAZ7whREg1ViuIpdbGuXCjxMThhIRz5oDDpSfU8CF64IDLl1pxoP0DoCn8AbgHJG4NokXiQ2oq9YZbO4cWKkV0bad4A6K1XYlC8O7jzVq78q7t7NpePLveGSlSbM+8mfntmzdvZmceJUOm0hOHF0GijxvFNIgsUkpidjFAIIXfsT8XiWZdZHLMAkDKlELDMSMhMiVU7mkzkEaEamXje6pCM/3HXfOzC7l6FuqUsRhPHicAOULpontITlID97uMDMrIYCOjVC84tX4g1OJ88h1Eng8xyP7sUJtxZK4vKdWzg+D2QC3FYjF17sA5VOI1pycS7t9hU/r7wWq60egxNxaoDGj70egVfBJsqIvkQIDZ8Jl/Wit2sBaoxfnERbQbOUFzGAKwmVFqK90lTKg/xY9kKYlcGUacyNshQAm8tqTUCqbXYPxTjCcQqDeuTQhhy+gVLFigluLxlEpm74QQhmddllQ1bfi0+vC/Pp/MAyU444s0MgHQzmd2d5gL2nH+cYLawAnq5ZEFioK4NiDl5d1qugtq8j7i7VlqClbDEUC7qispFfZ0OHD75QairSwrO5tUuFLeQSUarGZ+r23Q4pOJHInQix6KDq0oIOTsslI9g5qaPIOG4OPQkvCw4wKqhzBNUQBfZ3ZrOYorqQLOV8f/jzrcypSi0ZbaakXd5vdrPtyYv7qs1LI4/BObuAv+Is+GHr1Z2mrXlQfbb55Y2JOrh3m2ZZy6fQf1kYMHn2Udenjz1rXbrxw7GkTNNRYAvtFUAyoDCwDNxqXLpcr7p58LGly2AECbmmQbKalx1H7csmz4d0M15AURrgEVPQG+aRBUO9xf33o7y7elzrUHBqoJt93eqX+yLtfXP3/BuXt8cgQOahDgBhZqN9zKqY9++/PLr57no5e9tUpkb4FNVL63qU7A/r13ryyf/IC2vv9Rd8t4JrZTNRVQDYh+gDt1ULvhbr/6+mN/3Sg9PWmtnVqoBsj731y6im7YRJfgUwsV2u279U/P36mf+2zirtfUQeUJ0/RIpmWiwuVsq3n52xvye6fTaqtpnp2dtD1l9QVeU/0E03iA7FBFIF0qP8I0oAZyRfXw1s/Xbq8ee4b3MB9kWgIFlcHcfuNEak+uHOFhK93W2YE6n2zg6RSuxn2/rb+gwLQMf7+8o7JvUuOSc+uXl3KH/K6Zdg3WNdVvUBnMyrsfQvO7H4J4RL6CUFO+gcqeeIBh6gprvk31wzHK2dRTO0Eb5v0mrq5X1OLYj9uZ3SmfgOpEaITfzbNU4mj6CPQGFOk6oCau+niFlQI5ubRbXafsVrQqSSWvBIdZjnmSmkHww8u/aXgY7E1qWlHw2jsmPywApgCq7vizfoh7VF49Tfs9KnFDZXyyhj01NVWYgLGhmkPfAvV6PLEGhH4xtvgQCjD8U6Prlvv+YsIaSSMsWmrRVPZBt60wiwFY+G5aj9Q1HoWANCVNzdojAvXGUGGLgUikgBeAuR/24sHJdZ0AW5KmrfULsdQ32o8eS2UumsdlV15obT/McAGD0uT7BaXpGf724gyuNncgh7cuMK4KRq0It1mo4IRUmCF7BbZq2k+jHYN92QvrF4Q1GoOIHvxLTxS0GHSCgfVNvO9pdTUKwUAPEByRDWy/JVIa1aAMkU40NnYz2rVZYDyGycwjL3uI/eqdUaExSsi4SfThP5yCtWKJCDx6AAAAAElFTkSuQmCC") no-repeat' +
            '}';
        document.body.appendChild(style);
    }

    var onClick = function () {
        var iframe = document.createElement('iframe');
        iframe.className = this.className.replace('youtube-loader', 'youtube-loader-embedded', 1);
        var src = '//www.youtube.com/embed/' + this.id + '?autoplay=1&autohide=1&border=0&wmode=opaque&enablejsapi=1&hd=1&showinfo=0';
        var start = this.getAttribute('start')
        if (start) {
            src += '&start=' + start
        }
        iframe.setAttribute('src', src);
        iframe.setAttribute('allowfullscreen', '');
        iframe.setAttribute('frameborder', '0');
        iframe.setAttribute('type', 'text/html');
        this.parentNode.replaceChild(iframe, this);
    };

    for (var i = 0; i < players.length; i++) {
        var player = players[i];

        var img = document.createElement('img');
        img.setAttribute('src', '//i.ytimg.com/vi/' + player.id + '/maxresdefault.jpg');
        img.setAttribute('class', 'thumb');

        var div = document.createElement('div');
        div.setAttribute('class', 'play');

        player.appendChild(img);
        player.appendChild(div);

        player.onclick = onClick;
    }
})();
<commit_msg>string.prototype.replace does not take a parameter limiting action.<commit_after>(function () {
    'use strict';

    var players = document.getElementsByClassName('youtube-loader');

    if (players.length > 0) {
        var style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML =
            '.youtube-loader {' +
                'background-color:#000;' +
                'max-width:100%;' +
                'overflow:hidden;' +
                'position:relative;' +
                'cursor:hand;' +
                'cursor:pointer;' +
            '}' +
            '.youtube-loader .thumb {' +
                'bottom:0;' +
                'display:block;' +
                'left:0;' +
                'margin:auto;' +
                'max-width:100%;' +
                'position:absolute;' +
                'right:0;' +
                'top:0;' +
                'width:100%;' +
                'height:auto;' +
            '}' +
            '.youtube-loader .play {' +
                'transform:scale(0.858333333333333);' +
                'filter:alpha(opacity=80);' +
                'opacity:.9;' +
                'height:60px;' +
                'left:50%;' +
                'margin-left:-38px;' +
                'margin-top:-38px;' +
                'position:absolute;' +
                'top:50%;' +
                'width:85px;' +
                'background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFUAAAA8CAYAAAAXDvbIAAAEmklEQVR4Xu2cv0/bQBTHa0BC6pAiuiAlgFm6tYUOnSo1/AVNNjaoWEBCKl1AQkiFv4BEsDdsbKUSsIDUIHVOwgIjCSQVEyV0AgHp9zk5K7YT7GAn9tl3UiSM757vPvfu3buf0rMWQzgcHpUk6QVL1tXVNYq/+xqIkRFPtig+ajGeWbQcIlyZRapUKnnEoZ8+XD08PJAMJSBeuVQqqc9mctl7ySzi0NDQJODEEI/gWYVkJpa391QBOUDePjs72zTLfFOogPkFMOcDDLIZuyvATQDuarMIBqiyLPehCawB6JRZjQT8fRqM4vl83mBuNFAJKGrhV62pB5yZpeLnAHZcD1YDdXh4+AdEkf0UwTqBdKFQGK+PrkKNRCLR7u5u0lIRWiQAc/n5/Pw8Zej9oaUENNqiPBG9SiAPbR3RQB0YGJB7e3tPBaGnE7i7uxtjPq3S/OE+zcPgrj1dpEiJDj4JN4tc0GcM6jagfhJobBHIwQSMqVBhT//iodFQ09ZXgpYYUBUllYQ9da7q7+/vx4vFYloSrpRzUOFaxeFabUuDg4MxzDSR0y+CTQLorFbRWa1I6PlX0El9sylPJAcBAbUNagCoP6GpMdLUFDR1sg3fsCwyFApdI4QsJ/BuxEN4AFEJ7lQaefzoZj4zmczRxcXFv9nZ2RFkKuxmXmx+21tQ+/v731KBjo+Pf09MTLzhVHOVAYBnNJVBJbC0NrS7u5tdWlp6xxtcGgAQVJpIkW2qva3k1PzroTJhPMJlUCu2iDiQuBlUPdy5ubmoA59rqwhuoDIKmF4rJhKJ/MbGxoe2krEhnDuoPMDlFmo93OXl5T9bW1vvbSiXo0lvbm5GqKPyvE01K/Xl5WVuYWFBOjg4UNwyNwPNVPkCKoPoBbi+g1oPd3p6+nk2m33Vaa31LVQGcmdn5xBuWEeH4L6FCterlEwmT9fX1zvuevkOqpswWevwDVQMZ6/39vYymCsYK5fL6t7ZTttT+h73UL0Es853VmapuPNTvQiTQeVyRHVyckLzra/dbubNTAtXUAnmzMyMjExH3LCVVr/JoNJOYFeN+2NTf7zA1Df/NP7RUQdZX+uNoGLIeRSPx196XTP1ZWGa6imoBHNxcbGyv79Pp2F4C2BakD2zRkX0OIbJKr+6mop1f9e3UcKtK/LWzJs0IRWq2PbjnJERUJ1jWZVUv5dKbE13iK4KVexPdYhoVVO/YoNaQqJT0T09PVnnRAdXkrqTmhB4YVLFD1VBK6nYaJdXNv57YeefD6Aqjj+VQ5yjcqg2DeeoxAkV+2SZPVU1VZgA21DVpq+BilMqUzil8t22+AAKYP4pK7r+vH8aL1ydBuSwTjRaqtFUeqjZVrrdxtVJa47AlrEsHtXfCGS4Q6U2GEihYK5v9vI43CMAnWp0xVLD235ql9PQnAAdtRZaq6td2NBN9D/zjS6lMTR/vWYQXLgKMbqXCr9owAEXADN1e3ubolHTY63I9LIvfWKagAHgPvzqlzv0z/pkXun8CsiYAQhg0eKn5qY0/I8u91KuR6KT0a2YopahtiLcibhUiY3kUIGfcmWcE3kyk/EfxreyUB6zPb0AAAAASUVORK5CYII=") no-repeat' +
            '}' +
            '.youtube-loader img:hover + .play,' +
            '.youtube-loader .play:hover {' +
                'background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFUAAAA8CAYAAAAXDvbIAAAE+0lEQVR4Xu2czW8bRRTAZ7whREg1ViuIpdbGuXCjxMThhIRz5oDDpSfU8CF64IDLl1pxoP0DoCn8AbgHJG4NokXiQ2oq9YZbO4cWKkV0bad4A6K1XYlC8O7jzVq78q7t7NpePLveGSlSbM+8mfntmzdvZmceJUOm0hOHF0GijxvFNIgsUkpidjFAIIXfsT8XiWZdZHLMAkDKlELDMSMhMiVU7mkzkEaEamXje6pCM/3HXfOzC7l6FuqUsRhPHicAOULpontITlID97uMDMrIYCOjVC84tX4g1OJ88h1Eng8xyP7sUJtxZK4vKdWzg+D2QC3FYjF17sA5VOI1pycS7t9hU/r7wWq60egxNxaoDGj70egVfBJsqIvkQIDZ8Jl/Wit2sBaoxfnERbQbOUFzGAKwmVFqK90lTKg/xY9kKYlcGUacyNshQAm8tqTUCqbXYPxTjCcQqDeuTQhhy+gVLFigluLxlEpm74QQhmddllQ1bfi0+vC/Pp/MAyU444s0MgHQzmd2d5gL2nH+cYLawAnq5ZEFioK4NiDl5d1qugtq8j7i7VlqClbDEUC7qispFfZ0OHD75QairSwrO5tUuFLeQSUarGZ+r23Q4pOJHInQix6KDq0oIOTsslI9g5qaPIOG4OPQkvCw4wKqhzBNUQBfZ3ZrOYorqQLOV8f/jzrcypSi0ZbaakXd5vdrPtyYv7qs1LI4/BObuAv+Is+GHr1Z2mrXlQfbb55Y2JOrh3m2ZZy6fQf1kYMHn2Udenjz1rXbrxw7GkTNNRYAvtFUAyoDCwDNxqXLpcr7p58LGly2AECbmmQbKalx1H7csmz4d0M15AURrgEVPQG+aRBUO9xf33o7y7elzrUHBqoJt93eqX+yLtfXP3/BuXt8cgQOahDgBhZqN9zKqY9++/PLr57no5e9tUpkb4FNVL63qU7A/r13ryyf/IC2vv9Rd8t4JrZTNRVQDYh+gDt1ULvhbr/6+mN/3Sg9PWmtnVqoBsj731y6im7YRJfgUwsV2u279U/P36mf+2zirtfUQeUJ0/RIpmWiwuVsq3n52xvye6fTaqtpnp2dtD1l9QVeU/0E03iA7FBFIF0qP8I0oAZyRfXw1s/Xbq8ee4b3MB9kWgIFlcHcfuNEak+uHOFhK93W2YE6n2zg6RSuxn2/rb+gwLQMf7+8o7JvUuOSc+uXl3KH/K6Zdg3WNdVvUBnMyrsfQvO7H4J4RL6CUFO+gcqeeIBh6gprvk31wzHK2dRTO0Eb5v0mrq5X1OLYj9uZ3SmfgOpEaITfzbNU4mj6CPQGFOk6oCau+niFlQI5ubRbXafsVrQqSSWvBIdZjnmSmkHww8u/aXgY7E1qWlHw2jsmPywApgCq7vizfoh7VF49Tfs9KnFDZXyyhj01NVWYgLGhmkPfAvV6PLEGhH4xtvgQCjD8U6Prlvv+YsIaSSMsWmrRVPZBt60wiwFY+G5aj9Q1HoWANCVNzdojAvXGUGGLgUikgBeAuR/24sHJdZ0AW5KmrfULsdQ32o8eS2UumsdlV15obT/McAGD0uT7BaXpGf724gyuNncgh7cuMK4KRq0It1mo4IRUmCF7BbZq2k+jHYN92QvrF4Q1GoOIHvxLTxS0GHSCgfVNvO9pdTUKwUAPEByRDWy/JVIa1aAMkU40NnYz2rVZYDyGycwjL3uI/eqdUaExSsi4SfThP5yCtWKJCDx6AAAAAElFTkSuQmCC") no-repeat' +
            '}';
        document.body.appendChild(style);
    }

    var onClick = function () {
        var iframe = document.createElement('iframe');
        iframe.className = this.className.replace('youtube-loader', 'youtube-loader-embedded');
        var src = '//www.youtube.com/embed/' + this.id + '?autoplay=1&autohide=1&border=0&wmode=opaque&enablejsapi=1&hd=1&showinfo=0';
        var start = this.getAttribute('start');
        if (start) {
            src += '&start=' + start
        }
        iframe.setAttribute('src', src);
        iframe.setAttribute('allowfullscreen', '');
        iframe.setAttribute('frameborder', '0');
        iframe.setAttribute('type', 'text/html');
        this.parentNode.replaceChild(iframe, this);
    };

    for (var i = 0; i < players.length; i++) {
        var player = players[i];

        var img = document.createElement('img');
        img.setAttribute('src', '//i.ytimg.com/vi/' + player.id + '/maxresdefault.jpg');
        img.setAttribute('class', 'thumb');

        var div = document.createElement('div');
        div.setAttribute('class', 'play');

        player.appendChild(img);
        player.appendChild(div);

        player.onclick = onClick;
    }
})();
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var assert = require('assert');
var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;

var errors = require('./errors');
var States = require('./reqres_states');

var DEFAULT_OUTGOING_REQ_TIMEOUT = 2000;

function TChannelConnectionBase(channel, direction, remoteAddr) {
    assert(!channel.destroyed, 'refuse to create connection for destroyed channel');

    var self = this;
    EventEmitter.call(self);
    self.channel = channel;
    self.options = self.channel.options;
    self.logger = channel.logger;
    self.random = channel.random;
    self.timers = channel.timers;
    self.direction = direction;
    self.remoteAddr = remoteAddr;
    self.timer = null;
    self.remoteName = null; // filled in by identify message

    // TODO: factor out an operation collection abstraction
    self.requests = {
        in: {},
        out: {}
    };
    self.pending = {
        in: 0,
        out: 0
    };

    self.lastTimeoutTime = 0;
    self.closing = false;

    self.startTimeoutTimer();
    self.tracer = self.channel.tracer;
}
inherits(TChannelConnectionBase, EventEmitter);

TChannelConnectionBase.prototype.close = function close(callback) {
    var self = this;
    self.resetAll(errors.SocketClosedError({reason: 'local close'}));
    callback();
};

// timeout check runs every timeoutCheckInterval +/- some random fuzz. Range is from
//   base - fuzz/2 to base + fuzz/2
TChannelConnectionBase.prototype.getTimeoutDelay = function getTimeoutDelay() {
    var self = this;
    var base = self.options.timeoutCheckInterval;
    var fuzz = self.options.timeoutFuzz;
    if (fuzz) {
        fuzz = Math.round(Math.floor(self.random() * fuzz) - (fuzz / 2));
    }
    return base + fuzz;
};

TChannelConnectionBase.prototype.startTimeoutTimer = function startTimeoutTimer() {
    var self = this;
    self.timer = self.timers.setTimeout(function onChannelTimeout() {
        // TODO: worth it to clear the fired self.timer objcet?
        self.onTimeoutCheck();
    }, self.getTimeoutDelay());
};

TChannelConnectionBase.prototype.clearTimeoutTimer = function clearTimeoutTimer() {
    var self = this;
    if (self.timer) {
        self.timers.clearTimeout(self.timer);
        self.timer = null;
    }
};

// If the connection has some success and some timeouts, we should probably leave it up,
// but if everything is timing out, then we should kill the connection.
TChannelConnectionBase.prototype.onTimeoutCheck = function onTimeoutCheck() {
    var self = this;
    if (self.closing) {
        return;
    }
    if (self.lastTimeoutTime) {
        self.emit('timedOut');
    } else {
        self.checkTimeout(self.requests.out, 'out');
        self.checkTimeout(self.requests.in, 'in');
        self.startTimeoutTimer();
    }
};

TChannelConnectionBase.prototype.checkTimeout = function checkTimeout(ops, direction) {
    var self = this;
    var opKeys = Object.keys(ops);
    for (var i = 0; i < opKeys.length; i++) {
        var id = opKeys[i];
        var req = ops[id];
        if (req === undefined) {
            self.logger.warn('unexpected undefined request', {
                direction: direction,
                id: id
            });
        } else if (req.timedOut) {
            self.logger.warn('lingering timed-out request', {
                direction: direction,
                id: id
            });
            delete ops[id];
            self.pending[direction]--;
        } else if (req.checkTimeout()) {
            if (direction === 'out') {
                self.lastTimeoutTime = self.timers.now();
            // } else {
            //     req.res.sendError // XXX may need to build
            }
            delete ops[id];
            self.pending[direction]--;
        }
    }
};

// this connection is completely broken, and is going away
// In addition to erroring out all of the pending work, we reset the state in case anybody
// stumbles across this object in a core dump.
TChannelConnectionBase.prototype.resetAll = function resetAll(err) {
    var self = this;

    self.clearTimeoutTimer();

    if (self.closing) return;
    self.closing = true;

    var inOpKeys = Object.keys(self.requests.in);
    var outOpKeys = Object.keys(self.requests.out);

    if (!err) {
        err = new Error('unknown connection reset'); // TODO typed error
    }

    var isError = err.type.indexOf('tchannel.socket') !== 0;
    self.logger[isError ? 'warn' : 'info']('resetting connection', {
        error: err,
        remoteName: self.remoteName,
        localName: self.channel.hostPort,
        numInOps: inOpKeys.length,
        numOutOps: outOpKeys.length,
        inPending: self.pending.in,
        outPending: self.pending.out
    });

    if (isError) {
        self.emit('error', err);
    }

    // requests that we've received we can delete, but these reqs may have started their
    //   own outgoing work, which is hard to cancel. By setting this.closing, we make sure
    //   that once they do finish that their callback will swallow the response.
    inOpKeys.forEach(function eachInOp(id) {
        // TODO: support canceling pending handlers
        delete self.requests.in[id];
        // TODO report or handle or log errors or something
    });

    // for all outgoing requests, forward the triggering error to the user callback
    outOpKeys.forEach(function eachOutOp(id) {
        var req = self.requests.out[id];
        delete self.requests.out[id];
        // TODO: shared mutable object... use Object.create(err)?
        req.emit('error', err);
    });

    self.pending.in = 0;
    self.pending.out = 0;
};

TChannelConnectionBase.prototype.popOutReq = function popOutReq(id) {
    var self = this;
    var req = self.requests.out[id];
    if (!req) {
        // TODO else case. We should warn about an incoming response for an
        // operation we did not send out.  This could be because of a timeout
        // or could be because of a confused / corrupted server.
        return;
    }
    delete self.requests.out[id];
    self.pending.out--;
    return req;
};

// create a request
TChannelConnectionBase.prototype.request = function connBaseRequest(options) {
    var self = this;
    if (!options) options = {};
    options.remoteAddr = self.remoteAddr;

    // TODO: use this to protect against >4Mi outstanding messages edge case
    // (e.g. zombie operation bug, incredible throughput, or simply very long
    // timeout
    // assert(!self.requests.out[id], 'duplicate frame id in flight');
    // TODO: provide some sort of channel default for "service"
    // TODO: generate tracing if empty?
    // TODO: refactor callers
    options.checksumType = options.checksum;

    // TODO: better default, support for dynamic
    options.ttl = options.timeout || DEFAULT_OUTGOING_REQ_TIMEOUT;
    options.tracer = self.tracer;
    var req = self.buildOutRequest(options);

    return self._addOutReq(req);
};

TChannelConnectionBase.prototype._addOutReq = function _addOutReq(req) {
    var self = this;
    self.requests.out[req.id] = req;
    self.pending.out++;
    return req;
};

TChannelConnectionBase.prototype.handleCallRequest = function handleCallRequest(req) {
    var self = this;
    req.remoteAddr = self.remoteName;
    self.pending.in++;
    self.requests.in[req.id] = req;
    req.on('error', onReqError);
    process.nextTick(runHandler);

    function onReqError(err) {
        self.onReqError(req, err);
    }

    function runHandler() {
        self.runHandler(req);
    }
};

TChannelConnectionBase.prototype.onReqError = function onReqError(req, err) {
    var self = this;
    if (!req.res) self.buildResponse(req);
    if (err.type === 'tchannel.timeout') {
        req.res.sendError('Timeout', err.message);
    } else {
        var errName = err.name || err.constructor.name;
        req.res.sendError('UnexpectedError', errName + ': ' + err.message);
    }
};

TChannelConnectionBase.prototype.runHandler = function runHandler(req) {
    var self = this;
    self.channel.handler.handleRequest(req, buildResponse);
    function buildResponse(options) {
        return self.buildResponse(req, options);
    }
};

TChannelConnectionBase.prototype.buildResponse = function buildResponse(req, options) {
    var self = this;
    var done = false;
    if (req.res && req.res.state !== States.Initial) {
        self.emit('error', errors.ResponseAlreadyStarted({
            state: req.res.state
        }));
    }
    req.res = self.buildOutResponse(req, options);
    req.res.on('finish', opDone);
    req.res.on('span', handleSpanFromRes);
    return req.res;

    function handleSpanFromRes(span) {
        self.emit('span', span);
    }

    function opDone() {
        if (done) return;
        done = true;
        self.onReqDone(req);
    }
};

TChannelConnectionBase.prototype.onReqDone = function onReqDone(req) {
    var self = this;
    if (self.requests.in[req.id] !== req) {
        self.logger.warn('mismatched onReqDone callback', {
            hostPort: self.channel.hostPort,
            id: req.id
        });
        return;
    }
    delete self.requests.in[req.id];
    self.pending.in--;
};

module.exports = TChannelConnectionBase;
<commit_msg>ConnectionBase: trivial cleanup in #onReqDone<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var assert = require('assert');
var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;

var errors = require('./errors');
var States = require('./reqres_states');

var DEFAULT_OUTGOING_REQ_TIMEOUT = 2000;

function TChannelConnectionBase(channel, direction, remoteAddr) {
    assert(!channel.destroyed, 'refuse to create connection for destroyed channel');

    var self = this;
    EventEmitter.call(self);
    self.channel = channel;
    self.options = self.channel.options;
    self.logger = channel.logger;
    self.random = channel.random;
    self.timers = channel.timers;
    self.direction = direction;
    self.remoteAddr = remoteAddr;
    self.timer = null;
    self.remoteName = null; // filled in by identify message

    // TODO: factor out an operation collection abstraction
    self.requests = {
        in: {},
        out: {}
    };
    self.pending = {
        in: 0,
        out: 0
    };

    self.lastTimeoutTime = 0;
    self.closing = false;

    self.startTimeoutTimer();
    self.tracer = self.channel.tracer;
}
inherits(TChannelConnectionBase, EventEmitter);

TChannelConnectionBase.prototype.close = function close(callback) {
    var self = this;
    self.resetAll(errors.SocketClosedError({reason: 'local close'}));
    callback();
};

// timeout check runs every timeoutCheckInterval +/- some random fuzz. Range is from
//   base - fuzz/2 to base + fuzz/2
TChannelConnectionBase.prototype.getTimeoutDelay = function getTimeoutDelay() {
    var self = this;
    var base = self.options.timeoutCheckInterval;
    var fuzz = self.options.timeoutFuzz;
    if (fuzz) {
        fuzz = Math.round(Math.floor(self.random() * fuzz) - (fuzz / 2));
    }
    return base + fuzz;
};

TChannelConnectionBase.prototype.startTimeoutTimer = function startTimeoutTimer() {
    var self = this;
    self.timer = self.timers.setTimeout(function onChannelTimeout() {
        // TODO: worth it to clear the fired self.timer objcet?
        self.onTimeoutCheck();
    }, self.getTimeoutDelay());
};

TChannelConnectionBase.prototype.clearTimeoutTimer = function clearTimeoutTimer() {
    var self = this;
    if (self.timer) {
        self.timers.clearTimeout(self.timer);
        self.timer = null;
    }
};

// If the connection has some success and some timeouts, we should probably leave it up,
// but if everything is timing out, then we should kill the connection.
TChannelConnectionBase.prototype.onTimeoutCheck = function onTimeoutCheck() {
    var self = this;
    if (self.closing) {
        return;
    }
    if (self.lastTimeoutTime) {
        self.emit('timedOut');
    } else {
        self.checkTimeout(self.requests.out, 'out');
        self.checkTimeout(self.requests.in, 'in');
        self.startTimeoutTimer();
    }
};

TChannelConnectionBase.prototype.checkTimeout = function checkTimeout(ops, direction) {
    var self = this;
    var opKeys = Object.keys(ops);
    for (var i = 0; i < opKeys.length; i++) {
        var id = opKeys[i];
        var req = ops[id];
        if (req === undefined) {
            self.logger.warn('unexpected undefined request', {
                direction: direction,
                id: id
            });
        } else if (req.timedOut) {
            self.logger.warn('lingering timed-out request', {
                direction: direction,
                id: id
            });
            delete ops[id];
            self.pending[direction]--;
        } else if (req.checkTimeout()) {
            if (direction === 'out') {
                self.lastTimeoutTime = self.timers.now();
            // } else {
            //     req.res.sendError // XXX may need to build
            }
            delete ops[id];
            self.pending[direction]--;
        }
    }
};

// this connection is completely broken, and is going away
// In addition to erroring out all of the pending work, we reset the state in case anybody
// stumbles across this object in a core dump.
TChannelConnectionBase.prototype.resetAll = function resetAll(err) {
    var self = this;

    self.clearTimeoutTimer();

    if (self.closing) return;
    self.closing = true;

    var inOpKeys = Object.keys(self.requests.in);
    var outOpKeys = Object.keys(self.requests.out);

    if (!err) {
        err = new Error('unknown connection reset'); // TODO typed error
    }

    var isError = err.type.indexOf('tchannel.socket') !== 0;
    self.logger[isError ? 'warn' : 'info']('resetting connection', {
        error: err,
        remoteName: self.remoteName,
        localName: self.channel.hostPort,
        numInOps: inOpKeys.length,
        numOutOps: outOpKeys.length,
        inPending: self.pending.in,
        outPending: self.pending.out
    });

    if (isError) {
        self.emit('error', err);
    }

    // requests that we've received we can delete, but these reqs may have started their
    //   own outgoing work, which is hard to cancel. By setting this.closing, we make sure
    //   that once they do finish that their callback will swallow the response.
    inOpKeys.forEach(function eachInOp(id) {
        // TODO: support canceling pending handlers
        delete self.requests.in[id];
        // TODO report or handle or log errors or something
    });

    // for all outgoing requests, forward the triggering error to the user callback
    outOpKeys.forEach(function eachOutOp(id) {
        var req = self.requests.out[id];
        delete self.requests.out[id];
        // TODO: shared mutable object... use Object.create(err)?
        req.emit('error', err);
    });

    self.pending.in = 0;
    self.pending.out = 0;
};

TChannelConnectionBase.prototype.popOutReq = function popOutReq(id) {
    var self = this;
    var req = self.requests.out[id];
    if (!req) {
        // TODO else case. We should warn about an incoming response for an
        // operation we did not send out.  This could be because of a timeout
        // or could be because of a confused / corrupted server.
        return;
    }
    delete self.requests.out[id];
    self.pending.out--;
    return req;
};

// create a request
TChannelConnectionBase.prototype.request = function connBaseRequest(options) {
    var self = this;
    if (!options) options = {};
    options.remoteAddr = self.remoteAddr;

    // TODO: use this to protect against >4Mi outstanding messages edge case
    // (e.g. zombie operation bug, incredible throughput, or simply very long
    // timeout
    // assert(!self.requests.out[id], 'duplicate frame id in flight');
    // TODO: provide some sort of channel default for "service"
    // TODO: generate tracing if empty?
    // TODO: refactor callers
    options.checksumType = options.checksum;

    // TODO: better default, support for dynamic
    options.ttl = options.timeout || DEFAULT_OUTGOING_REQ_TIMEOUT;
    options.tracer = self.tracer;
    var req = self.buildOutRequest(options);

    return self._addOutReq(req);
};

TChannelConnectionBase.prototype._addOutReq = function _addOutReq(req) {
    var self = this;
    self.requests.out[req.id] = req;
    self.pending.out++;
    return req;
};

TChannelConnectionBase.prototype.handleCallRequest = function handleCallRequest(req) {
    var self = this;
    req.remoteAddr = self.remoteName;
    self.pending.in++;
    self.requests.in[req.id] = req;
    req.on('error', onReqError);
    process.nextTick(runHandler);

    function onReqError(err) {
        self.onReqError(req, err);
    }

    function runHandler() {
        self.runHandler(req);
    }
};

TChannelConnectionBase.prototype.onReqError = function onReqError(req, err) {
    var self = this;
    if (!req.res) self.buildResponse(req);
    if (err.type === 'tchannel.timeout') {
        req.res.sendError('Timeout', err.message);
    } else {
        var errName = err.name || err.constructor.name;
        req.res.sendError('UnexpectedError', errName + ': ' + err.message);
    }
};

TChannelConnectionBase.prototype.runHandler = function runHandler(req) {
    var self = this;
    self.channel.handler.handleRequest(req, buildResponse);
    function buildResponse(options) {
        return self.buildResponse(req, options);
    }
};

TChannelConnectionBase.prototype.buildResponse = function buildResponse(req, options) {
    var self = this;
    var done = false;
    if (req.res && req.res.state !== States.Initial) {
        self.emit('error', errors.ResponseAlreadyStarted({
            state: req.res.state
        }));
    }
    req.res = self.buildOutResponse(req, options);
    req.res.on('finish', opDone);
    req.res.on('span', handleSpanFromRes);
    return req.res;

    function handleSpanFromRes(span) {
        self.emit('span', span);
    }

    function opDone() {
        if (done) return;
        done = true;
        self.onReqDone(req);
    }
};

TChannelConnectionBase.prototype.onReqDone = function onReqDone(req) {
    var self = this;
    if (self.requests.in[req.id] !== req) {
        self.logger.warn('mismatched onReqDone callback', {
            hostPort: self.channel.hostPort,
            id: req.id
        });
    } else {
        delete self.requests.in[req.id];
        self.pending.in--;
    }
};

module.exports = TChannelConnectionBase;
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;

var errors = require('./errors');
var OutgoingResponse = require('./outgoing_response');

var DEFAULT_OUTGOING_REQ_TIMEOUT = 2000;

function TChannelConnectionBase(channel, direction, remoteAddr) {
    var self = this;
    EventEmitter.call(self);
    self.channel = channel;
    self.options = self.channel.options;
    self.logger = channel.logger;
    self.random = channel.random;
    self.timers = channel.timers;
    self.direction = direction;
    self.remoteAddr = remoteAddr;
    self.timer = null;
    self.remoteName = null; // filled in by identify message

    // TODO: factor out an operation collection abstraction
    self.requests = {
        in: Object.create(null),
        out: Object.create(null)
    };
    self.pending = {
        in: 0,
        out: 0
    };

    self.lastTimeoutTime = 0;
    self.closing = false;

    self.startTimeoutTimer();
    self.tracer = self.channel.tracer;
}
inherits(TChannelConnectionBase, EventEmitter);

TChannelConnectionBase.prototype.close = function close(callback) {
    var self = this;
    self.resetAll(errors.SocketClosedError({reason: 'local close'}));
    callback();
};

// timeout check runs every timeoutCheckInterval +/- some random fuzz. Range is from
//   base - fuzz/2 to base + fuzz/2
TChannelConnectionBase.prototype.getTimeoutDelay = function getTimeoutDelay() {
    var self = this;
    var base = self.options.timeoutCheckInterval;
    var fuzz = self.options.timeoutFuzz;
    if (fuzz) {
        fuzz = Math.round(Math.floor(self.random() * fuzz) - (fuzz / 2));
    }
    return base + fuzz;
};

TChannelConnectionBase.prototype.startTimeoutTimer = function startTimeoutTimer() {
    var self = this;
    self.timer = self.timers.setTimeout(function onChannelTimeout() {
        // TODO: worth it to clear the fired self.timer objcet?
        self.onTimeoutCheck();
    }, self.getTimeoutDelay());
};

TChannelConnectionBase.prototype.clearTimeoutTimer = function clearTimeoutTimer() {
    var self = this;
    if (self.timer) {
        self.timers.clearTimeout(self.timer);
        self.timer = null;
    }
};

// If the connection has some success and some timeouts, we should probably leave it up,
// but if everything is timing out, then we should kill the connection.
TChannelConnectionBase.prototype.onTimeoutCheck = function onTimeoutCheck() {
    var self = this;
    if (self.closing) {
        return;
    }
    if (self.lastTimeoutTime) {
        self.emit('timedOut');
    } else {
        self.checkTimeout(self.requests.out, 'out');
        self.checkTimeout(self.requests.in, 'in');
        self.startTimeoutTimer();
    }
};

TChannelConnectionBase.prototype.checkTimeout = function checkTimeout(ops, direction) {
    var self = this;
    var opKeys = Object.keys(ops);
    for (var i = 0; i < opKeys.length; i++) {
        var id = opKeys[i];
        var req = ops[id];
        if (req === undefined) {
            self.logger.warn('unexpected undefined request', {
                direction: direction,
                id: id
            });
        } else if (req.timedOut) {
            self.logger.warn('lingering timed-out request', {
                direction: direction,
                id: id
            });
            delete ops[id];
            self.pending[direction]--;
        } else if (req.checkTimeout()) {
            if (direction === 'out') {
                self.lastTimeoutTime = self.timers.now();
            // } else {
            //     req.res.sendError // XXX may need to build
            }
            delete ops[id];
            self.pending[direction]--;
        }
    }
};

// this connection is completely broken, and is going away
// In addition to erroring out all of the pending work, we reset the state in case anybody
// stumbles across this object in a core dump.
TChannelConnectionBase.prototype.resetAll = function resetAll(err) {
    var self = this;

    self.clearTimeoutTimer();

    if (self.closing) return;
    self.closing = true;

    var inOpKeys = Object.keys(self.requests.in);
    var outOpKeys = Object.keys(self.requests.out);

    if (!err) {
        err = new Error('unknown connection reset'); // TODO typed error
    }

    var isError = err.type !== 'tchannel.socket-closed';
    self.logger[isError ? 'warn' : 'info']('resetting connection', {
        error: err,
        remoteName: self.remoteName,
        localName: self.channel.hostPort,
        numInOps: inOpKeys.length,
        numOutOps: outOpKeys.length,
        inPending: self.pending.in,
        outPending: self.pending.out
    });

    if (isError) {
        self.emit('error', err);
    }

    // requests that we've received we can delete, but these reqs may have started their
    //   own outgoing work, which is hard to cancel. By setting this.closing, we make sure
    //   that once they do finish that their callback will swallow the response.
    inOpKeys.forEach(function eachInOp(id) {
        // TODO: support canceling pending handlers
        delete self.requests.in[id];
        // TODO report or handle or log errors or something
    });

    // for all outgoing requests, forward the triggering error to the user callback
    outOpKeys.forEach(function eachOutOp(id) {
        var req = self.requests.out[id];
        delete self.requests.out[id];
        // TODO: shared mutable object... use Object.create(err)?
        req.emit('error', err);
    });

    self.pending.in = 0;
    self.pending.out = 0;
};

TChannelConnectionBase.prototype.popOutReq = function popOutReq(id) {
    var self = this;
    var req = self.requests.out[id];
    if (!req) {
        // TODO else case. We should warn about an incoming response for an
        // operation we did not send out.  This could be because of a timeout
        // or could be because of a confused / corrupted server.
        return;
    }
    delete self.requests.out[id];
    self.pending.out--;
    return req;
};

// create a request
TChannelConnectionBase.prototype.request = function connBaseRequest(options) {
    var self = this;
    if (!options) options = {};
    options.remoteAddr = self.remoteAddr;

    // TODO: use this to protect against >4Mi outstanding messages edge case
    // (e.g. zombie operation bug, incredible throughput, or simply very long
    // timeout
    // assert(!self.requests.out[id], 'duplicate frame id in flight');
    // TODO: provide some sort of channel default for "service"
    // TODO: generate tracing if empty?
    // TODO: refactor callers
    options.checksumType = options.checksum;

    // TODO: better default, support for dynamic
    options.ttl = options.timeout || DEFAULT_OUTGOING_REQ_TIMEOUT;
    options.tracer = self.tracer;
    var req = self.buildOutgoingRequest(options);
    self.requests.out[req.id] = req;
    self.pending.out++;
    return req;
};

TChannelConnectionBase.prototype.handleCallRequest = function handleCallRequest(req) {
    var self = this;
    req.remoteAddr = self.remoteName;
    self.pending.in++;
    self.requests.in[req.id] = req;
    var done = false;
    req.on('error', onReqError);
    process.nextTick(runHandler);

    function onReqError(err) {
        if (!req.res) buildResponse();
        if (err.type === 'tchannel.timeout') {
            req.res.sendError('Timeout', err.message);
        } else {
            var errName = err.name || err.constructor.name;
            req.res.sendError('UnexpectedError', errName + ': ' + err.message);
        }
    }

    function runHandler() {
        self.channel.handler.handleRequest(req, buildResponse);
    }

    function handleSpanFromRes(span) {
        self.emit('span', span);
    }

    function buildResponse(options) {
        if (req.res && req.res.state !== OutgoingResponse.States.Initial) {
            self.emit('error', errors.ResponseAlreadyStarted({
                state: req.res.state
            }));
        }
        req.res = self.buildOutgoingResponse(req, options);
        req.res.on('finish', opDone);
        req.res.on('errored', opDone);
        req.res.on('span', handleSpanFromRes);
        return req.res;
    }

    function opDone() {
        if (done) return;
        done = true;
        if (self.requests.in[req.id] !== req) {
            self.logger.warn('mismatched opDone callback', {
                hostPort: self.channel.hostPort,
                id: req.id
            });
            return;
        }
        delete self.requests.in[req.id];
        self.pending.in--;
    }
};

module.exports = TChannelConnectionBase;
<commit_msg>ConnectionBase: loosen socket error matching<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;

var errors = require('./errors');
var OutgoingResponse = require('./outgoing_response');

var DEFAULT_OUTGOING_REQ_TIMEOUT = 2000;

function TChannelConnectionBase(channel, direction, remoteAddr) {
    var self = this;
    EventEmitter.call(self);
    self.channel = channel;
    self.options = self.channel.options;
    self.logger = channel.logger;
    self.random = channel.random;
    self.timers = channel.timers;
    self.direction = direction;
    self.remoteAddr = remoteAddr;
    self.timer = null;
    self.remoteName = null; // filled in by identify message

    // TODO: factor out an operation collection abstraction
    self.requests = {
        in: Object.create(null),
        out: Object.create(null)
    };
    self.pending = {
        in: 0,
        out: 0
    };

    self.lastTimeoutTime = 0;
    self.closing = false;

    self.startTimeoutTimer();
    self.tracer = self.channel.tracer;
}
inherits(TChannelConnectionBase, EventEmitter);

TChannelConnectionBase.prototype.close = function close(callback) {
    var self = this;
    self.resetAll(errors.SocketClosedError({reason: 'local close'}));
    callback();
};

// timeout check runs every timeoutCheckInterval +/- some random fuzz. Range is from
//   base - fuzz/2 to base + fuzz/2
TChannelConnectionBase.prototype.getTimeoutDelay = function getTimeoutDelay() {
    var self = this;
    var base = self.options.timeoutCheckInterval;
    var fuzz = self.options.timeoutFuzz;
    if (fuzz) {
        fuzz = Math.round(Math.floor(self.random() * fuzz) - (fuzz / 2));
    }
    return base + fuzz;
};

TChannelConnectionBase.prototype.startTimeoutTimer = function startTimeoutTimer() {
    var self = this;
    self.timer = self.timers.setTimeout(function onChannelTimeout() {
        // TODO: worth it to clear the fired self.timer objcet?
        self.onTimeoutCheck();
    }, self.getTimeoutDelay());
};

TChannelConnectionBase.prototype.clearTimeoutTimer = function clearTimeoutTimer() {
    var self = this;
    if (self.timer) {
        self.timers.clearTimeout(self.timer);
        self.timer = null;
    }
};

// If the connection has some success and some timeouts, we should probably leave it up,
// but if everything is timing out, then we should kill the connection.
TChannelConnectionBase.prototype.onTimeoutCheck = function onTimeoutCheck() {
    var self = this;
    if (self.closing) {
        return;
    }
    if (self.lastTimeoutTime) {
        self.emit('timedOut');
    } else {
        self.checkTimeout(self.requests.out, 'out');
        self.checkTimeout(self.requests.in, 'in');
        self.startTimeoutTimer();
    }
};

TChannelConnectionBase.prototype.checkTimeout = function checkTimeout(ops, direction) {
    var self = this;
    var opKeys = Object.keys(ops);
    for (var i = 0; i < opKeys.length; i++) {
        var id = opKeys[i];
        var req = ops[id];
        if (req === undefined) {
            self.logger.warn('unexpected undefined request', {
                direction: direction,
                id: id
            });
        } else if (req.timedOut) {
            self.logger.warn('lingering timed-out request', {
                direction: direction,
                id: id
            });
            delete ops[id];
            self.pending[direction]--;
        } else if (req.checkTimeout()) {
            if (direction === 'out') {
                self.lastTimeoutTime = self.timers.now();
            // } else {
            //     req.res.sendError // XXX may need to build
            }
            delete ops[id];
            self.pending[direction]--;
        }
    }
};

// this connection is completely broken, and is going away
// In addition to erroring out all of the pending work, we reset the state in case anybody
// stumbles across this object in a core dump.
TChannelConnectionBase.prototype.resetAll = function resetAll(err) {
    var self = this;

    self.clearTimeoutTimer();

    if (self.closing) return;
    self.closing = true;

    var inOpKeys = Object.keys(self.requests.in);
    var outOpKeys = Object.keys(self.requests.out);

    if (!err) {
        err = new Error('unknown connection reset'); // TODO typed error
    }

    var isError = err.type.indexOf('tchannel.socket') !== 0;
    self.logger[isError ? 'warn' : 'info']('resetting connection', {
        error: err,
        remoteName: self.remoteName,
        localName: self.channel.hostPort,
        numInOps: inOpKeys.length,
        numOutOps: outOpKeys.length,
        inPending: self.pending.in,
        outPending: self.pending.out
    });

    if (isError) {
        self.emit('error', err);
    }

    // requests that we've received we can delete, but these reqs may have started their
    //   own outgoing work, which is hard to cancel. By setting this.closing, we make sure
    //   that once they do finish that their callback will swallow the response.
    inOpKeys.forEach(function eachInOp(id) {
        // TODO: support canceling pending handlers
        delete self.requests.in[id];
        // TODO report or handle or log errors or something
    });

    // for all outgoing requests, forward the triggering error to the user callback
    outOpKeys.forEach(function eachOutOp(id) {
        var req = self.requests.out[id];
        delete self.requests.out[id];
        // TODO: shared mutable object... use Object.create(err)?
        req.emit('error', err);
    });

    self.pending.in = 0;
    self.pending.out = 0;
};

TChannelConnectionBase.prototype.popOutReq = function popOutReq(id) {
    var self = this;
    var req = self.requests.out[id];
    if (!req) {
        // TODO else case. We should warn about an incoming response for an
        // operation we did not send out.  This could be because of a timeout
        // or could be because of a confused / corrupted server.
        return;
    }
    delete self.requests.out[id];
    self.pending.out--;
    return req;
};

// create a request
TChannelConnectionBase.prototype.request = function connBaseRequest(options) {
    var self = this;
    if (!options) options = {};
    options.remoteAddr = self.remoteAddr;

    // TODO: use this to protect against >4Mi outstanding messages edge case
    // (e.g. zombie operation bug, incredible throughput, or simply very long
    // timeout
    // assert(!self.requests.out[id], 'duplicate frame id in flight');
    // TODO: provide some sort of channel default for "service"
    // TODO: generate tracing if empty?
    // TODO: refactor callers
    options.checksumType = options.checksum;

    // TODO: better default, support for dynamic
    options.ttl = options.timeout || DEFAULT_OUTGOING_REQ_TIMEOUT;
    options.tracer = self.tracer;
    var req = self.buildOutgoingRequest(options);
    self.requests.out[req.id] = req;
    self.pending.out++;
    return req;
};

TChannelConnectionBase.prototype.handleCallRequest = function handleCallRequest(req) {
    var self = this;
    req.remoteAddr = self.remoteName;
    self.pending.in++;
    self.requests.in[req.id] = req;
    var done = false;
    req.on('error', onReqError);
    process.nextTick(runHandler);

    function onReqError(err) {
        if (!req.res) buildResponse();
        if (err.type === 'tchannel.timeout') {
            req.res.sendError('Timeout', err.message);
        } else {
            var errName = err.name || err.constructor.name;
            req.res.sendError('UnexpectedError', errName + ': ' + err.message);
        }
    }

    function runHandler() {
        self.channel.handler.handleRequest(req, buildResponse);
    }

    function handleSpanFromRes(span) {
        self.emit('span', span);
    }

    function buildResponse(options) {
        if (req.res && req.res.state !== OutgoingResponse.States.Initial) {
            self.emit('error', errors.ResponseAlreadyStarted({
                state: req.res.state
            }));
        }
        req.res = self.buildOutgoingResponse(req, options);
        req.res.on('finish', opDone);
        req.res.on('errored', opDone);
        req.res.on('span', handleSpanFromRes);
        return req.res;
    }

    function opDone() {
        if (done) return;
        done = true;
        if (self.requests.in[req.id] !== req) {
            self.logger.warn('mismatched opDone callback', {
                hostPort: self.channel.hostPort,
                id: req.id
            });
            return;
        }
        delete self.requests.in[req.id];
        self.pending.in--;
    }
};

module.exports = TChannelConnectionBase;
<|endoftext|>