<commit_before>/*

Note: At this time the plugin wrapper for this code is still under development.
The logic will work if carefully transplanted to other projects, but it can't yet
simply be applied to a selection by calling d3.textwrap(). That is the eventual goal,
though – stay tuned!

*/

(function() {

    // create the plugin method twice, both for regular use
    // and again for use inside the enter() selection
    d3.selection.prototype.textwrap = d3.selection.enter.prototype.textwrap = function(bounds) {

        // save callee into a variable so we can continue to refer to it
        // as the function scope changes
        var selection = this;

        // extract wrap boundaries from any d3-selected rect
        // and apply them to the text calling this method
        // (this still doesn't work yet...)
        var extract_bounds = function(bounds) {
            // discard the nested array wrappers added by d3
            var bounding_rect = bounds[0][0];
            // sanitize the svg element name so we can test against it
            var element_type = bounding_rect.tagName.toString();
            // if it's not a rect, exit
            if(element_type !== 'rect') {
                return false;
            // if it's a rect, proceed to extracting the position attributes
            } else {
                var bounds_extracted = {};
                bounds_extracted.x = bounds.attr('x');
                bounds_extracted.y = bounds.attr('y');
                bounds_extracted.width = bounds.attr('width');
                bounds_extracted.height = bounds.attr('height');
                return bounds_extracted; // this doesn't actually do anything yet
            }
        }

        // double check the input argument for the wrapping
        // boundaries to make sure it actually contains all
        // the information we'll need in order to wrap successfully
        var verify_bounds = function(bounds) {
            // if it's an associative array, make sure it has all the
            // necessary properties represented directly
            if(
                (typeof bounds == 'object') &&
                (bounds.x) &&
                (bounds.y) &&
                (bounds.width) &&
                (bounds.height)
            ) {
                // if that's the case, then the bounds are fine and we
                // can just return them
                return bounds;
            // if it's a numerically indexed array, assume it's a
            // d3-selected rect and try to extract the positions
            } else if(
                    // first try to make sure it's an array using Array.isArray
                    (
                        (typeof Array.isArray == 'function') &&
                        (Array.isArray(bounds))
                    ) ||
                    // but since Array.isArray isn't always supported, fall
                    // back to casting to the object to string when it's not
                    (Object.prototype.toString.call(bounds) === '[object Array]')
            ) {
                // once you're sure it's an array, extract the boundaries
                // from the rect
                return extract_bounds(bounds);
            } else {
            // but if the bounds are neither an object nor a numerical
            // array, then the bounds argument is invalid and you'll
            // need to fix it
                return false;
            }
        }

        // check that we have the necessary conditions for this function to operate properly
        if(
            // selection it's operating on cannot be not empty
            (selection.length == 0) ||
            // d3 must be available
            (!d3) ||
            // desired wrapping bounds must be provided as an input argument
            (!bounds) ||
            // input bounds must validate
            (!verify_bounds(bounds))
        ) {
            // try to return the calling selection if possible
            // so as not to interfere with methods downstream in the
            // chain
            if(selection) {
                return selection;
            // if all else fails, just return false. if you hit this point then you're
            // almost trying to call the textwrap() method on something that
            // doesn't make sense!
            } else {
                return false;
            }
        // if we've validated everything then we can finally proceed
        // to the meat of this operation
        } else {

            // wrap using html and foreignObjects if they are supported
            var wrap_with_foreign_objects = function(item) {
                console.log('wrapping with foreign object');
                // establish variables to quickly reference target nodes later
                var parent = d3.select(item.parentNode);
                var text_node = parent.selectAll('text');
                // extract our desired content from the single text element
                var text_to_wrap = text_node.text();
                // remove the text node and replace with a foreign object
                text_node.remove();
                var foreign_object = parent.append('foreignObject');
                // add foreign object and set dimensions, position, etc
                foreign_object
                    .attr("requiredFeatures", "http://www.w3.org/TR/SVG11/feature#Extensibility")
                    .attr('x', bounds.x)
                    .attr('y', bounds.y)
                    .attr('width', bounds.width)
                    .attr('height', bounds.height)
                ;
                // insert an HTML div
                var wrap_div = foreign_object
                    .append('xhtml:div')
                    // this class is currently hardcoded
                    // probably not necessary but easy to
                    // override using .classed() and for now
                    // it's nice to avoid a litany of input
                    // arguments
                    .attr('class', 'wrapped')
                ;
                // set div to same dimensions as foreign object
                wrap_div
                    .attr('height', bounds.height)
                    .attr('width', bounds.width)
//                    .attr('style', 'padding:' + padding + 'px;')
                    // insert text content
                    .html(text_to_wrap)
                ;
            }

            // wrap with tspans if foreignObject is undefined
            var wrap_with_tspans = function(text_node) {
                console.log('wrapping with tspans');
                // only fire the rest of this if the text content
                // overflows the desired dimensions
                if(this.getBBox().width > max_width) {
                    // store whatever is inside the text node
                    // in a variable and then zero out the
                    // initial content; we'll reinsert in a moment
                    // using tspan elements.
                    var text_node = d3.select(this);
                    var text_to_wrap = text_node.text();
                    text_node.text('');

                    if(text_to_wrap) {
                        // split at spaces to create an array of individual words
                        var text_to_wrap_array;
                        if(text_to_wrap.indexOf(' ') !== -1) {
                            text_to_wrap_array = text_to_wrap.split(' ');
                        } else {
                            // if there are no spaces, chop it in half.
                            // this is a hack! better to apply
                            // CSS word-break: break-all in order
                            // to handle long single-word strings that
                            // might overflow without spaces. will fix to
                            // dynamically compute the appropriate number
                            // of cuts later.
                            var string_length = text_to_wrap.length;
                            var midpoint = parseInt(string_length / 2);
                            var first_half = text_to_wrap.substr(0, midpoint);
                            var second_half = text_to_wrap.substr(midpoint, string_length);
                            text_to_wrap_array = [first_half, second_half];
                        }

                        // new array where we'll store the words re-assembled into
                        // substrings that have been tested against the desired
                        // maximum wrapping width
                        var substrings = [];
                        // computed text length is arguably incorrectly reported for
                        // all tspans after the first one, in that they will include
                        // the width of previous separate tspans. to compensate we need
                        // to manually track the computed text length of all those
                        // previous tspans and substrings, and then use that to offset
                        // the miscalculation. this then gives us the actual correct
                        // position we want to use in rendering the text in the SVG.
                        var offset = 0;
                        // loop through the words and test the computed text length
                        // of the string against the maximum desired wrapping width
                        for(var i = 0; i < text_to_wrap_array.length; i++) {
                            var word = text_to_wrap_array[i];
                            var previous_string = text_node.text();
                            var previous_width = this.getComputedTextLength();
                            // initialize the current word as the first word
                            // or append to the previous string if one exists
                            var new_string;
                            if(previous_string) {
                                new_string = previous_string + ' ' + word;
                            } else {
                                new_string = word;
                            }
                            // add the newest substring back to the text node and
                            // measure the length
                            text_node.text(new_string);
                            var new_width = this.getComputedTextLength();
                            // adjust the length by the offset we've tracked
                            // due to the misreported length discussed above
                            var test_width = new_width - offset;
                            // if our latest version of the string is too
                            // big for the bounds, use the previous
                            // version of the string (without the newest word
                            // added) and use the latest word to restart the
                            // process with a new tspan
                            if(test_width > max_width) {
                                if(previous_string !== '') {
                                    var temp = {string: previous_string, width: previous_width - offset};
                                    substrings.push(temp);
                                    offset = offset + previous_width;
                                    text_node.text('');
                                    text_node.text(word);
                                }
                            }
                            // if we're up to the last word in the array,
                            // get the computed length as is without
                            // appending anything
                            if(i == text_to_wrap_array.length - 1) {
                                var final_string = new_string.substr(previous_string.length);
                                // remove leading spaces if present
                                if(final_string.substr(0, 1) == ' ') {
                                    final_string = final_string.substr(1);
                                }
                                if(final_string !== '') {
                                    if((new_width - offset) > 0) {new_width = new_width - offset}
                                    var temp = {string: final_string, width: new_width};
                                    substrings.push(temp);
                                    text_node.text('');
                                }
                            }
                        }
                        // double check that there are no empty substrings
                        // because those would create blank tspans
                        var substrings_clean = [];
                        for(var i = 0; i < substrings.length; i++) {
                            if(substrings[i].string.length > 0) {
                                substrings_clean.push(substrings[i]);
                            }
                        }
                        // append each substring as a tspan
                        var current_tspan;
                        var tspan_count;
                        for(var i = 0; i < substrings_clean.length; i++) {
                            var substring = substrings_clean[i].string;
                            if(i > 0) {
                                var previous_substring = substrings_clean[i - 1];
                            }
                            current_tspan = text_node.append('tspan')
                                .text(substring)
                            ;
                            current_tspan
                                .attr('dy', function(d) {
                                    if(i > 0) {
                                        // line height shouldn't be hard coded
                                        // will fix this later
                                        return '1.5em';
                                    }
                                })
                            ;
                            // shift left from default position, which
                            // is probably based on the full length of the
                            // text string until we make this adjustment
                            current_tspan
                                .attr('dx', function() {
                                    var render_offset = 0;
                                    if(i > 0) {
                                        render_offset = 0;
                                        for(j = 0; j < i; j++) {
                                            render_offset += substrings[j].width;
                                        }
                                    }
                                    return render_offset * -1;
                                })
                            ;
                        }
                    }
                }
            }

            // test for browser support and then switch
            // between the different text wrapping methods
            var wrap_method;
            if(typeof SVGForeignObjectElement !== 'undefined') {
                wrap_method = wrap_with_foreign_objects;
            } else {
                wrap_method = wrap_with_tspans;
            }

            // run the wrap function for each item
            // in the selection
            for(var i = 0; i < selection.length; i++) {
                var item = selection[i];
                wrap_method(item);
            }

        }

	}

})();
<commit_msg>getter function for bounds object<commit_after>/*

Note: At this time the plugin wrapper for this code is still under development.
The logic will work if carefully transplanted to other projects, but it can't yet
simply be applied to a selection by calling d3.textwrap(). That is the eventual goal,
though – stay tuned!

*/

