<commit_before>class TextWriter {

    constructor({
        element         = undefined,
        text            = ['Hello World'],
        addDelay        = 1000,
        addSpeed        = 100,
        speedVariance   = 0,
        deleteDelay     = 1000,
        deleteSpeed     = 50,
        loopInfinitely  = true,
        // endPoint        = 'end',
        cursorCharacter = '|'
    }) {

        // Private
        this._textIndexFirst = 0;
        this._textIndexCurr  = 0;
        this._textIndexLast  = text.length - 1;

        // Public
        this.element         = element;
        this.text            = text;

        this.addDelay        = addDelay;
        this.addSpeed        = addSpeed;
        this.speedVariance   = speedVariance;
        this.deleteDelay     = deleteDelay;
        this.deleteSpeed     = deleteSpeed;
        this.loopInfinitely  = loopInfinitely;
        // this.endPoint        = endPoint;
        this.cursorCharacter = cursorCharacter;

        // Require a DOM node type
        if ( !this.element ) {
            throw new Error('An element is required');
        }

        // Require a string
        if ( !this.text ) {
            throw new Error('Text is required');
        }

        this.element.innerHTML = `

                ${ this.cursorCharacter }
            `;

        this.textLength = this.text[ this._textIndexFirst ].length;
        this.counter    = 0;

        // If there's a waiting time
        setTimeout(
            () => {
                if ( this.addDelay ) {
                    this.removeClass( ' text-fill-cursor--animate' );
                }
                this.addTextToElement(
                    this.element.children[0],
                    this.text[ this._textIndexCurr ].split(''),
                    this.counter,
                    this.calculateSpeed( this.addSpeed, this.speedVariance )
                );
            },
            this.addDelay
        );

    }

    /**
     * Add a class from the cursor element
     * @param  {string} className The class name to add
     */
    addClass( className ) {
        this.element.children[1].className += className;
    }

    /**
     * Remove a class from the cursor element
     * @param  {string} className The class name to add
     */
    removeClass( className ) {
        this.element.children[1].className = this.element.children[1].className.replace( className, '' );
    }

    /**
     * Returns the set speed or calculates a variance of that base speed
     * @param  {number} speed         The base speed to use
     * @param  {number} speedVariance Optional: provide a variance to multioply the base speed by
     * @return {number}               The base or calculated speed
     */
    calculateSpeed( speed, speedVariance ) {

        // Nothing to calculate
        if ( speedVariance === 0 ) {
            return speed;
        }

        return speed + Math.ceil( Math.random() * speedVariance );

    }

    /**
     * Add a letter, used recursively
     * @param  {object} element     The selected element
     * @param  {string} element     The word/sentence to write out
     * @param  {number} counter     The counter / position to remove from, typically the start of the string
     * @param  {number} deleteSpeed The speed in ms to add each letter
     */
    addTextToElement( element, text, counter, speed ) {

        setTimeout( () => {

            // Remove the animation class before adding text
            if ( counter === 0 ) {
                this.removeClass( ' text-fill-cursor--animate' );
            }

            // Add the text node
            // Increment the character counter/index
            element.textContent += text[ counter++ ];

            // Recursively add the next character
            if ( counter < text.length ) {
                this.addTextToElement( element, text, counter, this.calculateSpeed( this.addSpeed, this.speedVariance ) );
            }

            // Animate when it has reached the end
            if ( counter >= text.length ) {
                this.addClass( ' text-fill-cursor--animate' );
            }

            // If the current text is complete and there's instructions to remove the text
            if (
                counter >= text.length &&
                (   this.loopInfinitely ||
                    this._textIndexCurr < this._textIndexLast
                )
            ) {

                this.removeTextFromElement(
                    element,
                    element.textContent.length,
                    this.deleteDelay
                );
            }

        }, speed );

    };

    /**
     * Remove a letter, used recursively
     * @param  {object} element     The selected element
     * @param  {number} counter     The counter / position to remove from, typically the end of the string
     * @param  {number} deleteSpeed The speed in ms to remove each letter
     */
    removeTextFromElement( element, counter, deleteSpeed ) {

        setTimeout( () => {

            // Remove the animation class before removing text
            if ( counter === element.textContent.length ) {
                this.removeClass( ' text-fill-cursor--animate' );
            }

            // Add the text node
            // De-increment the character counter/index
            element.textContent = element.textContent.substring( 0, counter-- );

            // Recursively remove the next character
            if ( counter >= 0 ) {
                this.removeTextFromElement( element, counter, this.deleteSpeed );
            }

            // Animate when it has reached the start
            if ( counter < 0 ) {
                this.addClass( ' text-fill-cursor--animate' );
            }

            // Loop if set to infinitely loop
            if ( counter < 0 &&
                ( this.loopInfinitely ||
                    this._textIndexCurr <= this._textIndexLast
                    )
            ) {

                // If there's more than one message to display
                // Increment the index of which message is the current one
                if (
                    this.text.length > 1 &&
                    this._textIndexCurr < this._textIndexLast
                ) {

                    this._textIndexCurr++;

                // Infinitely looping?
                // The end has been reached, set the current message as the one to display
                } else if (
                    this.text.length > 1 &&
                    this._textIndexCurr >= this._textIndexLast &&
                    this.loopInfinitely
                ) {
                    this._textIndexCurr = 0;
                }

                this.addTextToElement(
                    this.element.children[0],
                    this.text[ this._textIndexCurr ].split(''),
                    0,
                    this.addDelay
                );
            }

        }, deleteSpeed );

    }

}
<commit_msg>Remove superfluous spacing Update methods used to add/remove classes to elements<commit_after>class TextWriter {

    constructor({
        element         = undefined,
        text            = ['Hello World'],
        addDelay        = 1000,
        addSpeed        = 100,
        speedVariance   = 0,
        deleteDelay     = 1000,
        deleteSpeed     = 50,
        loopInfinitely  = true,
        // endPoint        = 'end',
        cursorCharacter = '|'
    }) {

        // Private
        this._textIndexFirst = 0;
        this._textIndexCurr  = 0;
        this._textIndexLast  = text.length - 1;

        // Public
        this.element         = element;
        this.text            = text;

        this.addDelay        = addDelay;
        this.addSpeed        = addSpeed;
        this.speedVariance   = speedVariance;
        this.deleteDelay     = deleteDelay;
        this.deleteSpeed     = deleteSpeed;
        this.loopInfinitely  = loopInfinitely;
        // this.endPoint        = endPoint;
        this.cursorCharacter = cursorCharacter;

        // Require a DOM node type
        if (!this.element) {
            throw new Error('An element is required');
        }

        // Require a string
        if (!this.text) {
            throw new Error('Text is required');
        }

        // Span elements need to touch to prevent space
        this.element.innerHTML = `

                ${this.cursorCharacter}
            `;

        this.textLength = this.text[ this._textIndexFirst ].length;
        this.counter    = 0;

        // If there's a waiting time
        setTimeout(
            () => {
                if (this.addDelay) {
                    this.removeClass('text-fill-cursor--animate');
                }
                this.addTextToElement(
                    this.element.children[0],
                    this.text[ this._textIndexCurr ].split(''),
                    this.counter,
                    this.calculateSpeed(this.addSpeed, this.speedVariance)
                );
            },
            this.addDelay
        );

    }

    /**
     * Add a class from the cursor element
     * @param  {string} className The class name to add
     */
    addClass(className) {
        this.element.children[1].classList.add(className);
    }

    /**
     * Remove a class from the cursor element
     * @param  {string} className The class name to add
     */
    removeClass(className) {
        this.element.children[1].classList.remove(className);
    }

    /**
     * Returns the set speed or calculates a variance of that base speed
     * @param  {number} speed         The base speed to use
     * @param  {number} speedVariance Optional: provide a variance to multioply the base speed by
     * @return {number}               The base or calculated speed
     */
    calculateSpeed(speed, speedVariance) {

        // Nothing to calculate
        if (speedVariance === 0) {
            return speed;
        }

        return speed + Math.ceil(Math.random() * speedVariance);

    }

    /**
     * Add a letter, used recursively
     * @param  {object} element     The selected element
     * @param  {string} element     The word/sentence to write out
     * @param  {number} counter     The counter / position to remove from, typically the start of the string
     * @param  {number} deleteSpeed The speed in ms to add each letter
     */
    addTextToElement(element, text, counter, speed) {

        setTimeout(() => {

            // Remove the animation class before adding text
            if (counter === 0) {
                this.removeClass('text-fill-cursor--animate');
            }

            // Add the text node
            // Increment the character counter/index
            element.textContent += text[ counter++ ];

            // Recursively add the next character
            if (counter < text.length) {
                this.addTextToElement(element, text, counter, this.calculateSpeed(this.addSpeed, this.speedVariance));
            }

            // Animate when it has reached the end
            if (counter >= text.length) {
                this.addClass('text-fill-cursor--animate');
            }

            // If the current text is complete and there's instructions to remove the text
            if (
                counter >= text.length &&
                (  this.loopInfinitely ||
                    this._textIndexCurr < this._textIndexLast
                )
            ) {

                this.removeTextFromElement(
                    element,
                    element.textContent.length,
                    this.deleteDelay
                );
            }

        }, speed);

    };

    /**
     * Remove a letter, used recursively
     * @param  {object} element     The selected element
     * @param  {number} counter     The counter / position to remove from, typically the end of the string
     * @param  {number} deleteSpeed The speed in ms to remove each letter
     */
    removeTextFromElement(element, counter, deleteSpeed) {

        setTimeout(() => {

            // Remove the animation class before removing text
            if (counter === element.textContent.length) {
                this.removeClass('text-fill-cursor--animate');
            }

            // Add the text node
            // De-increment the character counter/index
            element.textContent = element.textContent.substring(0, counter--);

            // Recursively remove the next character
            if (counter >= 0) {
                this.removeTextFromElement(element, counter, this.deleteSpeed);
            }

            // Animate when it has reached the start
            if (counter < 0) {
                this.addClass('text-fill-cursor--animate');
            }

            // Loop if set to infinitely loop
            if (counter < 0 &&
                (this.loopInfinitely ||
                    this._textIndexCurr <= this._textIndexLast
                    )
            ) {

                // If there's more than one message to display
                // Increment the index of which message is the current one
                if (
                    this.text.length > 1 &&
                    this._textIndexCurr < this._textIndexLast
                ) {

                    this._textIndexCurr++;

                // Infinitely looping?
                // The end has been reached, set the current message as the one to display
                } else if (
                    this.text.length > 1 &&
                    this._textIndexCurr >= this._textIndexLast &&
                    this.loopInfinitely
                ) {
                    this._textIndexCurr = 0;
                }

                this.addTextToElement(
                    this.element.children[0],
                    this.text[ this._textIndexCurr ].split(''),
                    0,
                    this.addDelay
                );
            }

        }, deleteSpeed);

    }

}
<|endoftext|>


<commit_before>import Search from './Search';

export default Search
<commit_msg>semicolon, I used to use gaeron style to commit semicolon to make code more clearer.<commit_after>import Search from './Search';

export default Search;
<|endoftext|>


<commit_before>"use babel";

const COURSE_TYPES = {
  DARWIN: { courseType: 1, apiType: 1, courseName: "DARWIN" },
  LT: { courseType: 2, apiType: 2, courseName: "LT" },
  PT: { courseType: 3, apiType: 3, courseName: "PT" },
  OCC: { courseType: 4, apiType: 4, courseName: "OCC" },
  BE: { courseType: 4, apiType: 5, courseName: "BE" },
  PILOT: { courseType: 4, apiType: 6, courseName: "PILOT" },
  PHONICS: { courseType: 5, apiType: 7, courseName: "PHONICS" },
  BELL: { courseType: 6, apiType: 8, courseName: "BELL" },
  TOURISM: { courseType: 1, apiType: 9, courseName: "TOURISM" },
  // reuse TOURISM
  ALIX_V2: { courseType: 1, apiType: 9, courseName: "TOURISM" },
  LINGOCHAMP: { courseType: 7, apiType: 10, courseName: "LINGOCHAMP" },
  "DARWIN BUSINESS": {
    courseType: 1,
    apiType: 9,
    courseName: "DARWIN BUSINESS"
  },
  "DARWIN EXTENSION": { courseType: 1, apiType: 26, courseName: "DARWIN EXTENSION" },
  "DARWIN HIFI": { courseType: 1, apiType: 15, courseName: "DARWIN HIFI" },
  "DARWIN HOMEWORK": {
    courseType: 1,
    apiType: 17,
    courseName: "DARWIN HOMEWORK"
  },
  AIX: { courseType: 15, apiType: 18, courseName: "AIX" },
  "SPROUT DAILY READERS": {
    courseType: 16,
    apiType: 19,
    courseName: "SPROUT DAILY READERS"
  },
  "Darwin PvP": { courseType: 1, apiType: 20, courseName: "Darwin PvP" },
  "SPROUT WORD COURSE": {
    courseType: 17,
    apiType: 21,
    courseName: "SPROUT DAILY WORDS"
  },
  "SPROUT EXTENSION": {
    courseType: 18,
    apiType: 22,
    courseName: "SPROUT EXTENSION"
  },
  "SPROUT GOODNIGHT": {
    courseType: 18,
    apiType: 23,
    courseName: "SPROUT GOODNIGHT"
  },
  "OL SHORT VIDEO": { courseType: 19, apiType: 24, courseName: "OL SHORT VIDEO" },
  "SPROUT COMPAT READERS": {
    courseType: 20,
    apiType: 28,
    courseName: "SPROUT COMPATRDR",
  },
  "OL LISTENING": {
    courseType: 1, apiType: 29, courseName: "OL LISTENING"
  },
  "LINGOME SPEAKING": {
    courseType: 21, apiType: 27, courseName: "LINGOME CMS"
  },
  "SUPRA": {
    courseType: 22, apiType: 30, courseName: "SUPRA"
  },
  "SPROUT GUIDANCE": {
    courseType: 23, apiType: 34, courseName: "SPROUT GUIDANCE"
  },
  TOEIC: {
    courseType: 24, apiType: 33, courseName: "TOEIC"
  }
};