(function() {

    // create the plugin method twice, both for regular use
    // and again for use inside the enter() selection
    d3.selection.prototype.textwrap = d3.selection.enter.prototype.textwrap = function(bounds) {

        // save callee into a variable so we can continue to refer to it
        // as the function scope changes
        var selection = this;

        // extract wrap boundaries from any d3-selected rect
        // and apply them to the text calling this method
        // (this still doesn't work yet...)
        var extract_bounds = function(bounds) {
            // discard the nested array wrappers added by d3
            var bounding_rect = bounds[0][0];
            // sanitize the svg element name so we can test against it
            var element_type = bounding_rect.tagName.toString();
            // if it's not a rect, exit
            if(element_type !== 'rect') {
                return false;
            // if it's a rect, proceed to extracting the position attributes
            } else {
                var bounds_extracted = {};
                bounds_extracted.x = bounds.attr('x');
                bounds_extracted.y = bounds.attr('y');
                bounds_extracted.width = bounds.attr('width');
                bounds_extracted.height = bounds.attr('height');
                return bounds_extracted; // this doesn't actually do anything yet
            }
        }

        // double check the input argument for the wrapping
        // boundaries to make sure it actually contains all
        // the information we'll need in order to wrap successfully
        var verify_bounds = function(bounds) {
            // if it's an associative array, make sure it has all the
            // necessary properties represented directly
            if(
                (typeof bounds == 'object') &&
                (bounds.x) &&
                (bounds.y) &&
                (bounds.width) &&
                (bounds.height)
                // if that's the case, then the bounds are fine
            ) {
                // add a simple getter method so you can use either
                // bounds.x or bounds.attr('x') as your notation,
                // the latter being a common convention among D3
                // developers
                bounds.attr = function(property) {
                    if(this[property]) {
                        return this[property];
                    }
                }
                // return the lightly modified bounds
                return bounds;
            // if it's a numerically indexed array, assume it's a
            // d3-selected rect and try to extract the positions
            } else if(
                    // first try to make sure it's an array using Array.isArray
                    (
                        (typeof Array.isArray == 'function') &&
                        (Array.isArray(bounds))
                    ) ||
                    // but since Array.isArray isn't always supported, fall
                    // back to casting to the object to string when it's not
                    (Object.prototype.toString.call(bounds) === '[object Array]')
            ) {
                // once you're sure it's an array, extract the boundaries
                // from the rect
                return extract_bounds(bounds);
            } else {
            // but if the bounds are neither an object nor a numerical
            // array, then the bounds argument is invalid and you'll
            // need to fix it
                return false;
            }
        }

        // check that we have the necessary conditions for this function to operate properly
        if(
            // selection it's operating on cannot be not empty
            (selection.length == 0) ||
            // d3 must be available
            (!d3) ||
            // desired wrapping bounds must be provided as an input argument
            (!bounds) ||
            // input bounds must validate
            (!verify_bounds(bounds))
        ) {
            // try to return the calling selection if possible
            // so as not to interfere with methods downstream in the
            // chain
            if(selection) {
                return selection;
            // if all else fails, just return false. if you hit this point then you're
            // almost trying to call the textwrap() method on something that
            // doesn't make sense!
            } else {
                return false;
            }
        // if we've validated everything then we can finally proceed
        // to the meat of this operation
        } else {

            // wrap using html and foreignObjects if they are supported
            var wrap_with_foreign_objects = function(item) {
                console.log('wrapping with foreign object');
                // establish variables to quickly reference target nodes later
                var parent = d3.select(item.parentNode);
                var text_node = parent.selectAll('text');
                // extract our desired content from the single text element
                var text_to_wrap = text_node.text();
                // remove the text node and replace with a foreign object
                text_node.remove();
                var foreign_object = parent.append('foreignObject');
                // add foreign object and set dimensions, position, etc
                foreign_object
                    .attr("requiredFeatures", "http://www.w3.org/TR/SVG11/feature#Extensibility")
                    .attr('x', bounds.x)
                    .attr('y', bounds.y)
                    .attr('width', bounds.width)
                    .attr('height', bounds.height)
                ;
                // insert an HTML div
                var wrap_div = foreign_object
                    .append('xhtml:div')
                    // this class is currently hardcoded
                    // probably not necessary but easy to
                    // override using .classed() and for now
                    // it's nice to avoid a litany of input
                    // arguments
                    .attr('class', 'wrapped')
                ;
                // set div to same dimensions as foreign object
                wrap_div
                    .attr('height', bounds.height)
                    .attr('width', bounds.width)
//                    .attr('style', 'padding:' + padding + 'px;')
                    // insert text content
                    .html(text_to_wrap)
                ;
            }

            // wrap with tspans if foreignObject is undefined
            var wrap_with_tspans = function(text_node) {
                console.log('wrapping with tspans');
                // only fire the rest of this if the text content
                // overflows the desired dimensions
                if(this.getBBox().width > max_width) {
                    // store whatever is inside the text node
                    // in a variable and then zero out the
                    // initial content; we'll reinsert in a moment
                    // using tspan elements.
                    var text_node = d3.select(this);
                    var text_to_wrap = text_node.text();
                    text_node.text('');

                    if(text_to_wrap) {
                        // split at spaces to create an array of individual words
                        var text_to_wrap_array;
                        if(text_to_wrap.indexOf(' ') !== -1) {
                            text_to_wrap_array = text_to_wrap.split(' ');
                        } else {
                            // if there are no spaces, chop it in half.
                            // this is a hack! better to apply
                            // CSS word-break: break-all in order
                            // to handle long single-word strings that
                            // might overflow without spaces. will fix to
                            // dynamically compute the appropriate number
                            // of cuts later.
                            var string_length = text_to_wrap.length;
                            var midpoint = parseInt(string_length / 2);
                            var first_half = text_to_wrap.substr(0, midpoint);
                            var second_half = text_to_wrap.substr(midpoint, string_length);
                            text_to_wrap_array = [first_half, second_half];
                        }

                        // new array where we'll store the words re-assembled into
                        // substrings that have been tested against the desired
                        // maximum wrapping width
                        var substrings = [];
                        // computed text length is arguably incorrectly reported for
                        // all tspans after the first one, in that they will include
                        // the width of previous separate tspans. to compensate we need
                        // to manually track the computed text length of all those
                        // previous tspans and substrings, and then use that to offset
                        // the miscalculation. this then gives us the actual correct
                        // position we want to use in rendering the text in the SVG.
                        var offset = 0;
                        // loop through the words and test the computed text length
                        // of the string against the maximum desired wrapping width
                        for(var i = 0; i < text_to_wrap_array.length; i++) {
                            var word = text_to_wrap_array[i];
                            var previous_string = text_node.text();
                            var previous_width = this.getComputedTextLength();
                            // initialize the current word as the first word
                            // or append to the previous string if one exists
                            var new_string;
                            if(previous_string) {
                                new_string = previous_string + ' ' + word;
                            } else {
                                new_string = word;
                            }
                            // add the newest substring back to the text node and
                            // measure the length
                            text_node.text(new_string);
                            var new_width = this.getComputedTextLength();
                            // adjust the length by the offset we've tracked
                            // due to the misreported length discussed above
                            var test_width = new_width - offset;
                            // if our latest version of the string is too
                            // big for the bounds, use the previous
                            // version of the string (without the newest word
                            // added) and use the latest word to restart the
                            // process with a new tspan
                            if(test_width > max_width) {
                                if(previous_string !== '') {
                                    var temp = {string: previous_string, width: previous_width - offset};
                                    substrings.push(temp);
                                    offset = offset + previous_width;
                                    text_node.text('');
                                    text_node.text(word);
                                }
                            }
                            // if we're up to the last word in the array,
                            // get the computed length as is without
                            // appending anything
                            if(i == text_to_wrap_array.length - 1) {
                                var final_string = new_string.substr(previous_string.length);
                                // remove leading spaces if present
                                if(final_string.substr(0, 1) == ' ') {
                                    final_string = final_string.substr(1);
                                }
                                if(final_string !== '') {
                                    if((new_width - offset) > 0) {new_width = new_width - offset}
                                    var temp = {string: final_string, width: new_width};
                                    substrings.push(temp);
                                    text_node.text('');
                                }
                            }
                        }
                        // double check that there are no empty substrings
                        // because those would create blank tspans
                        var substrings_clean = [];
                        for(var i = 0; i < substrings.length; i++) {
                            if(substrings[i].string.length > 0) {
                                substrings_clean.push(substrings[i]);
                            }
                        }
                        // append each substring as a tspan
                        var current_tspan;
                        var tspan_count;
                        for(var i = 0; i < substrings_clean.length; i++) {
                            var substring = substrings_clean[i].string;
                            if(i > 0) {
                                var previous_substring = substrings_clean[i - 1];
                            }
                            current_tspan = text_node.append('tspan')
                                .text(substring)
                            ;
                            current_tspan
                                .attr('dy', function(d) {
                                    if(i > 0) {
                                        // line height shouldn't be hard coded
                                        // will fix this later
                                        return '1.5em';
                                    }
                                })
                            ;
                            // shift left from default position, which
                            // is probably based on the full length of the
                            // text string until we make this adjustment
                            current_tspan
                                .attr('dx', function() {
                                    var render_offset = 0;
                                    if(i > 0) {
                                        render_offset = 0;
                                        for(j = 0; j < i; j++) {
                                            render_offset += substrings[j].width;
                                        }
                                    }
                                    return render_offset * -1;
                                })
                            ;
                        }
                    }
                }
            }

            // test for browser support and then switch
            // between the different text wrapping methods
            var wrap_method;
            if(typeof SVGForeignObjectElement !== 'undefined') {
                wrap_method = wrap_with_foreign_objects;
            } else {
                wrap_method = wrap_with_tspans;
            }

            // run the wrap function for each item
            // in the selection
            for(var i = 0; i < selection.length; i++) {
                var item = selection[i];
                wrap_method(item);
            }

        }

	}

})();
<|endoftext|>


<commit_before>// hold on, d and i will not be our input arguments when this is properly
// converted into a flexible plugin – you'll just call it in on a text
// selection.

function textwrap(d, i) {

		// if we have foreign objects available, insert with a div
		if(typeof SVGForeignObjectElement !== 'undefined')
		 {
			// establish variables to quickly reference target nodes later
			var parent = d3.select(this.parentNode);
			var text_node = parent.selectAll('text');
			// extract our desired content from the single text element
			var text_to_wrap = text_node.text();
			// remove the text node and replace with a foreign object
			text_node.remove();
			var foreign_object = parent.append('foreignObject');
			// initialize foreign object and set dimensions, position, etc
			foreign_object
			    .attr("requiredFeatures", "http://www.w3.org/TR/SVG11/feature#Extensibility")
				.attr('x', x_position)
				.attr('y', y_position)
 				.attr('width', max_width)
 				.attr('height', max_height)
			;
			// insert an HTML div
			var wrap_div = foreign_object
				.append('xhtml:div')
				// this class is currently hardcoded
				// probably not necessary but easy to
				// override using .classed() and for now
				// it's nice to avoid a litany of input
				// arguments
				.attr('class', 'wrapped')
			;
			// set div to same dimensions as foreign object
			wrap_div
 				.attr('height', max_height)
 				.attr('width', max_width)
 				.attr('style', 'padding:' + padding + 'px;')
 				// insert text content
				.html(text_to_wrap)
			;
		}

		// if we can't just insert foreign objects,
		// jump through hoops to separate into tspans
		if(typeof SVGForeignObjectElement == 'undefined')
		 {

			// only fire the rest of this if the text content
			// overflows the desired dimensions
			if(this.getBBox().width > max_width) {

				// grab the text content from the text node
				// into a variable and then zero out the
				// initial content; we'll reinsert in a moment
				// using tspan elements.
				var text_node = d3.select(this);
				var text_to_wrap = text_node.text();
				text_node.text('');

				if(text_to_wrap) {
					// split on spaces to create an array of individual words
					var text_to_wrap_array;
					if(text_to_wrap.indexOf(' ') !== -1) {
						text_to_wrap_array = text_to_wrap.split(' ');
					} else {
						// if there are no spaces, chop it in half.
						// this is a hack! better to apply
						// CSS word-break: break-all; in order
						// to handle long single-word strings that
						// might overflow. will fix to dynamically
						// compute the appropriate number of cuts later.
						var string_length = text_to_wrap.length;
						var midpoint = parseInt(string_length / 2);
						var first_half = text_to_wrap.substr(0, midpoint);
						var second_half = text_to_wrap.substr(midpoint, string_length);
						text_to_wrap_array = [first_half, second_half];
					}

					// new array where we'll store the words re-assembled into
					// substrings that have been tested against the desired
					// maximum wrapping width
					var substrings = [];
					// computed text length is arguably incorrectly reported for
					// all tspans after the first one, in that they will include
					// previous separate tspans, so to compensate we need to manually
					// track the computed text length of all previous tspans/substrings
					// and use that to offset the miscalculation to get the actual
					// position we want to use in rendering the text in the SVG.
					var offset = 0;
					// loop through the words and test the computed text length
					// of the string against the maximum width
					for(i = 0; i < text_to_wrap_array.length; i++) {
						var word = text_to_wrap_array[i];
						var previous_string = text_node.text();
						var previous_width = this.getComputedTextLength();

						var new_string;

						// initialize or append to the string depending on
						// whether it's the first word
						if(previous_string) {
							new_string = previous_string + ' ' + word;
						} else {
							new_string = word;
						}

						// add the newest, longest substring to the text node and
						// measure the length
						text_node.text(new_string);
						var new_width = this.getComputedTextLength();

						// adjust the length by the offset we've tracked
						// due to the misreported length discussed above
						var test_width = new_width - offset;

						// if our latest version of the string is too
						// big for the bounds, use the previous
						// version of the string (without the newest word
						// added) and use this latest word to restart the
						// process with a new tspan
						if(test_width > max_width) {
							if(previous_string !== '') {
								var temp = {string: previous_string, width: previous_width - offset};
								substrings.push(temp);
								offset = offset + previous_width;
								text_node.text('');
								text_node.text(word);
							}
						}
						// if we're up to the last word in the array,
						// get the computed length as is without
						// appending anything
						if(i == text_to_wrap_array.length - 1) {
							var final_string = new_string.substr(previous_string.length);
							// make sure it's not an empty string
							if(final_string.substr(0, 1) == ' ') {
								final_string = final_string.substr(1);
							}
							if(final_string !== '') {
								if((new_width - offset) > 0) {new_width = new_width - offset}
								var temp = {string: final_string, width: new_width};
								substrings.push(temp);
								text_node.text('');
							}
						}
					}

					// double check that there are no empty substrings
					var substrings_clean = [];
					for(i = 0; i < substrings.length; i++) {
						if(substrings[i].string.length > 0) {
							substrings_clean.push(substrings[i]);
						}
					}

					// append each substring as a tspan
					var current_tspan;
					var tspan_count;
					for(i = 0; i < substrings_clean.length; i++) {
						var substring = substrings_clean[i].string;
						if(i > 0) {
							var previous_substring = substrings_clean[i - 1];
						}
						current_tspan = text_node.append('tspan')
							.text(substring)
 						;
						current_tspan
							.attr('dy', function(d) {
								if(i > 0) {
									// line height shouldn't be hard coded
									// will fix this later
									return '1.5em';
								}
							})
						;
						// shift left from default position, which
						// is probably based on the full length of the
						// text string until we make this adjustment
						current_tspan
							.attr('dx', function() {
								var render_offset = 0;
								if(i > 0) {
									render_offset = 0;
									for(j = 0; j < i; j++) {
										render_offset += substrings[j].width;
									}
								}
								return render_offset * -1;
							})
						;
					}

				}
			}
		}
	}
<commit_msg>comments <commit_after>// hold on, d and i will not be our input arguments when this is properly
// converted into a flexible plugin – you'll just call it on a text
// selection.

function textwrap(d, i) {

		// if we have foreign objects available, insert with a div
		if(typeof SVGForeignObjectElement !== 'undefined')
		 {
			// establish variables to quickly reference target nodes later
			var parent = d3.select(this.parentNode);
			var text_node = parent.selectAll('text');
			// extract our desired content from the single text element
			var text_to_wrap = text_node.text();
			// remove the text node and replace with a foreign object
			text_node.remove();
			var foreign_object = parent.append('foreignObject');
			// add foreign object and set dimensions, position, etc
			foreign_object
			    .attr("requiredFeatures", "http://www.w3.org/TR/SVG11/feature#Extensibility")
				.attr('x', x_position)
				.attr('y', y_position)
 				.attr('width', max_width)
 				.attr('height', max_height)
			;
			// insert an HTML div
			var wrap_div = foreign_object
				.append('xhtml:div')
				// this class is currently hardcoded
				// probably not necessary but easy to
				// override using .classed() and for now
				// it's nice to avoid a litany of input
				// arguments
				.attr('class', 'wrapped')
			;
			// set div to same dimensions as foreign object
			wrap_div
 				.attr('height', max_height)
 				.attr('width', max_width)
 				.attr('style', 'padding:' + padding + 'px;')
 				// insert text content
				.html(text_to_wrap)
			;
		}

		// if we can't just insert foreign objects,
		// jump through hoops to separate into tspans
		if(typeof SVGForeignObjectElement == 'undefined')
		 {

			// only fire the rest of this if the text content
			// overflows the desired dimensions
			if(this.getBBox().width > max_width) {

				// store whatever is inside the text node
				// in a variable and then zero out the
				// initial content; we'll reinsert in a moment
				// using tspan elements.
				var text_node = d3.select(this);
				var text_to_wrap = text_node.text();
				text_node.text('');

				if(text_to_wrap) {
					// split at spaces to create an array of individual words
					var text_to_wrap_array;
					if(text_to_wrap.indexOf(' ') !== -1) {
						text_to_wrap_array = text_to_wrap.split(' ');
					} else {
						// if there are no spaces, chop it in half.
						// this is a hack! better to apply
						// CSS word-break: break-all in order
						// to handle long single-word strings that
						// might overflow without spaces. will fix to
						// dynamically compute the appropriate number
						// of cuts later.
						var string_length = text_to_wrap.length;
						var midpoint = parseInt(string_length / 2);
						var first_half = text_to_wrap.substr(0, midpoint);
						var second_half = text_to_wrap.substr(midpoint, string_length);
						text_to_wrap_array = [first_half, second_half];
					}

					// new array where we'll store the words re-assembled into
					// substrings that have been tested against the desired
					// maximum wrapping width
					var substrings = [];
					// computed text length is arguably incorrectly reported for
					// all tspans after the first one, in that they will include
					// the width of previous separate tspans. to compensate we need
					// to manually track the computed text length of all those
					// previous tspans and substrings, and then use that to offset
					// the miscalculation. this then gives us the actual correct
					// position we want to use in rendering the text in the SVG.
					var offset = 0;
					// loop through the words and test the computed text length
					// of the string against the maximum desired wrapping width
					for(i = 0; i < text_to_wrap_array.length; i++) {
						var word = text_to_wrap_array[i];
						var previous_string = text_node.text();
						var previous_width = this.getComputedTextLength();
						// initialize the current word as the first word
						// or append to the previous string if one exists
						var new_string;
						if(previous_string) {
							new_string = previous_string + ' ' + word;
						} else {
							new_string = word;
						}
						// add the newest substring back to the text node and
						// measure the length
						text_node.text(new_string);
						var new_width = this.getComputedTextLength();
						// adjust the length by the offset we've tracked
						// due to the misreported length discussed above
						var test_width = new_width - offset;
						// if our latest version of the string is too
						// big for the bounds, use the previous
						// version of the string (without the newest word
						// added) and use the latest word to restart the
						// process with a new tspan
						if(test_width > max_width) {
							if(previous_string !== '') {
								var temp = {string: previous_string, width: previous_width - offset};
								substrings.push(temp);
								offset = offset + previous_width;
								text_node.text('');
								text_node.text(word);
							}
						}
						// if we're up to the last word in the array,
						// get the computed length as is without
						// appending anything
						if(i == text_to_wrap_array.length - 1) {
							var final_string = new_string.substr(previous_string.length);
							// remove leading spaces if present
							if(final_string.substr(0, 1) == ' ') {
								final_string = final_string.substr(1);
							}
							if(final_string !== '') {
								if((new_width - offset) > 0) {new_width = new_width - offset}
								var temp = {string: final_string, width: new_width};
								substrings.push(temp);
								text_node.text('');
							}
						}
					}
					// double check that there are no empty substrings
					// because those would create blank tspans
					var substrings_clean = [];
					for(i = 0; i < substrings.length; i++) {
						if(substrings[i].string.length > 0) {
							substrings_clean.push(substrings[i]);
						}
					}
					// append each substring as a tspan
					var current_tspan;
					var tspan_count;
					for(i = 0; i < substrings_clean.length; i++) {
						var substring = substrings_clean[i].string;
						if(i > 0) {
							var previous_substring = substrings_clean[i - 1];
						}
						current_tspan = text_node.append('tspan')
							.text(substring)
 						;
						current_tspan
							.attr('dy', function(d) {
								if(i > 0) {
									// line height shouldn't be hard coded
									// will fix this later
									return '1.5em';
								}
							})
						;
						// shift left from default position, which
						// is probably based on the full length of the
						// text string until we make this adjustment
						current_tspan
							.attr('dx', function() {
								var render_offset = 0;
								if(i > 0) {
									render_offset = 0;
									for(j = 0; j < i; j++) {
										render_offset += substrings[j].width;
									}
								}
								return render_offset * -1;
							})
						;
					}

				}
			}
		}
	}