export default COURSE_TYPES;<commit_msg>feat: add submarine_k12 (#231)<commit_after>"use babel";

const COURSE_TYPES = {
  DARWIN: { courseType: 1, apiType: 1, courseName: "DARWIN" },
  LT: { courseType: 2, apiType: 2, courseName: "LT" },
  PT: { courseType: 3, apiType: 3, courseName: "PT" },
  OCC: { courseType: 4, apiType: 4, courseName: "OCC" },
  BE: { courseType: 4, apiType: 5, courseName: "BE" },
  PILOT: { courseType: 4, apiType: 6, courseName: "PILOT" },
  PHONICS: { courseType: 5, apiType: 7, courseName: "PHONICS" },
  BELL: { courseType: 6, apiType: 8, courseName: "BELL" },
  TOURISM: { courseType: 1, apiType: 9, courseName: "TOURISM" },
  // reuse TOURISM
  ALIX_V2: { courseType: 1, apiType: 9, courseName: "TOURISM" },
  LINGOCHAMP: { courseType: 7, apiType: 10, courseName: "LINGOCHAMP" },
  "DARWIN BUSINESS": {
    courseType: 1,
    apiType: 9,
    courseName: "DARWIN BUSINESS"
  },
  "DARWIN EXTENSION": { courseType: 1, apiType: 26, courseName: "DARWIN EXTENSION" },
  "DARWIN HIFI": { courseType: 1, apiType: 15, courseName: "DARWIN HIFI" },
  "DARWIN HOMEWORK": {
    courseType: 1,
    apiType: 17,
    courseName: "DARWIN HOMEWORK"
  },
  AIX: { courseType: 15, apiType: 18, courseName: "AIX" },
  "SPROUT DAILY READERS": {
    courseType: 16,
    apiType: 19,
    courseName: "SPROUT DAILY READERS"
  },
  "Darwin PvP": { courseType: 1, apiType: 20, courseName: "Darwin PvP" },
  "SPROUT WORD COURSE": {
    courseType: 17,
    apiType: 21,
    courseName: "SPROUT DAILY WORDS"
  },
  "SPROUT EXTENSION": {
    courseType: 18,
    apiType: 22,
    courseName: "SPROUT EXTENSION"
  },
  "SPROUT GOODNIGHT": {
    courseType: 18,
    apiType: 23,
    courseName: "SPROUT GOODNIGHT"
  },
  "OL SHORT VIDEO": { courseType: 19, apiType: 24, courseName: "OL SHORT VIDEO" },
  "SPROUT COMPAT READERS": {
    courseType: 20,
    apiType: 28,
    courseName: "SPROUT COMPATRDR",
  },
  "OL LISTENING": {
    courseType: 1, apiType: 29, courseName: "OL LISTENING"
  },
  "LINGOME SPEAKING": {
    courseType: 21, apiType: 27, courseName: "LINGOME CMS"
  },
  "SUPRA": {
    courseType: 22, apiType: 30, courseName: "SUPRA"
  },
  "SPROUT GUIDANCE": {
    courseType: 23, apiType: 34, courseName: "SPROUT GUIDANCE"
  },
  TOEIC: {
    courseType: 24, apiType: 33, courseName: "TOEIC"
  },
  'SUBMARINE K12': { courseType: 1, apiType: 1, courseName: "SUBMARINE K12" },
};

export default COURSE_TYPES;<|endoftext|>


<commit_before>var script = d3.select('#provisualizer-script').attr('src');
var baseUrl = script.substring(0, script.lastIndexOf('provisualizer.js'));

// add stylesheet
var stylesheet = d3.select('head')
	.append('link')
		.attr('type', 'text/css')
		.attr('rel', 'stylesheet')
		.attr('href', baseUrl + 'provisualizer.css');

var maxLabelLength = 30;
var provisualizer = d3.select("#provisualizer");
var width = provisualizer.node().offsetWidth;
var height = provisualizer.node().offsetHeight;

var force = d3.layout.force()
	.size([width, height])
	.charge(-400)
	.chargeDistance(350)
	.linkDistance(50)
	.on("tick", tick);

var drag = force.drag()
	.on("dragstart", dragstart);

addSearchForm();
var svg = provisualizer.append("svg")
	.attr("width", "100%")
	.attr("height", "100%");

	var linkLines = svg.selectAll(".link");
	var nodeCircles = svg.selectAll("circle.node");
	var nodeLabels = svg.selectAll("text.node");

	// array of rows read from CSV (unfiltered)
	var agencyFunctionRelationships = [];

	// map from node names to node attributes
	var nodeAttributesByNodeName = {};

d3.csv(
	baseUrl + "data/nodes.csv",
	function(error, nodeAttributes) {

		nodeAttributes.forEach(
			function(nodeAttributeSet) {
				nodeAttributesByNodeName[nodeAttributeSet.FUNCTION] = nodeAttributeSet; // though we don't really need to copy the FUNCTION column
			}
		);
		// Now these node attributes can be read below
		// Note: the "FUNCTION" columns is misnamed because it includes both functions AND agencies
		// Also the table should really have included a TYPE column to specify "function" or "agency"
		d3.csv(
			baseUrl + "data/agency-functions.csv",
			function(error, agencyFunctionRelationshipsCSV) {
				agencyFunctionRelationships = agencyFunctionRelationshipsCSV;
				createFilteredGraphFromLinks();
			}
		)
	}
);

function tick() {
	linkLines
		.attr("x1", function(d) { return d.source.x; })
		.attr("y1", function(d) { return d.source.y; })
		.attr("x2", function(d) { return d.target.x; })
		.attr("y2", function(d) { return d.target.y; });
	nodeLabels
		.attr("x", function(d) { return d.x; })
		.attr("y", function(d) { return d.y; });
	nodeCircles
		.attr("cx", function(d) { return d.x; })
		.attr("cy", function(d) { return d.y; });
}

function dblclick(d) {
  d3.select(this).classed("fixed", d.fixed = false);
}

function dragstart(d) {
  d3.select(this).classed("fixed", d.fixed = true);
}

function jump(d) {
	window.location = d.URL
}

function createFilteredGraphFromLinks() {
	// Extract the distinct nodes from the node relationship table.

	// arrays containing the node objects, and the link objects, for D3 force layout
	var nodes = [];
	var links = [];
	// map from node names to node array indices
	var nodeIndicesByNodeName = {};

	agencyFunctionRelationships.forEach(
		function(agencyFunctionRow) {
			var agencyPeriod = nodeAttributesByNodeName[agencyFunctionRow.AGENCY].PERIOD;
			if (
				matchesDateFilter(agencyPeriod) &&
				(matchesTextFilter(agencyFunctionRow.FUNCTION) || matchesTextFilter(agencyFunctionRow.AGENCY))
			) {

				if (! (agencyFunctionRow.AGENCY in nodeIndicesByNodeName)) {
					// no node with that name yet
					var newNodeIndex = nodes.length;
					var agencyNode =  {
						name: agencyFunctionRow.AGENCY,
						type: "agency"
					};
					var nodeAttributes = nodeAttributesByNodeName[agencyFunctionRow.AGENCY];
					for(var p in nodeAttributes) agencyNode[p]=nodeAttributes[p];
					nodes[newNodeIndex] = agencyNode;
					nodeIndicesByNodeName[agencyFunctionRow.AGENCY] = newNodeIndex;
					addNodeLabel(agencyNode, nodes, links);
				}
				if (! (agencyFunctionRow.FUNCTION in nodeIndicesByNodeName)) {
					// no node with that name yet
					var newNodeIndex = nodes.length;
					var functionNode = {
						name: agencyFunctionRow.FUNCTION,
						type: "function"
					};
					var nodeAttributes = nodeAttributesByNodeName[agencyFunctionRow.FUNCTION];
					for(var p in nodeAttributes) functionNode[p]=nodeAttributes[p];
					nodes[newNodeIndex] = functionNode;
					nodeIndicesByNodeName[agencyFunctionRow.FUNCTION] = newNodeIndex;
					addNodeLabel(functionNode, nodes, links);
				}
				links.push(
					{
						source: nodes[nodeIndicesByNodeName[agencyFunctionRow.AGENCY]],
						target: nodes[nodeIndicesByNodeName[agencyFunctionRow.FUNCTION]]
					}
				)
			}
		}
	);

	// create force layout
	force
		.nodes(nodes)
		.links(links)
		.start();

	// (re-)populate force layout

	linkLines = linkLines.data(
		links,
		function(d) {
			return d.source.name + "->" + d.target.name;
		}
	);
	linkLines.exit().remove();
	linkLines
		.enter().append("line")
		.attr("class", "link");

	nodeCircles = nodeCircles.data(
		nodes,
		function(d) {
			return d.name + d.type;
		}
	);

	nodeCircles.exit().remove();

	nodeCircles.enter()
		.append("circle")
			.attr(
				"class",
				function(n) {
					return n.type + " node";
				}
			)
			.attr(
				"title",
				function(n) {
					return n.name;
				}
			)
			.attr(
				"r",
				function(n) {
					//return 10;
					return Math.sqrt(n.weight) * 10;
				}
			)
			// opacity depends on the node degree - node with infinite degree would be solid, degree 1 would be almost transparent
			.style(
				"fill-opacity",
				function(n) {
					return 1 - (.8/ n.weight);
				}
			)
			.on("dblclick", dblclick)
			.call(drag);

	nodeLabels = nodeLabels.data(
		nodes,
		function(d) {
			return d.name + d.type;
		}
	);

	nodeLabels.exit().remove();

	nodeLabels.enter()
		.append("text")
			.attr(
				"class",
				function(n) {
					return n.type + " node label";
				}
			)
			//.attr("x", function(d) { return d.cx; })
			//.attr("y", function(d) { return d.cy; })
			.text(
				function (n) {
					if (n.name.length > maxLabelLength)
						return n.name.substring(0, maxLabelLength) + "..."
					else
						return n.name;
				}
			)
			.attr(
				"title",
				function(n) {
					return n.name;
				}
			)
			.on("click", jump);

}

// experiment to add agency and function labels as nodes in their own right, linked to the circles representing agencies and functions
function addNodeLabel(node, nodes, links) {
	return; // disabled
	// attach a new label node to the specified node
	var nodeIndex = nodes.length;
	var labelNode = {
		name: node.name,
		type: "label"
	};
	nodes[nodeIndex] = labelNode;
	links.push(
		{
			source: nodes[nodeIndex - 1],
			target: nodes[nodeIndex]
		}
	)
}

function addSearchForm() {
	// search for the word specified in the URL fragment identifier
	var searchPhrase = "road";
	var fragment = window.location.hash;
	if (fragment) {
		// trim the leading # and decode the fragment identifier
		searchPhrase = decodeURIComponent(fragment.substring(1));
	}
	var searchForm = provisualizer.append("form");
	var labelSearch = searchForm.append("label")
		.attr("id", "agency-or-function-name-label")
		.attr("for", "agency-or-function-name-filter")
		.text("Agency or function:");
	var textSearch = searchForm.append("input")
		.attr("id", "agency-or-function-name-filter")
		.attr("type", "text")
		.attr("size", "20")
		.property("value", searchPhrase);
	var yearLabel = searchForm.append("label")
		.attr("id", "year-label")
		.attr("for", "year-filter")
		.text("Year:");
	var yearSearch = searchForm.append("input")
		.attr("id", "year-filter")
		.attr("type", "text")
		.attr("size", "4")
		.attr("maxlength", "4");
	var submitButton = searchForm.append("input")
		.attr("id", "submit")
		.attr("type", "submit")
		.property("value", "Search");

	searchForm
		.on(
			"submit",
			function(d, i) {
				// this event is now handled
				d3.event.preventDefault();
				createFilteredGraphFromLinks();
				window.location.hash = "#" + encodeURIComponent(textSearch.property("value"));
			}
		);
	return searchForm;
}

function matchesTextFilter(text) {
	var textFilter = d3.select('#agency-or-function-name-filter').property("value").toUpperCase();
	return text.toUpperCase().indexOf(textFilter) != -1
}

function matchesDateFilter(period) {
	var yearFilterText = d3.select('#year-filter').property("value");
	if (yearFilterText == "") {
		return true; // all records match a blank year filter
	}

	/* Example date ranges and their interpretations:

	ND-1851				x <= 1851
	c 1976-ct				1976 <= x <= current year
	c 1886-1905			1886 <= x <= 1905
	by 1921-by 1921		1921 <= x <= 1921 (i.e. x=1921)
	2010-2013 			2010 <= x <= 2013
	1987-by 2002			1987 <= x <= 2002
	1981-1981 - 1988	1981 <= x <= 1988
	1936-? 1994 			1936 <= x <= 1994
	? 1869-ct 				1869 <= x <= current year
	*/
	/* parsing date ranges:
	discard all chars not in "-0123456789"
	first date is text before "-"
	last date is text after final "-"
	if first date is blank, set it to "1770" (the year of Cook's first visit to Australia)
	if last date is blank, set it to the current year
	*/
	var cleanPeriod = period.replace(/[^-\d]*/g, "");
	var startYearGiven = cleanPeriod.substr(0, cleanPeriod.indexOf("-"));
	var endYearGiven = cleanPeriod.substr(1 + cleanPeriod.lastIndexOf("-"));
	var startYear, endYear;
	if (startYearGiven == "") {
		startYear = 1770;
	} else {
		startYear = Number(startYearGiven);
	}
	if (endYearGiven == "") {
		endYear = new Date().getFullYear();
	} else {
		endYear = Number(endYearGiven);
	}
	console.log(period, cleanPeriod, startYear, endYear);
	var yearFilter = Number(yearFilterText);

	return (startYear <= yearFilter) && (endYear >= yearFilter);
}
<commit_msg>add year to URI fragment identifier<commit_after>var script = d3.select('#provisualizer-script').attr('src');
var baseUrl = script.substring(0, script.lastIndexOf('provisualizer.js'));

// add stylesheet
var stylesheet = d3.select('head')
	.append('link')
		.attr('type', 'text/css')
		.attr('rel', 'stylesheet')
		.attr('href', baseUrl + 'provisualizer.css');

var maxLabelLength = 30;
var provisualizer = d3.select("#provisualizer");
var width = provisualizer.node().offsetWidth;
var height = provisualizer.node().offsetHeight;

var force = d3.layout.force()
	.size([width, height])
	.charge(-400)
	.chargeDistance(350)
	.linkDistance(50)
	.on("tick", tick);

var drag = force.drag()
	.on("dragstart", dragstart);

addSearchForm();
var svg = provisualizer.append("svg")
	.attr("width", "100%")
	.attr("height", "100%");

	var linkLines = svg.selectAll(".link");
	var nodeCircles = svg.selectAll("circle.node");
	var nodeLabels = svg.selectAll("text.node");

	// array of rows read from CSV (unfiltered)
	var agencyFunctionRelationships = [];

	// map from node names to node attributes
	var nodeAttributesByNodeName = {};

d3.csv(
	baseUrl + "data/nodes.csv",
	function(error, nodeAttributes) {

		nodeAttributes.forEach(
			function(nodeAttributeSet) {
				nodeAttributesByNodeName[nodeAttributeSet.FUNCTION] = nodeAttributeSet; // though we don't really need to copy the FUNCTION column
			}
		);
		// Now these node attributes can be read below
		// Note: the "FUNCTION" column is misnamed because it includes both functions AND agencies; it should be NODE.
		// Also the table should really have included a TYPE column to specify "function" or "agency"
		d3.csv(
			baseUrl + "data/agency-functions.csv",
			function(error, agencyFunctionRelationshipsCSV) {
				agencyFunctionRelationships = agencyFunctionRelationshipsCSV;
				createFilteredGraphFromLinks();
			}
		)
	}
);

function tick() {
	linkLines
		.attr("x1", function(d) { return d.source.x; })
		.attr("y1", function(d) { return d.source.y; })
		.attr("x2", function(d) { return d.target.x; })
		.attr("y2", function(d) { return d.target.y; });
	nodeLabels
		.attr("x", function(d) { return d.x; })
		.attr("y", function(d) { return d.y; });
	nodeCircles
		.attr("cx", function(d) { return d.x; })
		.attr("cy", function(d) { return d.y; });
}

function dblclick(d) {
  d3.select(this).classed("fixed", d.fixed = false);
}

function dragstart(d) {
  d3.select(this).classed("fixed", d.fixed = true);
}

function jump(d) {
	window.location = d.URL
}

function createFilteredGraphFromLinks() {
	// Extract the distinct nodes from the node relationship table.

	// arrays containing the node objects, and the link objects, for D3 force layout
	var nodes = [];
	var links = [];
	// map from node names to node array indices
	var nodeIndicesByNodeName = {};

	agencyFunctionRelationships.forEach(
		function(agencyFunctionRow) {
			var agencyPeriod = nodeAttributesByNodeName[agencyFunctionRow.AGENCY].PERIOD;
			if (
				matchesDateFilter(agencyPeriod) &&
				(matchesTextFilter(agencyFunctionRow.FUNCTION) || matchesTextFilter(agencyFunctionRow.AGENCY))
			) {

				if (! (agencyFunctionRow.AGENCY in nodeIndicesByNodeName)) {
					// no node with that name yet
					var newNodeIndex = nodes.length;
					var agencyNode =  {
						name: agencyFunctionRow.AGENCY,
						type: "agency"
					};
					var nodeAttributes = nodeAttributesByNodeName[agencyFunctionRow.AGENCY];
					for(var p in nodeAttributes) agencyNode[p]=nodeAttributes[p];
					nodes[newNodeIndex] = agencyNode;
					nodeIndicesByNodeName[agencyFunctionRow.AGENCY] = newNodeIndex;
					addNodeLabel(agencyNode, nodes, links);
				}
				if (! (agencyFunctionRow.FUNCTION in nodeIndicesByNodeName)) {
					// no node with that name yet
					var newNodeIndex = nodes.length;
					var functionNode = {
						name: agencyFunctionRow.FUNCTION,
						type: "function"
					};
					var nodeAttributes = nodeAttributesByNodeName[agencyFunctionRow.FUNCTION];
					for(var p in nodeAttributes) functionNode[p]=nodeAttributes[p];
					nodes[newNodeIndex] = functionNode;
					nodeIndicesByNodeName[agencyFunctionRow.FUNCTION] = newNodeIndex;
					addNodeLabel(functionNode, nodes, links);
				}
				links.push(
					{
						source: nodes[nodeIndicesByNodeName[agencyFunctionRow.AGENCY]],
						target: nodes[nodeIndicesByNodeName[agencyFunctionRow.FUNCTION]]
					}
				)
			}
		}
	);

	// create force layout
	force
		.nodes(nodes)
		.links(links)
		.start();

	// (re-)populate force layout

	linkLines = linkLines.data(
		links,
		function(d) {
			return d.source.name + "->" + d.target.name;
		}
	);
	linkLines.exit().remove();
	linkLines
		.enter().append("line")
		.attr("class", "link");

	nodeCircles = nodeCircles.data(
		nodes,
		function(d) {
			return d.name + d.type;
		}
	);

	nodeCircles.exit().remove();

	nodeCircles.enter()
		.append("circle")
			.attr(
				"class",
				function(n) {
					return n.type + " node";
				}
			)
			.attr(
				"title",
				function(n) {
					return n.name;
				}
			)
			.attr(
				"r",
				function(n) {
					//return 10;
					return Math.sqrt(n.weight) * 10;
				}
			)
			// opacity depends on the node degree - node with infinite degree would be solid, degree 1 would be almost transparent
			.style(
				"fill-opacity",
				function(n) {
					return 1 - (.8/ n.weight);
				}
			)
			.on("dblclick", dblclick)
			.call(drag);

	nodeLabels = nodeLabels.data(
		nodes,
		function(d) {
			return d.name + d.type;
		}
	);

	nodeLabels.exit().remove();

	nodeLabels.enter()
		.append("text")
			.attr(
				"class",
				function(n) {
					return n.type + " node label";
				}
			)
			//.attr("x", function(d) { return d.cx; })
			//.attr("y", function(d) { return d.cy; })
			.text(
				function (n) {
					if (n.name.length > maxLabelLength)
						return n.name.substring(0, maxLabelLength) + "..."
					else
						return n.name;
				}
			)
			.attr(
				"title",
				function(n) {
					return n.name;
				}
			)
			.on("click", jump);

}

// experiment to add agency and function labels as nodes in their own right, linked to the circles representing agencies and functions
function addNodeLabel(node, nodes, links) {
	return; // disabled
	// attach a new label node to the specified node
	var nodeIndex = nodes.length;
	var labelNode = {
		name: node.name,
		type: "label"
	};
	nodes[nodeIndex] = labelNode;
	links.push(
		{
			source: nodes[nodeIndex - 1],
			target: nodes[nodeIndex]
		}
	)
}

function addSearchForm() {
	// search for the word specified in the URL fragment identifier
	var searchPhrase = "road";
	var searchYear = "";
	var fragment = window.location.hash;
	if (fragment) {
		// trim the leading # and decode the fragment identifier
		var query = decodeURIComponent(fragment.substring(1));
		var delimiter = query.indexOf("\n");
		if (delimiter != -1) {
			// contains a phrase AND a year
			searchPhrase = query.substring(0, delimiter);
			searchYear = query.substring(delimiter + 1);
		} else {
			// just a phrase
			searchPhrase = query;
		}
	}
	var searchForm = provisualizer.append("form");
	var labelSearch = searchForm.append("label")
		.attr("id", "agency-or-function-name-label")
		.attr("for", "agency-or-function-name-filter")
		.text("Agency or function:");
	var textSearch = searchForm.append("input")
		.attr("id", "agency-or-function-name-filter")
		.attr("type", "text")
		.attr("size", "20")
		.property("value", searchPhrase);
	var yearLabel = searchForm.append("label")
		.attr("id", "year-label")
		.attr("for", "year-filter")
		.text("Year:");
	var yearSearch = searchForm.append("input")
		.attr("id", "year-filter")
		.attr("type", "text")
		.attr("size", "4")
		.attr("maxlength", "4")
		.property("value", searchYear);
	var submitButton = searchForm.append("input")
		.attr("id", "submit")
		.attr("type", "submit")
		.property("value", "Search");

	searchForm
		.on(
			"submit",
			function(d, i) {
				// this event is now handled
				d3.event.preventDefault();
				createFilteredGraphFromLinks();
				searchPhrase = textSearch.property("value");
				searchYear = yearSearch.property("value");
				if (searchYear == "") {
					window.location.hash = "#" + encodeURIComponent(searchPhrase);
				} else {
					window.location.hash = "#" + encodeURIComponent(searchPhrase + "\n" + searchYear);
				}
			}
		);
	return searchForm;
}

function matchesTextFilter(text) {
	var textFilter = d3.select('#agency-or-function-name-filter').property("value").toUpperCase();
	return text.toUpperCase().indexOf(textFilter) != -1
}

function matchesDateFilter(period) {
	var yearFilterText = d3.select('#year-filter').property("value");
	if (yearFilterText == "") {
		return true; // all records match a blank year filter
	}

	/* Example date ranges and their interpretations:

	ND-1851				x <= 1851
	c 1976-ct				1976 <= x <= current year
	c 1886-1905			1886 <= x <= 1905
	by 1921-by 1921		1921 <= x <= 1921 (i.e. x=1921)
	2010-2013 			2010 <= x <= 2013
	1987-by 2002			1987 <= x <= 2002
	1981-1981 - 1988	1981 <= x <= 1988
	1936-? 1994 			1936 <= x <= 1994
	? 1869-ct 				1869 <= x <= current year
	*/
	/* parsing date ranges:
	discard all chars not in "-0123456789"
	first date is text before "-"
	last date is text after final "-"
	if first date is blank, set it to "1770" (the year of Cook's first visit to Australia)
	if last date is blank, set it to the current year
	*/
	var cleanPeriod = period.replace(/[^-\d]*/g, "");
	var startYearGiven = cleanPeriod.substr(0, cleanPeriod.indexOf("-"));
	var endYearGiven = cleanPeriod.substr(1 + cleanPeriod.lastIndexOf("-"));
	var startYear, endYear;
	if (startYearGiven == "") {
		startYear = 1770;
	} else {
		startYear = Number(startYearGiven);
	}
	if (endYearGiven == "") {
		endYear = new Date().getFullYear();
	} else {
		endYear = Number(endYearGiven);
	}
	console.log(period, cleanPeriod, startYear, endYear);
	var yearFilter = Number(yearFilterText);

	return (startYear <= yearFilter) && (endYear >= yearFilter);
}
<|endoftext|>


<commit_before>const webp = require('webp-converter'); // eslint-disable-line import/no-extraneous-dependencies
const readChunk = require('read-chunk'); // eslint-disable-line import/no-extraneous-dependencies
const fileType = require('file-type'); // eslint-disable-line import/no-extraneous-dependencies
const fs = require('fs');

const imagesFolder = './static/images/posts';

fs.readdirSync(imagesFolder).forEach(file => {
  const stats = fs.lstatSync(`${imagesFolder}/${file}`);
  const isDirectory = stats.isDirectory();
  if (!isDirectory) {
    const buffer = readChunk.sync(`${imagesFolder}/${file}`, 0, 4100);
    const type = fileType(buffer);
    if (type && type.mime !== 'image/webp' && type.mime.startsWith('image/')) {
      const fileNoExtension = file.replace(type.ext, '');
      webp.cwebp(
        `${imagesFolder}/${file}`,
        `${imagesFolder}/${fileNoExtension}webp`,
        '-q 80',
        status => {
          console.log(status); // eslint-disable-line no-console
        }
      );
    }
  }
});
<commit_msg>test mime type with regex<commit_after>const webp = require('webp-converter'); // eslint-disable-line import/no-extraneous-dependencies
const readChunk = require('read-chunk'); // eslint-disable-line import/no-extraneous-dependencies
const fileType = require('file-type'); // eslint-disable-line import/no-extraneous-dependencies
const fs = require('fs');

const imagesFolder = './static/images/posts';

fs.readdirSync(imagesFolder).forEach(file => {
  const stats = fs.lstatSync(`${imagesFolder}/${file}`);
  const isDirectory = stats.isDirectory();
  if (!isDirectory) {
    const buffer = readChunk.sync(`${imagesFolder}/${file}`, 0, 4100);
    const type = fileType(buffer);
    if (type && /image\/(?!webp)/.test(type.mime)) {
      const fileNoExtension = file.replace(type.ext, '');
      webp.cwebp(
        `${imagesFolder}/${file}`,
        `${imagesFolder}/${fileNoExtension}webp`,
        '-q 80',
        status => {
          console.log(status); // eslint-disable-line no-console
        }
      );
    }
  }
});
<|endoftext|>


<commit_before>(function () {
  /*
   Constants
   */

  // Client variable which may be present containing options to override with
  var OPTIONS_VARIABLE = 'cookieconsent_options';

  // Name of cookie to be set when dismissed
  var DISMISSED_COOKIE = 'cookieconsent_dismissed';

  // The path to built in themes (s3 bucket)
  var THEME_BUCKET_PATH = 'http://cc.silktide.com/';

  // No point going further if they've already dismissed.
  if (document.cookie.indexOf(DISMISSED_COOKIE) > -1) {
    return;
  }

  // IE8...
  if(typeof String.prototype.trim !== 'function') {
    String.prototype.trim = function() {
      return this.replace(/^\s+|\s+$/g, '');
    };
  }

  /*
   Helper methods
   */
  var Util = {
    isArray: function (obj) {
      var proto = Object.prototype.toString.call(obj);
      return proto == '[object Array]';
    },

    isObject: function (obj) {
      return Object.prototype.toString.call(obj) == '[object Object]';
    },

    each: function (arr, callback, /* optional: */context, force) {
      if (Util.isObject(arr) && !force) {
        for (var key in arr) {
          if (arr.hasOwnProperty(key)) {
            callback.call(context, arr[key], key, arr);
          }
        }
      } else {
        for (var i = 0, ii = arr.length; i < ii; i++) {
          callback.call(context, arr[i], i, arr);
        }
      }
    },

    merge: function (obj1, obj2) {
      if (!obj1) return;
      Util.each(obj2, function (val, key) {
        if (Util.isObject(val) && Util.isObject(obj1[key])) {
          Util.merge(obj1[key], val);
        } else {
          obj1[key] = val;
        }
      })
    },

    bind: function (func, context) {
      return function () {
        return func.apply(context, arguments);
      };
    },

    /*
     find a property based on a . separated path.
     i.e. queryObject({details: {name: 'Adam'}}, 'details.name') // -> 'Adam'
     returns null if not found
     */
    queryObject: function (object, query) {
      var queryPart;
      var i = 0;
      var head = object;
      query = query.split('.');
      while ( (queryPart = query[i++]) && head.hasOwnProperty(queryPart) && (head = head[queryPart]) )  {
        if (i === query.length) return head;
      }
      return null;
    },

    setCookie: function (name, value, expirydays) {
      var exdate = new Date();
      expirydays = expirydays || 365;
      exdate.setDate(exdate.getDate() + expirydays);
      document.cookie = name + '=' + value + '; expires=' + exdate.toUTCString() + '; path=/'
    },

    addEventListener: function (el, event, eventListener) {
      if (el.addEventListener) {
        el.addEventListener(event, eventListener);
      } else {
        el.attachEvent('on' + event, eventListener);
      }
    }
  };

  var DomBuilder = (function () {
    /*
     The attribute we store events in.
     */
    var eventAttribute = 'data-cc-event';
    var conditionAttribute = 'data-cc-if';

    /*
     Shim to make addEventListener work correctly with IE.
     */
    var addEventListener = function (el, event, eventListener) {
      // Add multiple event listeners at once if array is passed.
      if (Util.isArray(event)) {
        return Util.each(event, function (ev) {
          addEventListener(el, ev, eventListener);
        });
      }

      if (el.addEventListener) {
        el.addEventListener(event, eventListener);
      } else {
        el.attachEvent('on' + event, eventListener);
      }
    };

    /*
     Replace {{variable.name}} with it's property on the scope
     Also supports {{variable.name || another.name || 'string'}}
     */
    var insertReplacements = function (htmlStr, scope) {
      return htmlStr.replace(/\{\{(.*?)\}\}/g, function (_match, sub) {
        var tokens = sub.split('||');
        var value;
        while (token = tokens.shift()) {
          token = token.trim();

          // If string
          if (token[0] === '"') return token.slice(1, token.length - 1);

          // If query matches
          value =  Util.queryObject(scope, token);

          if (value) return value;
        }

        return '';
      });
    };

    /*
     Turn a string of html into DOM
     */
    var buildDom = function (htmlStr) {
      var container = document.createElement('div');
      container.innerHTML = htmlStr;
      return container.children[0];
    };

    var applyToElementsWithAttribute = function (dom, attribute, func) {
      var els = dom.parentNode.querySelectorAll('[' + attribute + ']');
      Util.each(els, function (element) {
        var attributeVal = element.getAttribute(attribute);
        func(element, attributeVal);
      }, window, true);
    };

    /*
     Parse event attributes in dom and set listeners to their matching scope methods
     */
    var applyEvents = function (dom, scope) {
      applyToElementsWithAttribute(dom, eventAttribute, function (element, attributeVal) {
        var parts = attributeVal.split(':');
        var listener = Util.queryObject(scope, parts[1]);
        addEventListener(element, parts[0], Util.bind(listener, scope));
      });
    };

    var applyConditionals = function (dom, scope) {
      applyToElementsWithAttribute(dom, conditionAttribute, function (element, attributeVal) {
        var value = Util.queryObject(scope, attributeVal);
        if (!value) {
          element.parentNode.removeChild(element);
        }
      });
    };

    return {
      build: function (htmlStr, scope) {
        if (Util.isArray(htmlStr)) htmlStr = htmlStr.join('');

        htmlStr = insertReplacements(htmlStr, scope);
        var dom = buildDom(htmlStr);
        applyEvents(dom, scope);
        applyConditionals(dom, scope);

        return dom;
      }
    };
  })();

  /*
   Plugin
   */
  var cookieconsent = {
    options: {
      message: 'This website uses cookies to ensure you get the best experience on our website. ',
      dismiss: 'Got it!',
      learnMore: 'More info',
      link: null,
      container: null, // selector
      theme: 'light-floating'
    },

    init: function () {
      var options = window[OPTIONS_VARIABLE];
      if (options) this.setOptions(options);

      this.setContainer();

      // Calls render when theme is loaded.
      this.loadTheme(this.render);
    },

    setOptions: function (options) {
      Util.merge(this.options, options);
    },

    setContainer: function () {
      if (this.options.container) {
        this.container = document.querySelector(this.options.container);
      } else {
        this.container = document.body;
      }

      // Add class to container classes so we can specify css for IE8 only.
      this.containerClasses = '';
      if (navigator.appVersion.indexOf('MSIE 8') > -1) {
        this.containerClasses += ' cc_ie8'
      }
    },

    loadTheme: function (callback) {
      var theme = this.options.theme;

      // If theme is specified by name
      if (theme.indexOf('.css') === -1) {
        theme = THEME_BUCKET_PATH + theme + '.css';
      }

      var link = document.createElement('link');
      link.rel = 'stylesheet';
      link.type = 'text/css';
      link.href = theme;

      var loaded = false;
      link.onload = Util.bind(function () {
        if (!loaded && callback) {
          callback.call(this);
          loaded = true;
        }
      }, this);

      document.getElementsByTagName("head")[0].appendChild(link);
    },

    markup: [
      '
',
      '
',
      '{{options.dismiss}}',

      '
{{options.message}} {{options.learnMore}}
',

      'Cookie Consent plugin for the EU cookie law',
      '
',
      '
'
    ],

    render: function () {
      this.element = DomBuilder.build(this.markup, this);
      if (!this.container.firstChild) {
        this.container.appendChild(this.element);
      } else {
        this.container.insertBefore(this.element, this.container.firstChild);
      }
    },

    dismiss: function () {
      this.setDismissedCookie();
      this.container.removeChild(this.element);
    },

    setDismissedCookie: function () {
      Util.setCookie(DISMISSED_COOKIE, 'yes');
    }
  };

  var init;
  var initialized = false;
  (init = function () {
    if (!initialized && document.readyState == 'complete') {
      cookieconsent.init();
      initalized = true;
    }
  })();

  Util.addEventListener(document, 'readystatechange', init);

})();<commit_msg>Support for theme false;<commit_after>(function () {
  /*
   Constants
   */

  // Client variable which may be present containing options to override with
  var OPTIONS_VARIABLE = 'cookieconsent_options';

  // Name of cookie to be set when dismissed
  var DISMISSED_COOKIE = 'cookieconsent_dismissed';

  // The path to built in themes (s3 bucket)
  var THEME_BUCKET_PATH = 'http://cc.silktide.com/';

  // No point going further if they've already dismissed.
  if (document.cookie.indexOf(DISMISSED_COOKIE) > -1) {
    return;
  }

  // IE8...
  if(typeof String.prototype.trim !== 'function') {
    String.prototype.trim = function() {
      return this.replace(/^\s+|\s+$/g, '');
    };
  }

  /*
   Helper methods
   */
  var Util = {
    isArray: function (obj) {
      var proto = Object.prototype.toString.call(obj);
      return proto == '[object Array]';
    },

    isObject: function (obj) {
      return Object.prototype.toString.call(obj) == '[object Object]';
    },

    each: function (arr, callback, /* optional: */context, force) {
      if (Util.isObject(arr) && !force) {
        for (var key in arr) {
          if (arr.hasOwnProperty(key)) {
            callback.call(context, arr[key], key, arr);
          }
        }
      } else {
        for (var i = 0, ii = arr.length; i < ii; i++) {
          callback.call(context, arr[i], i, arr);
        }
      }
    },

    merge: function (obj1, obj2) {
      if (!obj1) return;
      Util.each(obj2, function (val, key) {
        if (Util.isObject(val) && Util.isObject(obj1[key])) {
          Util.merge(obj1[key], val);
        } else {
          obj1[key] = val;
        }
      })
    },

    bind: function (func, context) {
      return function () {
        return func.apply(context, arguments);
      };
    },

    /*
     find a property based on a . separated path.
     i.e. queryObject({details: {name: 'Adam'}}, 'details.name') // -> 'Adam'
     returns null if not found
     */
    queryObject: function (object, query) {
      var queryPart;
      var i = 0;
      var head = object;
      query = query.split('.');
      while ( (queryPart = query[i++]) && head.hasOwnProperty(queryPart) && (head = head[queryPart]) )  {
        if (i === query.length) return head;
      }
      return null;
    },

    setCookie: function (name, value, expirydays) {
      var exdate = new Date();
      expirydays = expirydays || 365;
      exdate.setDate(exdate.getDate() + expirydays);
      document.cookie = name + '=' + value + '; expires=' + exdate.toUTCString() + '; path=/'
    },

    addEventListener: function (el, event, eventListener) {
      if (el.addEventListener) {
        el.addEventListener(event, eventListener);
      } else {
        el.attachEvent('on' + event, eventListener);
      }
    }
  };

  var DomBuilder = (function () {
    /*
     The attribute we store events in.
     */
    var eventAttribute = 'data-cc-event';
    var conditionAttribute = 'data-cc-if';

    /*
     Shim to make addEventListener work correctly with IE.
     */
    var addEventListener = function (el, event, eventListener) {
      // Add multiple event listeners at once if array is passed.
      if (Util.isArray(event)) {
        return Util.each(event, function (ev) {
          addEventListener(el, ev, eventListener);
        });
      }

      if (el.addEventListener) {
        el.addEventListener(event, eventListener);
      } else {
        el.attachEvent('on' + event, eventListener);
      }
    };

    /*
     Replace {{variable.name}} with it's property on the scope
     Also supports {{variable.name || another.name || 'string'}}
     */
    var insertReplacements = function (htmlStr, scope) {
      return htmlStr.replace(/\{\{(.*?)\}\}/g, function (_match, sub) {
        var tokens = sub.split('||');
        var value;
        while (token = tokens.shift()) {
          token = token.trim();

          // If string
          if (token[0] === '"') return token.slice(1, token.length - 1);

          // If query matches
          value =  Util.queryObject(scope, token);

          if (value) return value;
        }

        return '';
      });
    };

    /*
     Turn a string of html into DOM
     */
    var buildDom = function (htmlStr) {
      var container = document.createElement('div');
      container.innerHTML = htmlStr;
      return container.children[0];
    };

    var applyToElementsWithAttribute = function (dom, attribute, func) {
      var els = dom.parentNode.querySelectorAll('[' + attribute + ']');
      Util.each(els, function (element) {
        var attributeVal = element.getAttribute(attribute);
        func(element, attributeVal);
      }, window, true);
    };

    /*
     Parse event attributes in dom and set listeners to their matching scope methods
     */
    var applyEvents = function (dom, scope) {
      applyToElementsWithAttribute(dom, eventAttribute, function (element, attributeVal) {
        var parts = attributeVal.split(':');
        var listener = Util.queryObject(scope, parts[1]);
        addEventListener(element, parts[0], Util.bind(listener, scope));
      });
    };

    var applyConditionals = function (dom, scope) {
      applyToElementsWithAttribute(dom, conditionAttribute, function (element, attributeVal) {
        var value = Util.queryObject(scope, attributeVal);
        if (!value) {
          element.parentNode.removeChild(element);
        }
      });
    };

    return {
      build: function (htmlStr, scope) {
        if (Util.isArray(htmlStr)) htmlStr = htmlStr.join('');

        htmlStr = insertReplacements(htmlStr, scope);
        var dom = buildDom(htmlStr);
        applyEvents(dom, scope);
        applyConditionals(dom, scope);

        return dom;
      }
    };
  })();

  /*
   Plugin
   */
  var cookieconsent = {
    options: {
      message: 'This website uses cookies to ensure you get the best experience on our website. ',
      dismiss: 'Got it!',
      learnMore: 'More info',
      link: null,
      container: null, // selector
      theme: 'light-floating'
    },

    init: function () {
      var options = window[OPTIONS_VARIABLE];
      if (options) this.setOptions(options);

      this.setContainer();

      // Calls render when theme is loaded.
      if (this.options.theme) {
        this.loadTheme(this.render);
      } else {
        this.render();
      }
    },

    setOptions: function (options) {
      Util.merge(this.options, options);
    },

    setContainer: function () {
      if (this.options.container) {
        this.container = document.querySelector(this.options.container);
      } else {
        this.container = document.body;
      }

      // Add class to container classes so we can specify css for IE8 only.
      this.containerClasses = '';
      if (navigator.appVersion.indexOf('MSIE 8') > -1) {
        this.containerClasses += ' cc_ie8'
      }
    },

    loadTheme: function (callback) {
      var theme = this.options.theme;

      // If theme is specified by name
      if (theme.indexOf('.css') === -1) {
        theme = THEME_BUCKET_PATH + theme + '.css';
      }

      var link = document.createElement('link');
      link.rel = 'stylesheet';
      link.type = 'text/css';
      link.href = theme;

      var loaded = false;
      link.onload = Util.bind(function () {
        if (!loaded && callback) {
          callback.call(this);
          loaded = true;
        }
      }, this);

      document.getElementsByTagName("head")[0].appendChild(link);
    },

    markup: [
      '
',
      '
',
      '{{options.dismiss}}',

      '
{{options.message}} {{options.learnMore}}
',

      'Cookie Consent plugin for the EU cookie law',
      '
',
      '
'
    ],

    render: function () {
      this.element = DomBuilder.build(this.markup, this);
      if (!this.container.firstChild) {
        this.container.appendChild(this.element);
      } else {
        this.container.insertBefore(this.element, this.container.firstChild);
      }
    },

    dismiss: function () {
      this.setDismissedCookie();
      this.container.removeChild(this.element);
    },

    setDismissedCookie: function () {
      Util.setCookie(DISMISSED_COOKIE, 'yes');
    }
  };

  var init;
  var initialized = false;
  (init = function () {
    if (!initialized && document.readyState == 'complete') {
      cookieconsent.init();
      initalized = true;
    }
  })();

  Util.addEventListener(document, 'readystatechange', init);

})();
<|endoftext|>


<commit_before>var path = require('path');
var webpack = require('webpack');

var config = require('./config');

// webpack.config.js
module.exports = {
  entry: {
    hello: path.join(config.src, 'components', 'hello', 'index.vue'),
    // Don't touch me - components

    demo: path.join(config.src, 'directives', 'demo', 'index.js')
    // Don't touch me - directives
  },

  output: {
    path: config.dist,
    filename: '[name].js',
    sourceMapFilename: '[file].map',
    library: '[name]',
    libraryTarget: 'umd',
    umdNamedDefine: true
  },

  externals: {
    'vue': {
      root: 'Vue',
      commonjs2: 'vue',
      commonjs: 'vue',
      amd: 'vue'
    },
    'vue-resource': {
      root: 'VueResource',
      commonjs2: 'vue-resource',
      commonjs: 'vue-resource',
      amd: 'vue-resource'
    }
  },

  module: {
    loaders: [
      {
        test: /\.vue$/,
        loader: 'vue',
        include: [config.src]
      },
      {
        test: /\.js$/,
        loader: 'babel!eslint',
        include: [config.src].concat(config.babelModules)
      },
      {
        // edit this for additional asset file types
        test: /\.(png|jpg|gif)$/,
        loader: 'url',
        query: {
          // inline files smaller then 10kb as base64 dataURL
          limit: 10000,
          // fallback to file-loader with this naming scheme
          name: 'img/[name].[ext]'
        },
        include: [config.src]
      }
    ]
  },

  vue: {
    loaders: {
      js: 'babel!eslint'
    }
  },
  babel: {
    presets: ['es2015'],
    plugins: ['transform-runtime']
  }
};
<commit_msg>update<commit_after>var path = require('path');
var webpack = require('webpack');

var config = require('./config');

// webpack.config.js
module.exports = {
  entry: {
    hello: path.join(config.src, 'components', 'hello', 'index.vue'),
    // Don't touch me - components

    demo: path.join(config.src, 'directives', 'demo', 'index.js'),
    // Don't touch me - directives
  },

  output: {
    path: config.dist,
    filename: '[name].js',
    sourceMapFilename: '[file].map',
    library: '[name]',
    libraryTarget: 'umd',
    umdNamedDefine: true
  },

  externals: {
    'vue': {
      root: 'Vue',
      commonjs2: 'vue',
      commonjs: 'vue',
      amd: 'vue'
    },
    'vue-resource': {
      root: 'VueResource',
      commonjs2: 'vue-resource',
      commonjs: 'vue-resource',
      amd: 'vue-resource'
    }
  },

  module: {
    loaders: [
      {
        test: /\.vue$/,
        loader: 'vue',
        include: [config.src]
      },
      {
        test: /\.js$/,
        loader: 'babel!eslint',
        include: [config.src].concat(config.babelModules)
      },
      {
        // edit this for additional asset file types
        test: /\.(png|jpg|gif)$/,
        loader: 'url',
        query: {
          // inline files smaller then 10kb as base64 dataURL
          limit: 10000,
          // fallback to file-loader with this naming scheme
          name: 'img/[name].[ext]'
        },
        include: [config.src]
      }
    ]
  },

  vue: {
    loaders: {
      js: 'babel!eslint'
    }
  },
  babel: {
    presets: ['es2015'],
    plugins: ['transform-runtime']
  }
};
<|endoftext|>


<commit_before>var babel = require('rollup-plugin-babel');
var commonjs = require('rollup-plugin-commonjs');
var resolve = require('rollup-plugin-node-resolve');

module.exports = (config) => {
  if ( ! process.env.SAUCE_USERNAME || ! process.env.SAUCE_ACCESS_KEY) {
    console.error('Make sure the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables are set.');
    process.exit(1);
  }

  var customLaunchers = {
    'chrome-1': {
      base: 'SauceLabs',
      browserName: 'chrome',
      platform: 'macOS 10.14',
      version: 'latest-1'
    },
    'firefox': {
      base: 'SauceLabs',
      browserName: 'firefox',
      platform: 'Windows 10',
      version: 'latest'
    },
    'firefox-1': {
      base: 'SauceLabs',
      browserName: 'firefox',
      platform: 'macOS 10.14',
      version: 'latest-1'
    },

    'edge': {
      base: 'SauceLabs',
      browserName: 'MicrosoftEdge',
      platform: 'Windows 10',
      version: 'latest'
    },
    'edge-1': {
      base: 'SauceLabs',
      browserName: 'MicrosoftEdge',
      platform: 'Windows 10',
      version: 'latest-1'
    },

    'safari12': {
      base: 'SauceLabs',
      browserName: 'safari',
      platform: 'macOS 10.14',
      version: '12.0'
    },
    'safari13': {
      base: 'SauceLabs',
      browserName: 'safari',
      platform: 'macOS 10.13',
      version: '13.0'
    },

    'ios11': {
      base: 'SauceLabs',
      browserName: 'Safari',
      platformName: 'iOS',
      platformVersion: '11.3',
      deviceName: 'iPhone X Simulator',
      idleTimeout: 120
    },
    'ios12': {
      base: 'SauceLabs',
      browserName: 'Safari',
      platformName: 'iOS',
      platformVersion: '12.2',
      deviceName: 'iPhone XR Simulator',
      idleTimeout: 120
    },

    'android6': {
      base: 'SauceLabs',
      browserName: 'Chrome',
      platformName: 'Android',
      platformVersion: '6.0',
      deviceName: 'Android GoogleAPI Emulator'
    },
    'android8': {
      base: 'SauceLabs',
      browserName: 'Chrome',
      platformName: 'Android',
      platformVersion: '8.0',
      deviceName: 'Android GoogleAPI Emulator'
    }
  };

  config.set({
    basePath: '',
    frameworks: ['mocha'],
    files: [
      'https://unpkg.com/nette-forms/src/assets/netteForms.js',
      'tests/index.js'
    ],

    preprocessors: {
      'tests/index.js': ['rollup']
    },
    rollupPreprocessor: {
      plugins: [
        babel({
          exclude: /node_modules\/(?!event-target-shim|sinon)/,
          runtimeHelpers: true,
        }),
        resolve(),
        commonjs({
          namedExports: {
            'node_modules/chai/index.js': ['assert'],
          },
        }),
      ],
      output: {
        name: 'naja',
        format: 'iife',
        sourcemap: 'inline',
      },
    },

    reporters: ['dots', 'saucelabs'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: false,
    singleRun: true,

    sauceLabs: {
      testName: 'Naja.js',
      build: process.env.TRAVIS_BUILD_NUMBER
    },

    customLaunchers: customLaunchers,
    browsers: Object.keys(customLaunchers),
    browserDisconnectTolerance: 2,
    browserNoActivityTimeout: 120000,
    captureTimeout: 120000, // try to give ios simulators some time to boot up
    concurrency: 5,
  });
};
<commit_msg>chore: update tested browsers (Safari and Android)<commit_after>var babel = require('rollup-plugin-babel');
var commonjs = require('rollup-plugin-commonjs');
var resolve = require('rollup-plugin-node-resolve');

module.exports = (config) => {
  if ( ! process.env.SAUCE_USERNAME || ! process.env.SAUCE_ACCESS_KEY) {
    console.error('Make sure the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables are set.');
    process.exit(1);
  }

  var customLaunchers = {
    'chrome-1': {
      base: 'SauceLabs',
      browserName: 'chrome',
      platform: 'macOS 10.15',
      version: 'latest-1'
    },
    'firefox': {
      base: 'SauceLabs',
      browserName: 'firefox',
      platform: 'Windows 10',
      version: 'latest'
    },
    'firefox-1': {
      base: 'SauceLabs',
      browserName: 'firefox',
      platform: 'macOS 10.15',
      version: 'latest-1'
    },

    'edge': {
      base: 'SauceLabs',
      browserName: 'MicrosoftEdge',
      platform: 'Windows 10',
      version: 'latest'
    },
    'edge-1': {
      base: 'SauceLabs',
      browserName: 'MicrosoftEdge',
      platform: 'Windows 10',
      version: 'latest-1'
    },

    'safari12': {
      base: 'SauceLabs',
      browserName: 'safari',
      platform: 'macOS 10.14',
      version: '12.0'
    },
    'safari13': {
      base: 'SauceLabs',
      browserName: 'safari',
      platform: 'macOS 10.15',
      version: '13.0'
    },

    'ios12': {
      base: 'SauceLabs',
      browserName: 'Safari',
      platformName: 'iOS',
      platformVersion: '12.4',
      deviceName: 'iPhone XR Simulator',
      idleTimeout: 120
    },
    'ios13': {
      base: 'SauceLabs',
      browserName: 'Safari',
      platformName: 'iOS',
      platformVersion: '13.2',
      deviceName: 'iPhone 11 Simulator',
      idleTimeout: 120
    },

    'android6': {
      base: 'SauceLabs',
      browserName: 'Chrome',
      platformName: 'Android',
      platformVersion: '6.0',
      deviceName: 'Android GoogleAPI Emulator'
    },
    'android10': {
      base: 'SauceLabs',
      browserName: 'Chrome',
      platformName: 'Android',
      platformVersion: '10.0',
      deviceName: 'Android GoogleAPI Emulator'
    }
  };

  config.set({
    basePath: '',
    frameworks: ['mocha'],
    files: [
      'https://unpkg.com/nette-forms/src/assets/netteForms.js',
      'tests/index.js'
    ],

    preprocessors: {
      'tests/index.js': ['rollup']
    },
    rollupPreprocessor: {
      plugins: [
        babel({
          exclude: /node_modules\/(?!event-target-shim|sinon)/,
          runtimeHelpers: true,
        }),
        resolve(),
        commonjs({
          namedExports: {
            'node_modules/chai/index.js': ['assert'],
          },
        }),
      ],
      output: {
        name: 'naja',
        format: 'iife',
        sourcemap: 'inline',
      },
    },

    reporters: ['dots', 'saucelabs'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: false,
    singleRun: true,

    sauceLabs: {
      testName: 'Naja.js',
      build: process.env.TRAVIS_BUILD_NUMBER
    },

    customLaunchers: customLaunchers,
    browsers: Object.keys(customLaunchers),
    browserDisconnectTolerance: 2,
    browserNoActivityTimeout: 120000,
    captureTimeout: 120000, // try to give ios simulators some time to boot up
    concurrency: 5,
  });
};
<|endoftext|>


<commit_before>// Copyright Adrian Ottiker. All Rights Reserved.

/**
 * @fileoverview N-client: build and render components.
 * @author adi@ottiker.com (Adrian Ottiker)
 */

// extend Object prototype with clone function
/*Object.defineProperty(Object.prototype, "clone", {

    //writeable: false, (default value)
    //enumerable: false, (default value)
    //configurable: false, (default value)
    value: function(){

        function O(){}
        O.prototype = this;
        return new O();
    }
});*/

// TODO send error to server
window.onerror = function(error, url, line) {

    //console.log( error + "\n" + url + "\n" + line );
};

//reset session store
if (!window.name || window.name == "undefined") {

    window.name = "";
};

/**
 * Main Namespace
 * @type {class}
 */
var N = {

    clone: function(obj){

        function O(){}
        O.prototype = obj;
        return new O();
    },

    err: function(msg) {

        throw new Error(msg);
    },

    // !------------------------------ N.obs
    /**
     * simple observer class
     * @retrun {Observer} instance of the observer Class
     */
    obs: (function(){

        //Class
        var Observer = {

            //Listen to an Event
            l: function(name, method) {

                var events = this.e;

                if (typeof method == 'function') {

                    if (!events[name]) {

                        events[name] = [];
                    }

                    events[name].push(method);
                }
            },

            //Fire Event
            f: function(name) {

                var events = this.e[name];

                //Fire registred Methods
                if (events) {

                    //slice first argument and apply the others to the callback function
                    var args = Array.prototype.slice.call(arguments).slice(1);

                    for (var i = 0, l = events.length; i < l; ++i) {

                        if (events[i]) {

                            events[i].apply(null, args);
                        }
                    }
                }
            },

            //Remove Event
            r: function(event, handler) {

                if (this.e[event]) {

                    if (handler) {

                        for (var i = 0, l = this.e[event].length; i < l; ++i) {

                            if (this.e[event][i] == handler) {

                                this.e[event][i] = null;
                            }
                        }
                    }
                    else {

                        delete this.e[event];
                    }
                }
            }
        },

        //obeserver chache
        observers = {};

        //return existing observer or return new Observer
        return function(name) {

            if (observers[name]) {

                return observers[name];
            }
            else {

                var obs = N.clone(Observer);
                obs.e = {};

                return name ? observers[name] = obs : obs;
            }
        };
    })(),

    // !------------------------------ N.link
    /**
     * handles asynchronous/synchronous binary or text-based communication
     * Returns a configured Link
     * @public
     * @param {object} configuration options for the link
     * @param {function} callback function
     * @return {XMLHttpRequest}
     *
     * options: {
     *
     *        path:     {string}    the url
     *        data:     {object}    post data
     *        sync:     {boolean}   if true the request will block all other browser actions
     *        upload:   {function}  first argument: percent of loaded data, second argument: XMLHttpRequestProgressEvent
     *        download: {function}  first argument: percent of loaded data, second argument: XMLHttpRequestProgressEvent
     * }
     */

    link: function(method, options, callback) {

        if (typeof method !== "string") {

            N.err("Operation Method is mandatory");
        }

        if (typeof options === "function") {

            callback = options;
            options = {};
        }

        if (!options.miid && !this.miid) {

            N.err("Module Instance ID (miid) is mandatory");
        }

        var link = new XMLHttpRequest(); //create new link

        if (link) {

            //open the connection
            link.open(options.data ? "post" : "get", N.ok + "/" + (this.miid || options.miid) + "/" + method + "/" + (options.path || ""), !options.sync);

            //set session id in http header
            if (window.name) {

                link.setRequestHeader("x-sid", window.name);
            }

            //handle data
            if (options.data && !(typeof FormData !== "undefined" && data instanceof FormData)) {

                try {

                    //set content-type header to application/json
                    link.setRequestHeader("content-type", "application/json");

                    //stringify object to JSON
                    options.data = JSON.stringify(options.data);

                } catch(err) {

                    //abort request
                    link.abort();

                    //fire callback with error
                    if (callback) {

                        N.err(err);
                    }

                    //exit function
                    return;
                }
            }

            //attach callback to upload progress event
            if (link.upload && options.upload) {

                link.upload.onprogress = options.upload;
            }

            //attach callback to download progress event
            if (options.downlaod) {

                link.onprogress = options.download;
            }

            //request complete callback
            link.onreadystatechange = function() {

                //check if request is complete
                if (link.readyState == 4) {

                    //get error message
                    var err = link.A ? "A" : link.status < 400 ? null : link.responseText || "E",
                        sid = link.getResponseHeader("x-sid");

                    //reset abort status
                    link.A = 0;

                    //set/reset session id on client
                    if (sid) {

                        window.name = sid == "N" ? "" : sid;
                    }

                    if (callback) {

                        var response = null;

                        if (!err) {

                            //parse result as JSON
                            if ((link.getResponseHeader("content-type") || "").indexOf("application/json") > -1) {

                                try {

                                    response = JSON.parse(link.responseText);
                                }
                                catch (e) {

                                    err = e;
                                }
                            }
                            else {

                                response = link.responseText;
                            }
                        }

                        //fire callback
                        callback(err, response, link);
                    }
                }
            };

            //send data
            link.send(options.data);

            return function() {

                link.A = 1;
                link.abort();
            };
        }
    },

    // !------------------------------ N.css
    /**
     * Load css Files
     * @param {string} url to css file
     */
    css: function(file) {

        //create link and append it to the DOM
        var head = document.getElementsByTagName("head")[0],
            link = document.createElement("link"),
            attr = {
                rel:    "stylesheet",
                type:   "text/css",
                href:   N.ok + "/core/getFile/" + file
            };

        for (var name in attr) {

            link.setAttribute(name, attr[name]);
        }

        head.appendChild(link);
    },

    // !------------------------------ N.comp
    /**
     * Create Instances of Modules
     */
    mod: function(target, miid, callback) {

        //default argument values
        callback = (typeof callback == "function" ? callback : function() {});
        target = (typeof target === "string" ? document.getElementById(target) : target);

        //error checks
        if (!target) {
            return callback("Target not found or undefined.");
        }
        if (!miid) {
            return callback("Component ID undefined.");
        }

        //get module
        N.link("getConfig", {miid: "core", path: miid}, function(err, response) {

            //error checks
            if (err || !response) {

                return callback(err || "Empty response");
            }

            target.style.display = "none";

            var div = document.createElement("div");

            //add miid to html
            div.setAttribute("id", miid);

            //TODO show loader

            // render html from response[1]
            if (response[1]) {
                div.innerHTML = response[1];
            }

            //append div to dom
            target.appendChild(div);

            // load css from response[2]
            for (var i in response[2]) {
                N.css(response[2][i]);
            }

            // TODO handle requirejs errors
            /*require.onError = function(){

                target.style.display = "block";
                callback(null, clone);
            };*/

            // load, clone and init modules
            require([response[0].owner + "/" + response[0].name + "/main"], function(module) {

                var clone = N.clone(module);

                clone.miid = miid;
                clone.module = module;
                clone.$ = target;
                clone.link = N.link;

                // TODO create observer with user defined id (only for GUI)
                clone.obs = N.obs(miid);

                // TODO register module state

                // init module
                if (clone.init) {
                    clone.init(response[0]);
                }

                // TODO: hide loader
                // TODO: init state

                target.style.display = "block";

                callback(null, clone);
            });
        });
    },

    state: (function() {

        /*
            1. register all module states to N.states or whatever..
            2. onchange go through all module states
            3. handle browser history

            every module has his own state map object.
            listen to onhashchange.

            create a api function to change hashes?

            example of a map = {

                '/path/x': {

                    //activate this state if dom event was fired
                    events: [

                        ["#selector", "click"]
                    ],

                    //execute methods if state is activated
                    methods: [

                        ["getItems", ["param1", "param2"]]
                    ],

                    //activate viewstates if state is activated
                    viewStates: ["listEmpty"]
                }
            }
        */

        var states = {

            /*URI: [state]*/
        };

        return function() {

            //events
            //methods
            //viewstates
        };
    })()
};<commit_msg>fixed bug in N.link<commit_after>// Copyright Adrian Ottiker. All Rights Reserved.

/**
 * @fileoverview N-client: build and render components.
 * @author adi@ottiker.com (Adrian Ottiker)
 */

// extend Object prototype with clone function
/*Object.defineProperty(Object.prototype, "clone", {

    //writeable: false, (default value)
    //enumerable: false, (default value)
    //configurable: false, (default value)
    value: function(){

        function O(){}
        O.prototype = this;
        return new O();
    }
});*/

// TODO send error to server
window.onerror = function(error, url, line) {

    //console.log( error + "\n" + url + "\n" + line );
};

//reset session store
if (!window.name || window.name == "undefined") {

    window.name = "";
};

/**
 * Main Namespace
 * @type {class}
 */
var N = {

    clone: function(obj){

        function O(){}
        O.prototype = obj;
        return new O();
    },

    err: function(msg) {

        throw new Error(msg);
    },

    // !------------------------------ N.obs
    /**
     * simple observer class
     * @retrun {Observer} instance of the observer Class
     */
    obs: (function(){

        //Class
        var Observer = {

            //Listen to an Event
            l: function(name, method) {

                var events = this.e;

                if (typeof method == 'function') {

                    if (!events[name]) {

                        events[name] = [];
                    }

                    events[name].push(method);
                }
            },

            //Fire Event
            f: function(name) {

                var events = this.e[name];

                //Fire registred Methods
                if (events) {

                    //slice first argument and apply the others to the callback function
                    var args = Array.prototype.slice.call(arguments).slice(1);

                    for (var i = 0, l = events.length; i < l; ++i) {

                        if (events[i]) {

                            events[i].apply(null, args);
                        }
                    }
                }
            },

            //Remove Event
            r: function(event, handler) {

                if (this.e[event]) {

                    if (handler) {

                        for (var i = 0, l = this.e[event].length; i < l; ++i) {

                            if (this.e[event][i] == handler) {

                                this.e[event][i] = null;
                            }
                        }
                    }
                    else {

                        delete this.e[event];
                    }
                }
            }
        },

        //obeserver chache
        observers = {};

        //return existing observer or return new Observer
        return function(name) {

            if (observers[name]) {

                return observers[name];
            }
            else {

                var obs = N.clone(Observer);
                obs.e = {};

                return name ? observers[name] = obs : obs;
            }
        };
    })(),

    // !------------------------------ N.link
    /**
     * handles asynchronous/synchronous binary or text-based communication
     * Returns a configured Link
     * @public
     * @param {object} configuration options for the link
     * @param {function} callback function
     * @return {XMLHttpRequest}
     *
     * options: {
     *
     *        path:     {string}    the url
     *        data:     {object}    post data
     *        sync:     {boolean}   if true the request will block all other browser actions
     *        upload:   {function}  first argument: percent of loaded data, second argument: XMLHttpRequestProgressEvent
     *        download: {function}  first argument: percent of loaded data, second argument: XMLHttpRequestProgressEvent
     * }
     */

    link: function(method, options, callback) {

        if (typeof method !== "string") {

            N.err("Operation Method is mandatory");
        }

        if (typeof options === "function") {

            callback = options;
            options = {};
        }

        if (!options.miid && !this.miid) {

            N.err("Module Instance ID (miid) is mandatory");
        }

        var link = new XMLHttpRequest(); //create new link

        if (link) {

            //open the connection
            link.open(options.data ? "post" : "get", N.ok + "/" + (this.miid || options.miid) + "/" + method + "/" + (options.path || ""), !options.sync);

            //set session id in http header
            if (window.name) {

                link.setRequestHeader("x-sid", window.name);
            }

            //handle data
            if (options.data && !(typeof FormData !== "undefined" && options.data instanceof FormData)) {

                try {

                    //set content-type header to application/json
                    link.setRequestHeader("content-type", "application/json");

                    //stringify object to JSON
                    options.data = JSON.stringify(options.data);

                } catch(err) {

                    //abort request
                    link.abort();

                    //fire callback with error
                    if (callback) {

                        N.err(err);
                    }

                    //exit function
                    return;
                }
            }

            //attach callback to upload progress event
            if (link.upload && options.upload) {

                link.upload.onprogress = options.upload;
            }

            //attach callback to download progress event
            if (options.downlaod) {

                link.onprogress = options.download;
            }

            //request complete callback
            link.onreadystatechange = function() {

                //check if request is complete
                if (link.readyState == 4) {

                    //get error message
                    var err = link.A ? "A" : link.status < 400 ? null : link.responseText || "E",
                        sid = link.getResponseHeader("x-sid");

                    //reset abort status
                    link.A = 0;

                    //set/reset session id on client
                    if (sid) {

                        window.name = sid == "N" ? "" : sid;
                    }

                    if (callback) {

                        var response = null;

                        if (!err) {

                            //parse result as JSON
                            if ((link.getResponseHeader("content-type") || "").indexOf("application/json") > -1) {

                                try {

                                    response = JSON.parse(link.responseText);
                                }
                                catch (e) {

                                    err = e;
                                }
                            }
                            else {

                                response = link.responseText;
                            }
                        }

                        //fire callback
                        callback(err, response, link);
                    }
                }
            };

            //send data
            link.send(options.data);

            return function() {

                link.A = 1;
                link.abort();
            };
        }
    },

    // !------------------------------ N.css
    /**
     * Load css Files
     * @param {string} url to css file
     */
    css: function(file) {

        //create link and append it to the DOM
        var head = document.getElementsByTagName("head")[0],
            link = document.createElement("link"),
            attr = {
                rel:    "stylesheet",
                type:   "text/css",
                href:   N.ok + "/core/getFile/" + file
            };

        for (var name in attr) {

            link.setAttribute(name, attr[name]);
        }

        head.appendChild(link);
    },

    // !------------------------------ N.comp
    /**
     * Create Instances of Modules
     */
    mod: function(target, miid, callback) {

        //default argument values
        callback = (typeof callback == "function" ? callback : function() {});
        target = (typeof target === "string" ? document.getElementById(target) : target);

        //error checks
        if (!target) {
            return callback("Target not found or undefined.");
        }
        if (!miid) {
            return callback("Component ID undefined.");
        }

        //get module
        N.link("getConfig", {miid: "core", path: miid}, function(err, response) {

            //error checks
            if (err || !response) {

                return callback(err || "Empty response");
            }

            target.style.display = "none";

            var div = document.createElement("div");

            //add miid to html
            div.setAttribute("id", miid);

            //TODO show loader

            // render html from response[1]
            if (response[1]) {
                div.innerHTML = response[1];
            }

            //append div to dom
            target.appendChild(div);

            // load css from response[2]
            for (var i in response[2]) {
                N.css(response[2][i]);
            }

            // TODO handle requirejs errors
            /*require.onError = function(){

                target.style.display = "block";
                callback(null, clone);
            };*/

            // load, clone and init modules
            require([response[0].owner + "/" + response[0].name + "/main"], function(module) {

                var clone = N.clone(module);

                clone.miid = miid;
                //clone.module = module;
                clone.$ = target;
                clone.link = N.link;
                clone.config = response[0];

                // TODO create observer with user defined id (only for GUI)
                clone.obs = N.obs(miid);

                // TODO register module state

                // init module
                if (clone.init) {
                    clone.init(response[0]);
                }

                // TODO: hide loader
                // TODO: init state

                target.style.display = "block";

                callback(null, clone);
            });
        });
    },

    state: (function() {

        /*
            1. register all module states to N.states or whatever..
            2. onchange go through all module states
            3. handle browser history

            every module has his own state map object.
            listen to onhashchange.

            create a api function to change hashes?

            example of a map = {

                '/path/x': {

                    //activate this state if dom event was fired
                    events: [

                        ["#selector", "click"]
                    ],

                    //execute methods if state is activated
                    methods: [

                        ["getItems", ["param1", "param2"]]
                    ],

                    //activate viewstates if state is activated
                    viewStates: ["listEmpty"]
                }
            }
        */

        var states = {

            /*URI: [state]*/
        };

        return function() {

            //events
            //methods
            //viewstates
        };
    })()
};<|endoftext|>


<commit_before><commit_msg>added hypotheses to the mix<commit_after><|endoftext|>