<|endoftext|>


<commit_before>movieApp.service('authService', ['$http', function($http) {

	var authService = this;

	this.clientId = "5p2k4moufxk2iib";
	this.redirectUri = "https://monsharen.github.io/watchlist/index.html";
	//this.redirectUri = "https://www.movie.pizza/";
	this.responseType = "token";

	this.authenticate = function() {
		var url = authService.getAuthenticationUrl();
		window.location.href = url;
	};

	this.getAuthenticationUrl = function() {
		return 'https://www.dropbox.com/1/oauth2/authorize?' +
			'response_type=' + authService.responseType +
			'&client_id=' + authService.clientId +
			"&redirect_uri=" + authService.redirectUri;
	};

	this.isUserAuthenticated = function(onSuccess, onError) {
		var params = authService.getHashParams();
		var authToken = params["/access_token"];

		if (authToken != null) {
			onSuccess(authToken);
		} else {
			onError();
		}
	};

	this.getUrlParams = function() {
		var urlParams = document.location.search.split('+').join(' ');

		var params = {}, tokens, re = /[?&]?([^=]+)=([^&]*)/g;

		while (tokens = re.exec(urlParams)) {
			params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
		}
		return params;
	};

	this.getHashParams = function() {

	    var hashParams = {};
	    var e,
	        a = /\+/g,  // Regex for replacing addition symbol with a space
	        r = /([^&;=]+)=?([^&;]*)/g,
	        d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
	        q = window.location.hash.substring(1);

	    while (e = r.exec(q))
	       hashParams[d(e[1])] = d(e[2]);

	    return hashParams;
	};

}]);

movieApp.service('dropboxService', ['$http', function($http) {

	/*
	File structure
	[
		{ "name": "nameOfList1", "movies":[], "series": [] },
		{ "name": "nameOfList2", "movies":[], "series": [] }
	]
	*/

    var dropbox = this;
    this.fileName = "data.json";
    this.baseUrl = 'https://content.dropboxapi.com/1';

    this.getData = function(authToken, onSuccess, onError, onComplete) {
		var req = {
			method: 'GET',
			url: dropbox.baseUrl + "/files/auto/" + dropbox.fileName,
			headers: {
				"Authorization": "Bearer " + authToken
			}
		};

		$http(req).then(function(response) {
			onSuccess(response.data);
		},
		function(error) {
			onError(error);
		}).finally( function() {
			onComplete();
		});
	};

	this.getEmptyFile = function() {
		return [ { name: "Watch list", movies:[], series: [] } ];
	};

	this.getNewList = function(name) {
		return { name: name, movies:[], series: [] };
	};

	this.upload = function(authToken, data, onSuccess, onError, onComplete) {

		var req = {
			method: 'POST',
			url: dropbox.baseUrl + "/files_put/auto/" + dropbox.fileName,
			headers: {
				"Authorization": "Bearer " + authToken
			},
			data: data
		};

		$http(req).then(function(response) {
			onSuccess(response);
		},
		function(response) {
			onError(response);
		}).finally( function() {
			onComplete();
		});
	}
}]);

movieApp.service('omdbService', ['$http', function($http) {

	var omdbService = this;
	this.baseUrl = "https://www.omdbapi.com/";

    this.find = function(movieTitle, onSuccess, onError, onComplete) {

		if (!movieTitle) {
			onError("empty search queries not allowed");
			onComplete();
			return;
		}

		var req = {
			method: 'GET',
			url: omdbService.baseUrl + "?s=" + movieTitle
		};

		$http(req).then(function(response) {
			console.log("search result");
			console.log(response.data.Search);

			if (response.data.Error) {
				console.log("search failed: " + response.data.Error);
				onError("search failed: " + response.data.Error);
				return;
			}

			onSuccess(response.data.Search);
		},
		function(response) {
			console.log("request failed: ");
			console.log(response);
			onError(response);
		}).finally( function() {
			onComplete();
		});
	}

	this.getFullMovieDetails = function(imdbId, onSuccess, onError, onComplete) {
		var req = {
			method: 'GET',
			url: omdbService.baseUrl + "/?i=" + imdbId
		};

		$http(req).then(function(response) {
			console.log("got movie result");
			console.log(response.data);

			if (response.data.Error) {
				onError(response.data.Error);
				return;
			}

			var movie = response.data;
			console.log("got full details: ");
			console.log(movie);
			onSuccess(movie);
		},
		function(response) {
			console.log("request failed: ");
			console.log(response);
			onError(response);
		}).finally( function() {
			onComplete();
		});
	};
}]);

movieApp.service('searchService', [ 'omdbService', function(omdbService) {

	this.search = function(query, onSuccess, onError, onComplete) {

		if (query.indexOf('imdb:') === 0) {
			var imdbId = query.substring(5);
			this.searchByImdbId(imdbId, onSuccess, onError, onComplete);
		} else if (query.indexOf('http://www.imdb.com/title') === 0) {

			var start = query.indexOf("/title/") + 7;
			var end = query.indexOf("/", start);
			var imdbId = query.substring(start, end);

			console.log("searching for imdbId " + imdbId);

			this.searchByImdbId(imdbId, onSuccess, onError, onComplete);
		} else {
			omdbService.find(query,
				function onFindSuccess(partialMovieDetails) {
					onSuccess(partialMovieDetails);
				},
				function onFindError(error) {
					onError(error);
				},
				function onFindComplete() {
					onComplete();
				}
			);
		}
	};

	this.searchByImdbId = function(imdbId, onSuccess, onError, onComplete) {
		omdbService.getFullMovieDetails(imdbId,
			function onFullMovieDetailsSuccess(movie) {
				var movieResults = [];
				movieResults.push(movie);
				onSuccess(movieResults);
			},
			function onFullMovieDetailsError(error) {
				onError(error);
			},
			function onFullMovieDetailsComplete() {
				onComplete();
			}
		);
	};
}]);
<commit_msg>more robust url parsing<commit_after>movieApp.service('authService', ['$http', function($http) {

	var authService = this;

	this.clientId = "5p2k4moufxk2iib";
	this.redirectUri = "https://monsharen.github.io/watchlist/index.html";
	//this.redirectUri = "https://www.movie.pizza/";
	this.responseType = "token";

	this.authenticate = function() {
		var url = authService.getAuthenticationUrl();
		window.location.href = url;
	};

	this.getAuthenticationUrl = function() {
		return 'https://www.dropbox.com/1/oauth2/authorize?' +
			'response_type=' + authService.responseType +
			'&client_id=' + authService.clientId +
			"&redirect_uri=" + authService.redirectUri;
	};

	this.isUserAuthenticated = function(onSuccess, onError) {
		var params = authService.getHashParams();
		var authToken = params["/access_token"];

		if (authToken != null) {
			onSuccess(authToken);
		} else {
			onError();
		}
	};

	this.getUrlParams = function() {
		var urlParams = document.location.search.split('+').join(' ');

		var params = {}, tokens, re = /[?&]?([^=]+)=([^&]*)/g;

		while (tokens = re.exec(urlParams)) {
			params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
		}
		return params;
	};

	this.getHashParams = function() {

	    var hashParams = {};
	    var e,
	        a = /\+/g,  // Regex for replacing addition symbol with a space
	        r = /([^&;=]+)=?([^&;]*)/g,
	        d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
	        q = window.location.hash.substring(1);

	    while (e = r.exec(q))
	       hashParams[d(e[1])] = d(e[2]);

	    return hashParams;
	};

}]);

movieApp.service('dropboxService', ['$http', function($http) {

	/*
	File structure
	[
		{ "name": "nameOfList1", "movies":[], "series": [] },
		{ "name": "nameOfList2", "movies":[], "series": [] }
	]
	*/

    var dropbox = this;
    this.fileName = "data.json";
    this.baseUrl = 'https://content.dropboxapi.com/1';

    this.getData = function(authToken, onSuccess, onError, onComplete) {
		var req = {
			method: 'GET',
			url: dropbox.baseUrl + "/files/auto/" + dropbox.fileName,
			headers: {
				"Authorization": "Bearer " + authToken
			}
		};

		$http(req).then(function(response) {
			onSuccess(response.data);
		},
		function(error) {
			onError(error);
		}).finally( function() {
			onComplete();
		});
	};

	this.getEmptyFile = function() {
		return [ { name: "Watch list", movies:[], series: [] } ];
	};

	this.getNewList = function(name) {
		return { name: name, movies:[], series: [] };
	};

	this.upload = function(authToken, data, onSuccess, onError, onComplete) {

		var req = {
			method: 'POST',
			url: dropbox.baseUrl + "/files_put/auto/" + dropbox.fileName,
			headers: {
				"Authorization": "Bearer " + authToken
			},
			data: data
		};

		$http(req).then(function(response) {
			onSuccess(response);
		},
		function(response) {
			onError(response);
		}).finally( function() {
			onComplete();
		});
	}
}]);

movieApp.service('omdbService', ['$http', function($http) {

	var omdbService = this;
	this.baseUrl = "https://www.omdbapi.com/";

    this.find = function(movieTitle, onSuccess, onError, onComplete) {

		if (!movieTitle) {
			onError("empty search queries not allowed");
			onComplete();
			return;
		}

		var req = {
			method: 'GET',
			url: omdbService.baseUrl + "?s=" + movieTitle
		};

		$http(req).then(function(response) {
			console.log("search result");
			console.log(response.data.Search);

			if (response.data.Error) {
				console.log("search failed: " + response.data.Error);
				onError("search failed: " + response.data.Error);
				return;
			}

			onSuccess(response.data.Search);
		},
		function(response) {
			console.log("request failed: ");
			console.log(response);
			onError(response);
		}).finally( function() {
			onComplete();
		});
	}

	this.getFullMovieDetails = function(imdbId, onSuccess, onError, onComplete) {
		var req = {
			method: 'GET',
			url: omdbService.baseUrl + "/?i=" + imdbId
		};

		$http(req).then(function(response) {
			console.log("got movie result");
			console.log(response.data);

			if (response.data.Error) {
				onError(response.data.Error);
				return;
			}

			var movie = response.data;
			console.log("got full details: ");
			console.log(movie);
			onSuccess(movie);
		},
		function(response) {
			console.log("request failed: ");
			console.log(response);
			onError(response);
		}).finally( function() {
			onComplete();
		});
	};
}]);

movieApp.service('searchService', [ 'omdbService', function(omdbService) {

	this.search = function(query, onSuccess, onError, onComplete) {

		if (query.indexOf('imdb:') === 0) {
			var imdbId = query.substring(5);
			this.searchByImdbId(imdbId, onSuccess, onError, onComplete);
		} else if (query.indexOf('imdb.com/title') != -1) {

			var start = query.indexOf("/title/") + 7;
			var end = query.indexOf("/", start);
			var imdbId = query.substring(start, end);

			console.log("searching for imdbId " + imdbId);

			this.searchByImdbId(imdbId, onSuccess, onError, onComplete);
		} else {
			omdbService.find(query,
				function onFindSuccess(partialMovieDetails) {
					onSuccess(partialMovieDetails);
				},
				function onFindError(error) {
					onError(error);
				},
				function onFindComplete() {
					onComplete();
				}
			);
		}
	};

	this.searchByImdbId = function(imdbId, onSuccess, onError, onComplete) {
		omdbService.getFullMovieDetails(imdbId,
			function onFullMovieDetailsSuccess(movie) {
				var movieResults = [];
				movieResults.push(movie);
				onSuccess(movieResults);
			},
			function onFullMovieDetailsError(error) {
				onError(error);
			},
			function onFullMovieDetailsComplete() {
				onComplete();
			}
		);
	};
}]);
<|endoftext|>


<commit_before>/*jslint node: true*/
/*jshint loopfunc:true */
"use strict";
var glob = require('glob');
var path = require('path');
var $rdf = require('rdflib');
var request = require('request');
var S = require('string');
var url = require('url');
var async = require('async');

var debug = require('./debug').ACL;
var utils = require('./utils.js');
var ns = require('./vocab/ns.js').ns;
var rdfVocab = require('./vocab/rdf.js');
var HttpError = require('./http-error');
var ACL = require('solid-acl');

// TODO should this be set?
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

function match (graph, s, p, o) {
    var matches = graph.statementsMatching(
      s ? $rdf.sym(s) : undefined,
      p ? $rdf.sym(p) : undefined,
      o ? $rdf.sym(o) : undefined);
    return matches
}

function ACL (opts) {
  var self = this
  opts = opts || {}

  self.fetch = opts.fetch
  self.match = opts.match || match
  self.suffix = opts.suffix || '.acl'
}

ACL.prototype.isAcl = function (resource) {
  return !!S(resource).endsWith(this.suffix)
}

ACL.prototype.can = function (user, mode, resource, callback, options) {
  debug('Can ' + user || 'an agent' + ' ' + mode + ' ' + resource + '?')
  var self = this
  var accessType = 'accessTo'
  var acls = possibleACLs(resource, self.suffix)
  options = options || {}

  // If it is an ACL, only look for control this resource
  if (self.isAcl(resource)) {
    mode = 'Control'
  }

  async.eachSeries(
    acls,
    // Looks for ACL, if found, looks for a rule
    function (acl, next) {
      debug('Check if acl exist: ' + acl)

      // Let's see if there is a file..
      self.fetch(acl, function (err, graph) {
        if (err || !graph || graph.statements.length === 0) {
          // TODO
          // If no file is found and we want to Control,
          // we should not be able to do that!
          // Control is only to Read and Write the current file!
          // if (mode === 'Control') {
          //   return next(new Error("You can't Control an unexisting file"))
          // }
          if (err) debug('Error: ' + err)
          accessType = 'defaultForNew'
          return next()
        }
        self.findRule(
          graph, // The ACL graph
          user, // The webId of the user
          mode, // Read/Write/Append
          resource, // The resource we want to access
          accessType, // accessTo or defaultForNew
          acl, // The current Acl file!
          function (err) {
          return next(!err || err)
        }, options)
      })
    },
    function (err) {
      if (err === false || err === null) {
        debug('No ACL resource found - access allowed')
        err = new Error('No Access Control Policy found')
      }

      if (err === true) {
        debug('ACL policy found')
        err = null
      }

      if (err) {
        debug('Error: ' + err.message)
        if (!user || user.length === 0) {
          debug('Authentication required')
          err.status = 401
          err.message = 'Access to ' + resource + ' requires authorization'
        } else {
          debug(mode + ' access denied for: ' + user)
          err.status = 403
          err.message = 'Access denied for ' + user
        }
      }

      return callback(err)
    })
}

ACL.prototype.findAgentClass = function (graph, user, mode, resource, acl, callback) {
  var self = this

  // Agent class statement
  var agentClassStatements = self.match(
    graph,
    acl,
    'http://www.w3.org/ns/auth/acl#agentClass',
    undefined)

  if (agentClassStatements.length === 0) {
    return callback(false)
  }

  async.some(agentClassStatements, function (agentClassTriple, found) {
    // Check for FOAF groups
    debug('Found agentClass policy')
    if (agentClassTriple.object.uri === 'http://xmlns.com/foaf/0.1/Agent') {
      debug(mode + ' allowed access as FOAF agent')
      return found(true)
    }

    return found(false)
  }, callback)
}

ACL.prototype.findRule = function (graph, user, mode, resource, accessType, acl, callback, options) {
  var self = this

  // TODO check if this is necessary
  if (graph.statements.length === 0) {
    debug('ACL ' + acl + ' is empty')
    return callback(new Error('No policy found'))
  }

  debug('Found policies in ' + acl)

  // Check for mode
  var statements = self.getMode(graph, mode)
  if (mode === 'Append') {
    statements = statements
      .concat(self.getMode(graph, 'Write'))
  }

  async.some(
    statements,
    function (statement, done) {
      var statementSubject = statement.subject.uri

      // Check for origin
      var matchOrigin = self.matchOrigin(graph, statementSubject, options.origin)
      if (!matchOrigin) {
        debug('The request does not match the origin')
        return done(false)
      }

      // Check for accessTo/defaultForNew
      if (!self.isAcl(resource) || accessType === 'defaultForNew') {
        debug('Checking for accessType:' + accessType)
        var accesses = self.matchAccessType(graph, statementSubject, accessType, resource)
        if (!accesses) {
          debug('Cannot find accessType ' + accessType)
          return done(false)
        }
      }

      // Check for Agent
      var agentStatements = []

      if (user) {
        agentStatements = self.match(
          graph,
          statementSubject,
          'http://www.w3.org/ns/auth/acl#agent',
          user)
      }

      if (agentStatements.length) {
        debug(mode + ' access allowed (as agent) for: ' + user)
        return done(true)
      }

      debug('Inspect agentClass')
      // Check for AgentClass
      return self.findAgentClass(graph, user, mode, resource, statementSubject, done)
    },
    function (found) {
      if (!found) {
        return callback(new Error('Acl found but policy not found'))
      }
      return callback(null)
    })
}

// TODO maybe these functions can be integrated in the code
ACL.prototype.getMode = function getMode (graph, mode) {
  var self = this
  return self.match(
    graph,
    undefined,
    'http://www.w3.org/ns/auth/acl#mode',
    'http://www.w3.org/ns/auth/acl#' + mode)
}

ACL.prototype.matchAccessType = function matchAccessType (graph, rule, accessType, uri) {
  var self = this
  var matches = self.match(
    graph,
    rule,
    'http://www.w3.org/ns/auth/acl#' + accessType,
    undefined)

  return matches.some(function(match) {
    return S(uri).startsWith(match.object.uri);
  })

}

ACL.prototype.matchOrigin = function getOrigins (graph, rule, origin) {
  var self = this
  var origins = self.match(
    graph,
    rule,
    'http://www.w3.org/ns/auth/acl#origin',
    undefined)

  if (origins.length) {
    return origins.some(function (triple) {
      return triple.object.uri === origin
    })
  }

  return true
}

function possibleACLs (uri, suffix) {
  var first = S(uri).endsWith(suffix) ? uri : uri + '.acl'
  var urls = [first]
  var parsedUri = url.parse(uri)
  var baseUrl = (parsedUri.protocol ? parsedUri.protocol + '//' : '') + (parsedUri.host || '')
  if (baseUrl + '/' === uri) {
    return urls
  }

  var times = parsedUri.pathname.split('/').length
  for (var i = 0; i < times - 1; i++) {
    uri = path.dirname(uri)
    urls.push(uri + (uri[uri.length - 1] === '/' ? '.acl' : '/.acl'))
  }
  return urls
}

function fetchDocument (ldp, baseUri) {
    return function (uri, callback) {
        var graph = $rdf.graph();
        async.waterfall([
            function (cb) {
                // URL is local
                var newPath = S(uri).chompLeft(baseUri).s;
                // TODO prettify this
                var documentPath = utils.uriToFilename(newPath, ldp.root);
                var documentUri = url.parse(documentPath);
                documentPath = documentUri.pathname;
                return ldp.readFile(documentPath, cb);
            },
            function (body, cb) {
                try {
                    $rdf.parse(body, graph, uri, 'text/turtle');
                } catch(err) {
                    console.log(err)
                    return cb(err, graph);
                }
                return cb(null, graph);
            }
        ], callback);
    }
}

function getUserId (req, callback) {
    var onBehalfOf = req.get('On-Behalf-Of')
    if (!onBehalfOf) {
        return callback(null, req.session.userId);
    }

    var delegator = rdfVocab.debrack(onBehalfOf);
    verifyDelegator(delegator, req.session.userId, function(err, res) {
        if (res) {
            debug("Request User ID (delegation) :" + delegator);
            return callback(null, delegator);
        }
        return callback(null, req.session.userId);
    });
};

function verifyDelegator (ldp, baseUri, delegator, delegatee, callback) {
    fetchDocument(ldp, baseUri)(delegator, function(err, delegatorGraph) {

        // TODO handle error
        var delegatesStatements = delegatorGraph
            .each(delegatorGraph.sym(delegator),
                  delegatorGraph.sym("http://www.w3.org/ns/auth/acl#delegates"),
                  undefined);

        for (var delegateeIndex in delegatesStatements) {
            var delegateeValue = delegatesStatements[delegateeIndex];
            if (rdfVocab.debrack(delegateeValue.toString()) === delegatee) {
                callback(null, true);
            }
        }
        // TODO check if this should be false
        return callback(null, false);
    });
};
/**
 * Callback used by verifyDelegator.
 * @callback ACL~verifyDelegator_cb
 * @param {Object} err Error occurred when reading the acl file
 * @param {Number} err.status Status code of the error (HTTP way)
 * @param {String} err.message Reason of the error
 * @param {Boolean} result verification has passed or not
 */

function allow (mode) {
    return function (req, res, next) {
        var ldp = req.app.locals.ldp;
        if (!ldp.webid) {
            return next();
        }
        var baseUri = utils.uriBase(req)

        var acl = new ACL({
            fetch: fetchDocument(ldp, baseUri),
            match: match,
            suffix: ldp.suffixAcl
        })

        getUserId(req, function(err, userId) {
            if (err) return callback(err);

            var reqPath = res && res.locals && res.locals.path ? res.locals.path : req.path;
            var options = {
                origin: req.get('origin')
            }
            return acl.can(userId, mode, baseUri + reqPath, next, options)
        })
    }
}

exports.allow = allow;
<commit_msg>type in the debugging message<commit_after>/*jslint node: true*/
/*jshint loopfunc:true */
"use strict";
var glob = require('glob');
var path = require('path');
var $rdf = require('rdflib');
var request = require('request');
var S = require('string');
var url = require('url');
var async = require('async');

var debug = require('./debug').ACL;
var utils = require('./utils.js');
var ns = require('./vocab/ns.js').ns;
var rdfVocab = require('./vocab/rdf.js');
var HttpError = require('./http-error');
var ACL = require('solid-acl');

// TODO should this be set?
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

function match (graph, s, p, o) {
    var matches = graph.statementsMatching(
      s ? $rdf.sym(s) : undefined,
      p ? $rdf.sym(p) : undefined,
      o ? $rdf.sym(o) : undefined);
    return matches
}

function ACL (opts) {
  var self = this
  opts = opts || {}

  self.fetch = opts.fetch
  self.match = opts.match || match
  self.suffix = opts.suffix || '.acl'
}

ACL.prototype.isAcl = function (resource) {
  return !!S(resource).endsWith(this.suffix)
}

ACL.prototype.can = function (user, mode, resource, callback, options) {
  debug('Can ' + user || 'an agent' + ' ' + mode + ' ' + resource + '?')
  var self = this
  var accessType = 'accessTo'
  var acls = possibleACLs(resource, self.suffix)
  options = options || {}

  // If it is an ACL, only look for control this resource
  if (self.isAcl(resource)) {
    mode = 'Control'
  }

  async.eachSeries(
    acls,
    // Looks for ACL, if found, looks for a rule
    function (acl, next) {
      debug('Check if acl exist: ' + acl)

      // Let's see if there is a file..
      self.fetch(acl, function (err, graph) {
        if (err || !graph || graph.statements.length === 0) {
          // TODO
          // If no file is found and we want to Control,
          // we should not be able to do that!
          // Control is only to Read and Write the current file!
          // if (mode === 'Control') {
          //   return next(new Error("You can't Control an unexisting file"))
          // }
          if (err) debug('Error: ' + err)
          accessType = 'defaultForNew'
          return next()
        }
        self.findRule(
          graph, // The ACL graph
          user, // The webId of the user
          mode, // Read/Write/Append
          resource, // The resource we want to access
          accessType, // accessTo or defaultForNew
          acl, // The current Acl file!
          function (err) {
          return next(!err || err)
        }, options)
      })
    },
    function (err) {
      if (err === false || err === null) {
        debug('No ACL resource found - access not allowed')
        err = new Error('No Access Control Policy found')
      }

      if (err === true) {
        debug('ACL policy found')
        err = null
      }

      if (err) {
        debug('Error: ' + err.message)
        if (!user || user.length === 0) {
          debug('Authentication required')
          err.status = 401
          err.message = 'Access to ' + resource + ' requires authorization'
        } else {
          debug(mode + ' access denied for: ' + user)
          err.status = 403
          err.message = 'Access denied for ' + user
        }
      }

      return callback(err)
    })
}

ACL.prototype.findAgentClass = function (graph, user, mode, resource, acl, callback) {
  var self = this

  // Agent class statement
  var agentClassStatements = self.match(
    graph,
    acl,
    'http://www.w3.org/ns/auth/acl#agentClass',
    undefined)

  if (agentClassStatements.length === 0) {
    return callback(false)
  }

  async.some(agentClassStatements, function (agentClassTriple, found) {
    // Check for FOAF groups
    debug('Found agentClass policy')
    if (agentClassTriple.object.uri === 'http://xmlns.com/foaf/0.1/Agent') {
      debug(mode + ' allowed access as FOAF agent')
      return found(true)
    }

    return found(false)
  }, callback)
}

ACL.prototype.findRule = function (graph, user, mode, resource, accessType, acl, callback, options) {
  var self = this

  // TODO check if this is necessary
  if (graph.statements.length === 0) {
    debug('ACL ' + acl + ' is empty')
    return callback(new Error('No policy found'))
  }

  debug('Found policies in ' + acl)

  // Check for mode
  var statements = self.getMode(graph, mode)
  if (mode === 'Append') {
    statements = statements
      .concat(self.getMode(graph, 'Write'))
  }

  async.some(
    statements,
    function (statement, done) {
      var statementSubject = statement.subject.uri

      // Check for origin
      var matchOrigin = self.matchOrigin(graph, statementSubject, options.origin)
      if (!matchOrigin) {
        debug('The request does not match the origin')
        return done(false)
      }

      // Check for accessTo/defaultForNew
      if (!self.isAcl(resource) || accessType === 'defaultForNew') {
        debug('Checking for accessType:' + accessType)
        var accesses = self.matchAccessType(graph, statementSubject, accessType, resource)
        if (!accesses) {
          debug('Cannot find accessType ' + accessType)
          return done(false)
        }
      }

      // Check for Agent
      var agentStatements = []

      if (user) {
        agentStatements = self.match(
          graph,
          statementSubject,
          'http://www.w3.org/ns/auth/acl#agent',
          user)
      }

      if (agentStatements.length) {
        debug(mode + ' access allowed (as agent) for: ' + user)
        return done(true)
      }

      debug('Inspect agentClass')
      // Check for AgentClass
      return self.findAgentClass(graph, user, mode, resource, statementSubject, done)
    },
    function (found) {
      if (!found) {
        return callback(new Error('Acl found but policy not found'))
      }
      return callback(null)
    })
}

// TODO maybe these functions can be integrated in the code
ACL.prototype.getMode = function getMode (graph, mode) {
  var self = this
  return self.match(
    graph,
    undefined,
    'http://www.w3.org/ns/auth/acl#mode',
    'http://www.w3.org/ns/auth/acl#' + mode)
}

ACL.prototype.matchAccessType = function matchAccessType (graph, rule, accessType, uri) {
  var self = this
  var matches = self.match(
    graph,
    rule,
    'http://www.w3.org/ns/auth/acl#' + accessType,
    undefined)

  return matches.some(function(match) {
    return S(uri).startsWith(match.object.uri);
  })

}

ACL.prototype.matchOrigin = function getOrigins (graph, rule, origin) {
  var self = this
  var origins = self.match(
    graph,
    rule,
    'http://www.w3.org/ns/auth/acl#origin',
    undefined)

  if (origins.length) {
    return origins.some(function (triple) {
      return triple.object.uri === origin
    })
  }

  return true
}

function possibleACLs (uri, suffix) {
  var first = S(uri).endsWith(suffix) ? uri : uri + '.acl'
  var urls = [first]
  var parsedUri = url.parse(uri)
  var baseUrl = (parsedUri.protocol ? parsedUri.protocol + '//' : '') + (parsedUri.host || '')
  if (baseUrl + '/' === uri) {
    return urls
  }

  var times = parsedUri.pathname.split('/').length
  for (var i = 0; i < times - 1; i++) {
    uri = path.dirname(uri)
    urls.push(uri + (uri[uri.length - 1] === '/' ? '.acl' : '/.acl'))
  }
  return urls
}

function fetchDocument (ldp, baseUri) {
    return function (uri, callback) {
        var graph = $rdf.graph();
        async.waterfall([
            function (cb) {
                // URL is local
                var newPath = S(uri).chompLeft(baseUri).s;
                // TODO prettify this
                var documentPath = utils.uriToFilename(newPath, ldp.root);
                var documentUri = url.parse(documentPath);
                documentPath = documentUri.pathname;
                return ldp.readFile(documentPath, cb);
            },
            function (body, cb) {
                try {
                    $rdf.parse(body, graph, uri, 'text/turtle');
                } catch(err) {
                    console.log(err)
                    return cb(err, graph);
                }
                return cb(null, graph);
            }
        ], callback);
    }
}

function getUserId (req, callback) {
    var onBehalfOf = req.get('On-Behalf-Of')
    if (!onBehalfOf) {
        return callback(null, req.session.userId);
    }

    var delegator = rdfVocab.debrack(onBehalfOf);
    verifyDelegator(delegator, req.session.userId, function(err, res) {
        if (res) {
            debug("Request User ID (delegation) :" + delegator);
            return callback(null, delegator);
        }
        return callback(null, req.session.userId);
    });
};

function verifyDelegator (ldp, baseUri, delegator, delegatee, callback) {
    fetchDocument(ldp, baseUri)(delegator, function(err, delegatorGraph) {

        // TODO handle error
        var delegatesStatements = delegatorGraph
            .each(delegatorGraph.sym(delegator),
                  delegatorGraph.sym("http://www.w3.org/ns/auth/acl#delegates"),
                  undefined);

        for (var delegateeIndex in delegatesStatements) {
            var delegateeValue = delegatesStatements[delegateeIndex];
            if (rdfVocab.debrack(delegateeValue.toString()) === delegatee) {
                callback(null, true);
            }
        }
        // TODO check if this should be false
        return callback(null, false);
    });
};
/**
 * Callback used by verifyDelegator.
 * @callback ACL~verifyDelegator_cb
 * @param {Object} err Error occurred when reading the acl file
 * @param {Number} err.status Status code of the error (HTTP way)
 * @param {String} err.message Reason of the error
 * @param {Boolean} result verification has passed or not
 */

function allow (mode) {
    return function (req, res, next) {
        var ldp = req.app.locals.ldp;
        if (!ldp.webid) {
            return next();
        }
        var baseUri = utils.uriBase(req)

        var acl = new ACL({
            fetch: fetchDocument(ldp, baseUri),
            match: match,
            suffix: ldp.suffixAcl
        })

        getUserId(req, function(err, userId) {
            if (err) return callback(err);

            var reqPath = res && res.locals && res.locals.path ? res.locals.path : req.path;
            var options = {
                origin: req.get('origin')
            }
            return acl.can(userId, mode, baseUri + reqPath, next, options)
        })
    }
}

exports.allow = allow;
<|endoftext|>


<commit_before>$(document).ready(function() {
	$('.swiper-slide').bind('DOMSubtreeModified', function(e) { // Track segment changes
		setTimeout(triggerHook, 50);
	});

	function triggerHook () {
		var hook = $('.swiper-slide-active .slide-hook'); // Find custom hook in segment content, see notes at end.
		if (hook) {
			var slide_num = $(hook).attr('data-slide');
			$('iframe').contents().find('*[nr="'+ slide_num +'"] .slideButton').click(); // Trigger the jump by faking a click on the hidden slide button.
		}
	}

	// EXTRA: Presentation Mode //

	var pMode = false;

	window.addEventListener('keydown', function(k) {
		switch(k.keyCode) {
        case 'p':
        	if (!pMode) {
        		$('.linkSocialContainer').hide();
        		$('iframe').contents().find('#slideDeckContainer').hide();
        		pMode = true;
        	} else {
        		$('.linkSocialContainer').show();
        		$('iframe').contents().find('#slideDeckContainer').show();
        		pMode = false;
        	}
            break;
        default:
            break;
    }
});

// NOTES //

// Add this to each segment in the story map to hook to a particular bookmark in the scene.
//<commit_msg>Fixed brackets error<commit_after>$(document).ready(function() {
    $('.swiper-slide').bind('DOMSubtreeModified', function(e) { // Track segment changes
        setTimeout(triggerHook, 50);
    });

    function triggerHook () {
        var hook = $('.swiper-slide-active .slide-hook'); // Find custom hook in segment content, see notes at end.
        if (hook) {
            var slide_num = $(hook).attr('data-slide');
            $('iframe').contents().find('*[nr="'+ slide_num +'"] .slideButton').click(); // Trigger the jump by faking a click on the hidden slide button.
        }
    }

    // EXTRA: Presentation Mode //

    var pMode = false;

    window.addEventListener('keydown', function(k) {
        switch(k.keyCode) {
        case 'p':
            if (!pMode) {
                $('.linkSocialContainer').hide();
                $('iframe').contents().find('#slideDeckContainer').hide();
                pMode = true;
            } else {
                $('.linkSocialContainer').show();
                $('iframe').contents().find('#slideDeckContainer').show();
                pMode = false;
            }
            break;
        default:
            break;
        }
    });
});

// NOTES //

// Add this to each segment in the story map to hook to a particular bookmark in the scene.
// <|endoftext|>


<commit_before>  var io = require('socket.io').listen(8080);

   var players = {}, count = 0;

io.sockets.on('connection',
    function (socket)
    {
        var id = count++; //assign number
        var player = players[id] = new Player(id, 10, 10, 1); //create new      player
    player.socket = socket;

    socket.on('chat',
        function (data)
        {
            var message = player.name + ': ' + data.message;
            socket.emit('chat', { message: message});
            socket.broadcast.emit('chat', { message: message});
        });

    socket.on('position',
        function (data)
        {
            player.x = data.playerX;
            player.y = data.playerY;
            player.z = data.z;

            socket.emit('position', {pid : player.id, playerX : player.x, playerY : player.y, z : player.z});
            socket.broadcast.emit('position', {pid : player.id, playerX : player.x, playerY : player.y, z : player.z});
        });

    socket.on('register',
        function (name)
        {
            player.name = name;
            player.registered = true;
            socket.emit('chat', { message : "Server: bem vindo ao server " + player.name + '!'});
            socket.broadcast.emit('chat', { message : "Server: bem vindo ao server " + player.name + '!'});

            for(var p in players)
            {
                //send initial update
                if(!players[p].disconnected)
                {
                    socket.emit('addPlayer', {pid: players[p].id, x: players[p].x, y: players[p].y, name: players[p].name, sessionid: socket.id});
                    if(players[p].id != player.id)
                        players[p].socket.emit('addPlayer', {pid: player.id, x: player.x, y: player.y, name: player.name})
                }
            }

        });

    socket.on('disconnect',
        function (socket)
        {
            player.disconnected = true;
            delete players[player.id];
            player = null;
        });

});

 function Player(id, x, y, z) {
    this.id = id;
   this.x = x;
   this.y = y;
   this.z = z;
    this.name = "";
    this.disconnected = false;
    this.registered = false;
    this.socket = null;
 }
<commit_msg>teste.js<commit_after>var PORT = process.env.OPENSHIFT_INTERNAL_PORT || process.env.OPENSHIFT_NODEJS_PORT  || 8080;
var IPADDRESS = process.env.OPENSHIFT_INTERNAL_IP || process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1';

  var io = require('socket.io').listen(8000);

   var players = {}, count = 0;

io.sockets.on('connection',
    function (socket)
    {
        var id = count++; //assign number
        var player = players[id] = new Player(id, 10, 10, 1); //create new      player
    player.socket = socket;

    socket.on('chat',
        function (data)
        {
            var message = player.name + ': ' + data.message;
            socket.emit('chat', { message: message});
            socket.broadcast.emit('chat', { message: message});
        });

    socket.on('position',
        function (data)
        {
            player.x = data.playerX;
            player.y = data.playerY;
            player.z = data.z;

            socket.emit('position', {pid : player.id, playerX : player.x, playerY : player.y, z : player.z});
            socket.broadcast.emit('position', {pid : player.id, playerX : player.x, playerY : player.y, z : player.z});
        });

    socket.on('register',
        function (name)
        {
            player.name = name;
            player.registered = true;
            socket.emit('chat', { message : "Server: bem vindo ao server " + player.name + '!'});
            socket.broadcast.emit('chat', { message : "Server: bem vindo ao server " + player.name + '!'});

            for(var p in players)
            {
                //send initial update
                if(!players[p].disconnected)
                {
                    socket.emit('addPlayer', {pid: players[p].id, x: players[p].x, y: players[p].y, name: players[p].name, sessionid: socket.id});
                    if(players[p].id != player.id)
                        players[p].socket.emit('addPlayer', {pid: player.id, x: player.x, y: player.y, name: player.name})
                }
            }

        });

    socket.on('disconnect',
        function (socket)
        {
            player.disconnected = true;
            delete players[player.id];
            player = null;
        });

});

 function Player(id, x, y, z) {
    this.id = id;
   this.x = x;
   this.y = y;
   this.z = z;
    this.name = "";
    this.disconnected = false;
    this.registered = false;
    this.socket = null;
 }
<|endoftext|>


<commit_before>'use strict';

var settings_local = {};

var settings_path = process.env.GALAXY_API_SETTINGS;
if (settings_path) {
  if (settings_path[0] !== '.' &&
      settings_path[0] !== '/' &&
      settings_path[0] !== '~') {
    // Assume it's a relative path.
    settings_path = './' + settings_path;
  }
  console.log('Using settings file ' + settings_path);
  settings_local = require(settings_path);
}

exports.DEBUG = false;
exports.ORIGIN = 'http://localhost:4000';

exports.HOST = '0.0.0.0';
exports.PORT = 4000;

// Usage: postgres://user:password@host/database
exports.POSTGRES_URL = 'postgres://localhost/galaxy-api';

exports.SECRET = 'a secret string';

Object.keys(settings_local).forEach(function (k) {
  exports[k] = settings_local[k];
});
<commit_msg>simplify `settings.js`<commit_after>'use strict';

module.exports = {
  DEBUG: false,
  ORIGIN: 'http://localhost:4000',
  HOST: '0.0.0.0',
  PORT: 4000,

  // Usage: postgres://user:password@host/database
  POSTGRES_URL: 'postgres://localhost/galaxy-api',

  // Change this!
  SECRET: 'a secret string'
};

var settings_path = process.env.GALAXY_API_SETTINGS;

if (settings_path) {
  var settings_local = {};

  if (settings_path.substr(0, 2) !== './') {
    // Assume it's a relative path.
    settings_path = './' + settings_path;
  }

  console.log('Using settings file ' + settings_path);
  settings_local = require(settings_path);

  Object.keys(settings_local).forEach(function (k) {
    module.exports[k] = settings_local[k];
  });
}
<|endoftext|>


<commit_before>exports.PORT = process.env.PORT || '5000';
exports.SITE_URL = process.env.SITE_URL || 'http://127.0.0.1:5000';
exports.MONGODB_URL = process.env.MONGODB_URL || process.env.MONGOHQ_URL || 'mongodb://localhost/exposer';
exports.TWITTER_CONSUMER_KEY = process.env.TWITTER_CONSUMER_KEY;
exports.TWITTER_CONSUMER_SECRET = process.env.TWITTER_CONSUMER_SECRET;
exports.TWITTER_ACCESS_TOKEN_KEY = process.env.TWITTER_ACCESS_TOKEN_KEY;
exports.TWITTER_ACCESS_TOKEN_SECRET = process.env.TWITTER_ACCESS_TOKEN_SECRET;
exports.FACEBOOK_APP_ID = process.env.FACEBOOK_APP_ID;
exports.FACEBOOK_PAGE_ID = process.env.FACEBOOK_PAGE_ID;
exports.FACEBOOK_ACCESS_TOKEN = process.env.FACEBOOK_ACCESS_TOKEN;
exports.FACEBOOK_REALTIME_VERIFY_TOKEN = process.env.FACEBOOK_REALTIME_VERIFY_TOKEN;
exports.KEEP_ALIVE_INTERVAL = 25 * 60 * 1000; // ms

exports.TWITTER_QUERY = ['#gotofje', '#gotofsi', '#protesti', '@gotofsi', '@gotofje', '#gotoviso', '#mbprotest', '#ljprotest', '#fertikje', '#demonstracije'];
exports.FACEBOOK_QUERY = ['gotofje', 'gotofsi', 'protesti', 'gotoviso', 'mbprotest', 'ljprotest', 'fertikje', 'demonstracije'];
exports.FACEBOOK_REALTIME_PATHNAME = '/fb/realtime';
exports.MAX_POSTS_PER_REQUEST = 50;
exports.FACEBOOK_POLL_INTERVAL = 3 * 60 * 1000; // ms
exports.FACEBOOK_INTERVAL_BETWEEN_KEYWORDS = 1000; // ms (they say that one request per second rate is OK)
exports.POSTS_FILTER = {
    // Henrik Grubelnik (https://www.facebook.com/henrik.grubelnik) overposting
    'data.from.id': {'$ne': '100000591290520'}
};
<commit_msg>For debugging, keep-alive more often.<commit_after>exports.PORT = process.env.PORT || '5000';
exports.SITE_URL = process.env.SITE_URL || 'http://127.0.0.1:5000';
exports.MONGODB_URL = process.env.MONGODB_URL || process.env.MONGOHQ_URL || 'mongodb://localhost/exposer';
exports.TWITTER_CONSUMER_KEY = process.env.TWITTER_CONSUMER_KEY;
exports.TWITTER_CONSUMER_SECRET = process.env.TWITTER_CONSUMER_SECRET;
exports.TWITTER_ACCESS_TOKEN_KEY = process.env.TWITTER_ACCESS_TOKEN_KEY;
exports.TWITTER_ACCESS_TOKEN_SECRET = process.env.TWITTER_ACCESS_TOKEN_SECRET;
exports.FACEBOOK_APP_ID = process.env.FACEBOOK_APP_ID;
exports.FACEBOOK_PAGE_ID = process.env.FACEBOOK_PAGE_ID;
exports.FACEBOOK_ACCESS_TOKEN = process.env.FACEBOOK_ACCESS_TOKEN;
exports.FACEBOOK_REALTIME_VERIFY_TOKEN = process.env.FACEBOOK_REALTIME_VERIFY_TOKEN;
exports.KEEP_ALIVE_INTERVAL = 60 * 1000; // ms

exports.TWITTER_QUERY = ['#gotofje', '#gotofsi', '#protesti', '@gotofsi', '@gotofje', '#gotoviso', '#mbprotest', '#ljprotest', '#fertikje', '#demonstracije'];
exports.FACEBOOK_QUERY = ['gotofje', 'gotofsi', 'protesti', 'gotoviso', 'mbprotest', 'ljprotest', 'fertikje', 'demonstracije'];
exports.FACEBOOK_REALTIME_PATHNAME = '/fb/realtime';
exports.MAX_POSTS_PER_REQUEST = 50;
exports.FACEBOOK_POLL_INTERVAL = 3 * 60 * 1000; // ms
exports.FACEBOOK_INTERVAL_BETWEEN_KEYWORDS = 1000; // ms (they say that one request per second rate is OK)
exports.POSTS_FILTER = {
    // Henrik Grubelnik (https://www.facebook.com/henrik.grubelnik) overposting
    'data.from.id': {'$ne': '100000591290520'}
};
<|endoftext|>


<commit_before>//    Timeline.js
//    (c) 2010-2011 Tim Hudson
//    Timeline.js may be freely distributed under the MIT license.

(function(context) {

	var cache = [];

	var timeline = {};

	// Add animation object which requires start, stop and callback { start: 0, stop: 0.4, callback: function() {} }
	timeline.add = function(animation) {

		// Cache of all animations
		cache.push(animation);

		return this;
	};

	timeline.remove = function(animation) {

		if (cache.indexOf(animation) !== 1) {
			cache.splice(animation);
		}

		return this;
	};

	timeline.run = function(time) {

		function animate() {
			var l = cache.length;

			for(var i = 0; i < l; i++) {
				var animation = cache[i];

				if (time > animation.start && time < animation.stop) {
					animation.callback(relativePosition(animation.start, animation.stop));
				}
				else if (time >= animation.stop) {
					animation.callback(1);
				}
				else {
					animation.callback(0);
				}
			}
		}

		function relativePosition(start, stop) {
			var length = stop*100000 - start*100000,
				position = time*100000 - start*100000;

			return position / length;
		}

		animate();

		return this;

	};

	context.timeline = timeline;

}(this));<commit_msg>Fix indentation<commit_after>//    Timeline.js
//    (c) 2010-2011 Tim Hudson
//    Timeline.js may be freely distributed under the MIT license.

(function(context) {

  var cache = [];

  var timeline = {};

  // Add animation object which requires start, stop and callback { start: 0, stop: 0.4, callback: function() {} }
  timeline.add = function(animation) {

    // Cache of all animations
    cache.push(animation);

    return this;
  };

  timeline.remove = function(animation) {

    if (cache.indexOf(animation) !== 1) {
      cache.splice(animation);
    }

    return this;
  };

  timeline.run = function(time) {

    function animate() {
      var l = cache.length;

      for(var i = 0; i < l; i++) {
        var animation = cache[i];

        if (time > animation.start && time < animation.stop) {
          animation.callback(relativePosition(animation.start, animation.stop));
        }
        else if (time >= animation.stop) {
          animation.callback(1);
        }
        else {
          animation.callback(0);
        }
      }
    }

    function relativePosition(start, stop) {
      var length = stop*100000 - start*100000,
        position = time*100000 - start*100000;

      return position / length;
    }

    animate();

    return this;

  };

  context.timeline = timeline;

}(this));<|endoftext|>


<commit_before>//app-specific sentence

module.exports = {
  debug : true,
  slackChannel : "#football",
  serverPort : 8080,
  pingInteralInMilliseconds : 1000,// * 60, // server main loop interval.
  postBeforeTheMatch : true,
  postAfterTheMatch : true,
  preMatchWindowInMinutes: 5, // time before the match in minutes when we are ready to rant
  preMatchWindowCloseTimeInMinutesBeforeMatch: 1, //time before the match in minutes when ranting closes
  postMatchWindowInHours: 2,  // time after then match in hours when we are ready to rant
  postMatchWindowCloseTimeInHoursAfterMatch: 24, // time after the match in hours when rant window closes
  GA : 'UA-68256772-1', // Google analytics tracking ID
  gaIgnoreHosts: ['localhost:8080', 'localhost:5000', 'moubot.com'] //list of hosts to be ignored on API paths
};
<commit_msg>prod settings change<commit_after>//app-specific sentence

module.exports = {
  debug : false,
  slackChannel : "#football",
  serverPort : 8080,
  pingInteralInMilliseconds : 1000 * 60, // server main loop interval.
  postBeforeTheMatch : true,
  postAfterTheMatch : true,
  preMatchWindowInMinutes: 5, // time before the match in minutes when we are ready to rant
  preMatchWindowCloseTimeInMinutesBeforeMatch: 1, //time before the match in minutes when ranting closes
  postMatchWindowInHours: 2,  // time after then match in hours when we are ready to rant
  postMatchWindowCloseTimeInHoursAfterMatch: 24, // time after the match in hours when rant window closes
  GA : 'UA-68256772-1', // Google analytics tracking ID
  gaIgnoreHosts: ['localhost:8080', 'localhost:5000', 'moubot.com'] //list of hosts to be ignored on API paths
};
<|endoftext|>


<commit_before>//app-specific sentence

module.exports = {
  debug : false,
  slackChannel : "#football",
  serverPort : 8080,
  pingInteralInMilliseconds : 1000 * 60, // server main loop interval.
  postBeforeTheMatch : true,
  postAfterTheMatch : true,
  preMatchWindowInMinutes: 5, // time before the match in minutes when we are ready to rant
  preMatchWindowCloseTimeInMinutesBeforeMatch: 1, //time before the match in minutes when ranting closes
  postMatchWindowInHours: 2,  // time after then match in hours when we are ready to rant
  postMatchWindowCloseTimeInHoursAfterMatch: 24, // time after the match in hours when rant window closes
  GA : 'UA-68256772-1', // Google analytics tracking ID
  gaIgnoreHosts: ['localhost:8080', 'localhost:5000', 'moubot.com'] //list of hosts to be ignored on API paths
};
<commit_msg>prod settings after branch removed from website branch<commit_after>//app-specific sentence

module.exports = {
  debug : true,
  slackChannel : "#football",
  serverPort : 8080,
  pingInteralInMilliseconds : 1000 * 60, // server main loop interval.
  postBeforeTheMatch : true,
  postAfterTheMatch : true,
  preMatchWindowInMinutes: 5, // time before the match in minutes when we are ready to rant
  preMatchWindowCloseTimeInMinutesBeforeMatch: 1, //time before the match in minutes when ranting closes
  postMatchWindowInHours: 2,  // time after then match in hours when we are ready to rant
  postMatchWindowCloseTimeInHoursAfterMatch: 24, // time after the match in hours when rant window closes
  GA : 'UA-68256772-1', // Google analytics tracking ID
  gaIgnoreHosts: ['localhost:8080', 'localhost:5000', 'moubot.com'] //list of hosts to be ignored on API paths
};
<|endoftext|>


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

module.exports = {
    // By default, Marquette accepts connections on all IPv4 interfaces.
    // The following property can be used to listen on a specific interface. For
    // example, the following would only allow connections from the local machine.
    //uiHost: "127.0.0.1",

    // the tcp port that the Marquette web server is listening on
    uiPort: 1890,

    // the hostname of the MQTT broker to send and receive messages to
    mqttHost: "test.mosquitto.org",

    // the tcp port that the MQTT broker listening on
    //mqttPort: 1883,

    // Retry time in milliseconds for MQTT connections
    mqttReconnectTime: 15000

    // Enabled verbose logging output
    //verbose: true
}
<commit_msg>Use localhost by default, not test.mosquitto.org Makes development on trains easier<commit_after>/**
 * Copyright 2014 Nicholas Humfrey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

module.exports = {
    // By default, Marquette accepts connections on all IPv4 interfaces.
    // The following property can be used to listen on a specific interface. For
    // example, the following would only allow connections from the local machine.
    //uiHost: "127.0.0.1",

    // the tcp port that the Marquette web server is listening on
    uiPort: 1890,

    // the hostname of the MQTT broker to send and receive messages to
    mqttHost: "localhost",

    // the tcp port that the MQTT broker listening on
    //mqttPort: 1883,

    // Retry time in milliseconds for MQTT connections
    mqttReconnectTime: 15000

    // Enabled verbose logging output
    //verbose: true
}
<|endoftext|>