<commit_before>var timerender = (function () {

var exports = {};

var next_timerender_id = 0;

var set_to_start_of_day = function (time) {
    return time.setMilliseconds(0).setSeconds(0).setMinutes(0).setHours(0);
};

// Given an XDate object 'time', return a two-element list containing
//   - a string for the current human-formatted version
//   - a boolean for if it will need to be updated when the day changes
exports.render_now = function (time) {
    var start_of_today = set_to_start_of_day(new XDate());
    var start_of_other_day = set_to_start_of_day(time.clone());

    // How many days old is 'time'? 0 = today, 1 = yesterday, 7 = a
    // week ago, -1 = tomorrow, etc.

    // Presumably the result of diffDays will be an integer in this
    // case, but round it to be sure before comparing to integer
    // constants.
    var days_old = Math.round(start_of_other_day.diffDays(start_of_today));

    if (days_old === 0) {
        return [i18n.t("Today"), true];
    } else if (days_old === 1) {
        return [i18n.t("Yesterday"), true];
    } else if (days_old >= 365) {
        // For long running servers, searching backlog can get ambiguous
        // without a year stamp. Only show year if message is over a year old.
        return [time.toString("MMM\xa0dd,\xa0yyyy"), false];
    }
    // For now, if we get a message from tomorrow, we don't bother
    // rewriting the timestamp when it gets to be tomorrow.

    // "\xa0" is U+00A0 NO-BREAK SPACE.
    // Can't use   as that represents the literal string " ".
    return [time.toString("MMM\xa0dd"), false];
};

// List of the dates that need to be updated when the day changes.
// Each timestamp is represented as a list of length 2:
//   [id of the span element, XDate representing the time]
var update_list = [];

// The time at the beginning of the next day, when the timestamps are updated.
// Represented as an XDate with hour, minute, second, millisecond 0.
var next_update;
$(function () {
    next_update = set_to_start_of_day(new XDate()).addDays(1);
});

// time_above is an optional argument, to support dates that look like:
// --- ▲ Yesterday ▲ ------ ▼ Today ▼ ---
function maybe_add_update_list_entry(needs_update, id, time, time_above) {
    if (needs_update) {
        if (time_above !== undefined) {
            update_list.push([id, time, time_above]);
        } else {
            update_list.push([id, time]);
        }
    }
}

function render_date_span(elem, time_str, time_above_str) {
    elem.text("");
    if (time_above_str !== undefined) {
        return elem.append('' +
                           time_above_str).append($('
')).append(''
                           + time_str);
    }
    return elem.append(time_str);
}

// Given an XDate object 'time', return a DOM node that initially
// displays the human-formatted date, and is updated automatically as
// necessary (e.g. changing "Today" to "Yesterday" to "Jul 1").
// If two dates are given, it renders them as:
// --- ▲ Yesterday ▲ ------ ▼ Today ▼ ---

// (What's actually spliced into the message template is the contents
// of this DOM node as HTML, so effectively a copy of the node. That's
// okay since to update the time later we look up the node by its id.)
exports.render_date = function (time, time_above) {
    var id = "timerender" + next_timerender_id;
    next_timerender_id += 1;
    var rendered_time = exports.render_now(time);
    var node = $("").attr('id', id);
    if (time_above !== undefined) {
        var rendered_time_above = exports.render_now(time_above);
        node = render_date_span(node, rendered_time[0], rendered_time_above[0]);
    } else {
        node = render_date_span(node, rendered_time[0]);
    }
    maybe_add_update_list_entry(rendered_time[1], id, time, time_above);
    return node;
};

// This isn't expected to be called externally except manually for
// testing purposes.
exports.update_timestamps = function () {
    var time = new XDate();
    if (time >= next_update) {
        var to_process = update_list;
        update_list = [];

        _.each(to_process, function (elem) {
            var id = elem[0];
            var element = document.getElementById(id);
            // The element might not exist any more (because it
            // was in the zfilt table, or because we added
            // messages above it and re-collapsed).
            if (element !== null) {
                var time = elem[1];
                var time_above;
                var rendered_time = exports.render_now(time);
                if (elem.length === 3) {
                    time_above = elem[2];
                    var rendered_time_above = exports.render_now(time_above);
                    render_date_span($(element), rendered_time[0], rendered_time_above[0]);
                } else {
                    render_date_span($(element), rendered_time[0]);
                }
                maybe_add_update_list_entry(rendered_time[1], id, time, time_above);
            }
        });

        next_update = set_to_start_of_day(time.clone().addDays(1));
    }
};

setInterval(exports.update_timestamps, 60 * 1000);

// TODO: Remove the duplication with the below; it's a bit tricky
// because the return type models are pretty different.
exports.get_full_time = function (timestamp) {
    var time = new XDate(timestamp * 1000);
    // Convert to number of hours ahead/behind UTC.
    // The sign of getTimezoneOffset() is reversed wrt
    // the conventional meaning of UTC+n / UTC-n
    var tz_offset = -time.getTimezoneOffset() / 60;

    var full_date_str = time.toLocaleDateString();
    var full_time_str = time.toLocaleTimeString() +
        ' (UTC' + ((tz_offset < 0) ? '' : '+') + tz_offset + ')';
    return full_date_str + ' ' + full_time_str;
};

// this is for rendering absolute time based off the preferences for twenty-four
// hour time in the format of "%mmm %d, %h:%m %p".
exports.absolute_time = (function () {
    var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
                  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

    var fmt_time = function (date, H_24) {
        var payload = {
            hours: date.getHours(),
            minutes: date.getMinutes(),
        };

        if (payload.hours > 12 && !H_24) {
            payload.hours -= 12;
            payload.is_pm = true;
        }

        var str = ("0" + payload.hours).slice(-2) + ":" + ("0" + payload.minutes).slice(-2);

        if (!H_24) {
            str += payload.is_pm ? " PM" : " AM";
        }

        return str;
    };

    return function (timestamp) {
        var date = new Date(timestamp);
        var H_24 = page_params.twenty_four_hour_time;

        return MONTHS[date.getMonth()] + " " + date.getDate() + ", " + fmt_time(date, H_24);
    };
}());

// XDate.toLocaleDateString and XDate.toLocaleTimeString are
// expensive, so we delay running the following code until we need
// the full date and time strings.
exports.set_full_datetime = function timerender_set_full_datetime(message, time_elem) {
    if (message.full_date_str !== undefined) {
        return;
    }

    var time = new XDate(message.timestamp * 1000);
    // Convert to number of hours ahead/behind UTC.
    // The sign of getTimezoneOffset() is reversed wrt
    // the conventional meaning of UTC+n / UTC-n
    var tz_offset = -time.getTimezoneOffset() / 60;

    message.full_date_str = time.toLocaleDateString();
    message.full_time_str = time.toLocaleTimeString() +
        ' (UTC' + ((tz_offset < 0) ? '' : '+') + tz_offset + ')';

    time_elem.attr('title', message.full_date_str + ' ' + message.full_time_str);
};

return exports;
}());

if (typeof module !== 'undefined') {
    module.exports = timerender;
}
<commit_msg>message view: Render formal date string as tooltip on recipient row<commit_after>var timerender = (function () {

var exports = {};

var next_timerender_id = 0;

var set_to_start_of_day = function (time) {
    return time.setMilliseconds(0).setSeconds(0).setMinutes(0).setHours(0);
};

// Given an XDate object 'time', return a three-element list containing
//   - a string for the current human-formatted version
//   - a string for the current formally formatted version
//         e.g. "Monday, April 15, 2017"
//   - a boolean for if it will need to be updated when the day changes
exports.render_now = function (time) {
    var start_of_today = set_to_start_of_day(new XDate());
    var start_of_other_day = set_to_start_of_day(time.clone());

    // render formal time to be used as title attr tooltip
    var formal_time_str = time.toString('dddd,\xa0MMMM\xa0d,\xa0yyyy');

    // How many days old is 'time'? 0 = today, 1 = yesterday, 7 = a
    // week ago, -1 = tomorrow, etc.

    // Presumably the result of diffDays will be an integer in this
    // case, but round it to be sure before comparing to integer
    // constants.
    var days_old = Math.round(start_of_other_day.diffDays(start_of_today));

    if (days_old === 0) {
        return [i18n.t("Today"), formal_time_str, true];
    } else if (days_old === 1) {
        return [i18n.t("Yesterday"), formal_time_str, true];
    } else if (days_old >= 365) {
        // For long running servers, searching backlog can get ambiguous
        // without a year stamp. Only show year if message is over a year old.
        return [time.toString("MMM\xa0dd,\xa0yyyy"), formal_time_str, false];
    }
    // For now, if we get a message from tomorrow, we don't bother
    // rewriting the timestamp when it gets to be tomorrow.

    // "\xa0" is U+00A0 NO-BREAK SPACE.
    // Can't use   as that represents the literal string " ".
    return [time.toString("MMM\xa0dd"), formal_time_str, false];
};

// List of the dates that need to be updated when the day changes.
// Each timestamp is represented as a list of length 2:
//   [id of the span element, XDate representing the time]
var update_list = [];

// The time at the beginning of the next day, when the timestamps are updated.
// Represented as an XDate with hour, minute, second, millisecond 0.
var next_update;
$(function () {
    next_update = set_to_start_of_day(new XDate()).addDays(1);
});

// time_above is an optional argument, to support dates that look like:
// --- ▲ Yesterday ▲ ------ ▼ Today ▼ ---
function maybe_add_update_list_entry(needs_update, id, time, time_above) {
    if (needs_update) {
        if (time_above !== undefined) {
            update_list.push([id, time, time_above]);
        } else {
            update_list.push([id, time]);
        }
    }
}

function render_date_span(elem, time_str, time_above_str, time_formal_str) {
    elem.text("");
    if (time_above_str !== undefined) {
        return elem.append('' +
                           time_above_str).append($('
')).append(''
                           + time_str);
    }
    return elem.append(time_str).attr('title', time_formal_str);
}

// Given an XDate object 'time', return a DOM node that initially
// displays the human-formatted date, and is updated automatically as
// necessary (e.g. changing "Today" to "Yesterday" to "Jul 1").
// If two dates are given, it renders them as:
// --- ▲ Yesterday ▲ ------ ▼ Today ▼ ---

// (What's actually spliced into the message template is the contents
// of this DOM node as HTML, so effectively a copy of the node. That's
// okay since to update the time later we look up the node by its id.)
exports.render_date = function (time, time_above) {
    var id = "timerender" + next_timerender_id;
    next_timerender_id += 1;
    var rendered_time = exports.render_now(time);
    var node = $("").attr('id', id);
    if (time_above !== undefined) {
        var rendered_time_above = exports.render_now(time_above);
        node = render_date_span(node, rendered_time[0], rendered_time_above[0], rendered_time[1]);
    } else {
        node = render_date_span(node, rendered_time[0], undefined, rendered_time[1]);
    }
    maybe_add_update_list_entry(rendered_time[2], id, time, time_above);
    return node;
};

// This isn't expected to be called externally except manually for
// testing purposes.
exports.update_timestamps = function () {
    var time = new XDate();
    if (time >= next_update) {
        var to_process = update_list;
        update_list = [];

        _.each(to_process, function (elem) {
            var id = elem[0];
            var element = document.getElementById(id);
            // The element might not exist any more (because it
            // was in the zfilt table, or because we added
            // messages above it and re-collapsed).
            if (element !== null) {
                var time = elem[1];
                var time_above;
                var rendered_time = exports.render_now(time);
                if (elem.length === 3) {
                    time_above = elem[2];
                    var rendered_time_above = exports.render_now(time_above);
                    render_date_span($(element), rendered_time[0], rendered_time_above[0],
                        rendered_time[1]);
                } else {
                    render_date_span($(element), rendered_time[0], undefined, rendered_time[1]);
                }
                maybe_add_update_list_entry(rendered_time[2], id, time, time_above);
            }
        });

        next_update = set_to_start_of_day(time.clone().addDays(1));
    }
};

setInterval(exports.update_timestamps, 60 * 1000);

// TODO: Remove the duplication with the below; it's a bit tricky
// because the return type models are pretty different.
exports.get_full_time = function (timestamp) {
    var time = new XDate(timestamp * 1000);
    // Convert to number of hours ahead/behind UTC.
    // The sign of getTimezoneOffset() is reversed wrt
    // the conventional meaning of UTC+n / UTC-n
    var tz_offset = -time.getTimezoneOffset() / 60;

    var full_date_str = time.toLocaleDateString();
    var full_time_str = time.toLocaleTimeString() +
        ' (UTC' + ((tz_offset < 0) ? '' : '+') + tz_offset + ')';
    return full_date_str + ' ' + full_time_str;
};

// this is for rendering absolute time based off the preferences for twenty-four
// hour time in the format of "%mmm %d, %h:%m %p".
exports.absolute_time = (function () {
    var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
                  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

    var fmt_time = function (date, H_24) {
        var payload = {
            hours: date.getHours(),
            minutes: date.getMinutes(),
        };

        if (payload.hours > 12 && !H_24) {
            payload.hours -= 12;
            payload.is_pm = true;
        }

        var str = ("0" + payload.hours).slice(-2) + ":" + ("0" + payload.minutes).slice(-2);

        if (!H_24) {
            str += payload.is_pm ? " PM" : " AM";
        }

        return str;
    };

    return function (timestamp) {
        var date = new Date(timestamp);
        var H_24 = page_params.twenty_four_hour_time;

        return MONTHS[date.getMonth()] + " " + date.getDate() + ", " + fmt_time(date, H_24);
    };
}());

// XDate.toLocaleDateString and XDate.toLocaleTimeString are
// expensive, so we delay running the following code until we need
// the full date and time strings.
exports.set_full_datetime = function timerender_set_full_datetime(message, time_elem) {
    if (message.full_date_str !== undefined) {
        return;
    }

    var time = new XDate(message.timestamp * 1000);
    // Convert to number of hours ahead/behind UTC.
    // The sign of getTimezoneOffset() is reversed wrt
    // the conventional meaning of UTC+n / UTC-n
    var tz_offset = -time.getTimezoneOffset() / 60;

    message.full_date_str = time.toLocaleDateString();
    message.full_time_str = time.toLocaleTimeString() +
        ' (UTC' + ((tz_offset < 0) ? '' : '+') + tz_offset + ')';

    time_elem.attr('title', message.full_date_str + ' ' + message.full_time_str);
};

return exports;
}());

if (typeof module !== 'undefined') {
    module.exports = timerender;
}
<|endoftext|>


<commit_before>/*
	overthrow responsive sidescroller extension
	(c) 2013: Scott Jehl, Filament Group, Inc.
	MIT License
*/

(function( w, o ){

	if( !o || !"querySelector" in w.document ){
		return;
	}

	function sendEvent( elem, evt, args, ieID ){
		// TODO needs IE8 support
		if( document.createEvent ){
			var event = document.createEvent( "Event" );
			event.initEvent( evt, true, true );
			event.overthrow = args;
			elem.dispatchEvent( event );
		}
		else {
			w.document.documentElement[ieID][ evt ] = {
				e: evt,
				overthrow: args
			};
			w.document.documentElement[ evt ]++;
		}
	}

	o.sidescroller = function( elems, options ){
		var scrolls = elems,
			evtPrefix = "overthrow",
			evtNext = evtPrefix + "-next",
			evtPrev = evtPrefix + "-prev",
			disabledClassStr = " disabled",
			snapScroll = options && options.snapScroll,
			rewind = options && options.rewind,
			snapTolerance = options && options.snapTolerance !== undefined ? options.snapTolerance : 30;

		for( var i = 0; i < scrolls.length; i++ ){

			(function(){

				var thisSideScroll = scrolls[ i ],
					thisScroll = scrolls[ i ].querySelector( ".overthrow" ),
					nextPrev = w.document.createElement( "div" ),
					slideNum = 0,
					ieID = "overthrow" + (new Date().getTime()),
					handled = false;

				// prevent re-init
				if( thisSideScroll.initialized ){
					return;
				}
				thisSideScroll.initialized = true;

				thisSideScroll.setAttribute( "tabindex", "0" );

				// oldIE will need some expando event props
				if( w.document.attachEvent ){
					// these are iterators to trigger a property mutate event in IE8
					w.document.documentElement[ evtPrev ] = 0;
					w.document.documentElement[ evtNext ] = 0;

					// these for for the event data when that property iterates
					w.document.documentElement[ ieID ] = {};
					w.document.documentElement[ ieID ][ evtPrev ] = {};
					w.document.documentElement[ ieID ][ evtNext ] = {};

					thisSideScroll.ieID = ieID;
				}

				nextPrev.className = "sidescroll-nextprev-links";

				nextPrev.innerHTML = "Previous" +
					"Next";

				function setSlideWidths(){
					var slides = thisScroll.querySelectorAll( "li" ),
						percent = 100 / slides.length + "%";
					for( var i = 0; i < slides.length; i++ ){
						slides[ i ].style.width = percent;
					}
				}

				function setScrollableWidth(){
					var slides = thisScroll.querySelectorAll( "li" ),
					  container = thisScroll.querySelector( "ul" );

					container.style.width = (slides[0].offsetWidth * slides.length) + "px";
				}

				function getActiveSlides( left ){
					var slides = thisScroll.querySelectorAll( "li" ),
						numSlides = slides.length,
						slidesWidth = thisScroll.offsetWidth,
						slideWidth = slides[ 0 ].offsetWidth,
						scrollLeft = left !== undefined ? left : thisScroll.scrollLeft,
						startSlide = Math.round( scrollLeft / slideWidth ),
						valid = true,
						ret = [];

					startSlide = Math.max( 0, startSlide );
					startSlide = Math.min( numSlides, startSlide );

					ret[ 0 ] = startSlide;
					for( var i = 1; i < numSlides; i++ ){
						if( startSlide + (i * slideWidth) < slidesWidth){
							ret.push( startSlide + i);
						}
						else{
							valid = false;
						}
					}
					slideNum = startSlide;
					return ret;
				}

				// expose the getactiveslides function on the overthrow element
				thisSideScroll.getActiveSlides = getActiveSlides;

				function handleClick( evt ){

					var e = evt || w.event;

					if( e.preventDefault ){
						e.preventDefault();
					}
					else{
						e.returnValue = false;
					}

					if( e.type === "keydown" || ( handled === false || handled === e.type ) ){
						handled = e.type;

						o.intercept();
						var slides = thisScroll.querySelectorAll( "li" ),
							target = e.target || e.srcElement,
							slidesWidth = thisScroll.offsetWidth,
							slideWidth = slides[ 0 ].offsetWidth,
							currScroll = thisScroll.scrollLeft,
							slideNum = Math.round( currScroll / slideWidth ),
							next = (e.type !== "keydown" && target.className.indexOf( "next" ) > -1) || e.keyCode === 39,
							newSlide = slideNum + ( next ? 1 : -1 ),
							newScroll = slideWidth * newSlide,
							scrollWidth = thisScroll.scrollWidth - slidesWidth;

						// if can't go left, go to end
						if( rewind ){

							if( newScroll < 0 ){
								newScroll = scrollWidth;
							}
							else if( newScroll > scrollWidth ){
								newScroll = 0;
							}
						}
						else {
							if( newScroll < 0 ){
								newScroll = 0;
							}
							else if( newScroll > scrollWidth ){
								newScroll = scrollWidth;
							}
						}

						var newActive = getActiveSlides( newScroll );

						// if the slides are fixed width we want to make sure to show all
						// of the last slide when it's included in the active set. By default
						// the plugin assumes the slidewidth is set to match the width of the
						// scroller, here we have to force the extra scroll distance

						// TODO might be jarring, consider sorting the active slides as a right
						//      offset instead of just forcing the last distance.
						if( options.fixedItemWidth && next
							&& newActive[newActive.length - 1] == slides.length - 1	) {
							newScroll = thisScroll.querySelector( "ul" ).offsetWidth - slidesWidth;
						}

						if( newActive[ 0 ] !== slideNum ){

							o.toss( thisScroll, { left: newScroll } );

							sendEvent(
								thisSideScroll, // elem to receive event
								next ? evtNext : evtPrev, // evt name
								{
									active: newActive, // active slides
									originalEvent: e
								},
								ieID
							);
						}

						setTimeout( function(){ handled = false; }, 900 );
					}
				}

				var scrollStart = false;
				function handleSnap( e ){
					o.intercept();
					var slideWidth = thisScroll.querySelector( "li" ).offsetWidth,
						currScroll = thisScroll.scrollLeft,
						newSlide = Math.round( currScroll / slideWidth );

					if( scrollStart !== false ){
						var distScrolled = currScroll - scrollStart;
						if( Math.abs( distScrolled ) > snapTolerance ){
							newSlide = slideNum + ( distScrolled > 0 ? 1 : -1 );
						}

					}

					var newScroll = slideWidth * newSlide;

					o.toss( thisScroll, { left: newScroll, duration: 20 } );

					if( slideNum !== newSlide ){
						sendEvent(
							thisSideScroll, // elem to receive event
							newSlide > slideNum ? evtNext : evtPrev, // evt name
							{
								active: getActiveSlides( newScroll ), // active slides
								originalEvent: e
							},
							ieID
						);
						slideNum = newSlide;
					}
				}

				var debounce;
				function handleResize( e ){
					clearTimeout(debounce);
					debounce = setTimeout(function(){
						handleSnap( e );
					}, 100);
				}

				var debouncedos;
				function handleScroll( e ){
					if( overthrow.tossing ){
						return;
					}
					if( scrollStart === false ){
						scrollStart = thisScroll.scrollLeft;
					}
					clearTimeout( debouncedos );
					debouncedos = setTimeout(function(){
						if( snapScroll ){
							handleSnap( e );
						} else {
							sendEvent( thisSideScroll,	evtPrefix + "-scroll", {}, ieID );
						}

						scrollStart = false;
					}, 200);
				}

				function handleKey( e ){
					if( e.keyCode === 39 || e.keyCode === 37 ){
						handleClick( e );
					}
				}

				if( w.document.addEventListener ){
					nextPrev.addEventListener( "click", handleClick, false );
					nextPrev.addEventListener( "touchend", handleClick, false );
					w.addEventListener( "resize", handleResize, false );
					scrolls[ i ].addEventListener( "keydown", handleKey, false );

					thisScroll.addEventListener( "scroll", handleScroll, false );
				}
		 		else if( w.document.attachEvent ){
		 			nextPrev.attachEvent( "onclick", handleClick, false );
					w.attachEvent( "onresize", handleResize, false );
					scrolls[ i ].attachEvent( "onkeydown", handleKey, false );

					thisScroll.attachEvent( "onscroll", handleScroll, false );
		 		}

				thisSideScroll.insertBefore( nextPrev, thisScroll );

				if( !options || !options.fixedItemWidth ) {
					setSlideWidths();
				} else {
					setScrollableWidth();
				}

				// Todo this seems really fragile
				// side scroller init for plugins
				sendEvent(
					w.document.documentElement,
					evtPrefix + "-init",
					{ sideScroll: thisSideScroll, options: options },
					w.document.documentElement.ieID
				);
			}());
		}
	};

	// setup the document element to work with overthrow init
	// TODO use the body element, this feels super iffy
	if( w.document.attachEvent ){
		var initId = "overthrow-init" + (new Date().getTime());

		w.document.documentElement[ initId ] = {};
		w.document.documentElement[ initId ][ "overthrow-init" ] = 0;
		w.document.documentElement.ieID = initId;
	}

	o.sidescroller.onEvent = function( evt, elem, callback ){
		function cb( args ){
			var e = {
				type: evt,
				target: elem,
				overthrow: args.overthrow
			};
			callback( e );
		}
		if( w.document.addEventListener ){
			elem.addEventListener( evt, cb );
		}
		else if( w.document.attachEvent ){
			w.document.documentElement.attachEvent( "onpropertychange", function( event ) {
				if( event.propertyName === evt ){
					cb( w.document.documentElement[ elem.ieID ][ evt ] );
				}
			});
		}
	};
}( this, this.overthrow ));<commit_msg>get the correct active slides, move the full scroll distance on the last item in the list<commit_after>/*
	overthrow responsive sidescroller extension
	(c) 2013: Scott Jehl, Filament Group, Inc.
	MIT License
*/

(function( w, o ){

	if( !o || !"querySelector" in w.document ){
		return;
	}

	function sendEvent( elem, evt, args, ieID ){
		// TODO needs IE8 support
		if( document.createEvent ){
			var event = document.createEvent( "Event" );
			event.initEvent( evt, true, true );
			event.overthrow = args;
			elem.dispatchEvent( event );
		}
		else {
			w.document.documentElement[ieID][ evt ] = {
				e: evt,
				overthrow: args
			};
			w.document.documentElement[ evt ]++;
		}
	}

	o.sidescroller = function( elems, options ){
		var scrolls = elems,
			evtPrefix = "overthrow",
			evtNext = evtPrefix + "-next",
			evtPrev = evtPrefix + "-prev",
			disabledClassStr = " disabled",
			snapScroll = options && options.snapScroll,
			rewind = options && options.rewind,
			snapTolerance = options && options.snapTolerance !== undefined ? options.snapTolerance : 30;

		for( var i = 0; i < scrolls.length; i++ ){

			(function(){

				var thisSideScroll = scrolls[ i ],
					thisScroll = scrolls[ i ].querySelector( ".overthrow" ),
					nextPrev = w.document.createElement( "div" ),
					slideNum = 0,
					ieID = "overthrow" + (new Date().getTime()),
					handled = false;

				// prevent re-init
				if( thisSideScroll.initialized ){
					return;
				}
				thisSideScroll.initialized = true;

				thisSideScroll.setAttribute( "tabindex", "0" );

				// oldIE will need some expando event props
				if( w.document.attachEvent ){
					// these are iterators to trigger a property mutate event in IE8
					w.document.documentElement[ evtPrev ] = 0;
					w.document.documentElement[ evtNext ] = 0;

					// these for for the event data when that property iterates
					w.document.documentElement[ ieID ] = {};
					w.document.documentElement[ ieID ][ evtPrev ] = {};
					w.document.documentElement[ ieID ][ evtNext ] = {};

					thisSideScroll.ieID = ieID;
				}

				nextPrev.className = "sidescroll-nextprev-links";

				nextPrev.innerHTML = "Previous" +
					"Next";

				function setSlideWidths(){
					var slides = thisScroll.querySelectorAll( "li" ),
						percent = 100 / slides.length + "%";
					for( var i = 0; i < slides.length; i++ ){
						slides[ i ].style.width = percent;
					}
				}

				function setScrollableWidth(){
					var slides = thisScroll.querySelectorAll( "li" ),
					  container = thisScroll.querySelector( "ul" );

					container.style.width = (slides[0].offsetWidth * slides.length) + "px";
				}

				function getActiveSlides( left ){
					var slides = thisScroll.querySelectorAll( "li" ),
						numSlides = slides.length,
						slidesWidth = thisScroll.offsetWidth,
						slideWidth = slides[ 0 ].offsetWidth,
						scrollLeft = left !== undefined ? left : thisScroll.scrollLeft,
						startSlide = Math.round( scrollLeft / slideWidth ),
						tollerance = 10,
						ret = [];

					startSlide = Math.max( 0, startSlide );
					startSlide = Math.min( numSlides, startSlide );

					ret.push(startSlide);
					for( var i = 2; i < numSlides; i++ ){
						if( i * slideWidth < slidesWidth + tollerance ) {
							ret.push( startSlide + i - 1 );
						}
					}

					slideNum = startSlide;
					return ret;
				}

				// expose the getactiveslides function on the overthrow element
				thisSideScroll.getActiveSlides = getActiveSlides;

				function handleClick( evt ){

					var e = evt || w.event;

					if( e.preventDefault ){
						e.preventDefault();
					}
					else{
						e.returnValue = false;
					}

					if( e.type === "keydown" || ( handled === false || handled === e.type ) ){
						handled = e.type;

						o.intercept();
						var slides = thisScroll.querySelectorAll( "li" ),
							target = e.target || e.srcElement,
							slidesWidth = thisScroll.offsetWidth,
							slideWidth = slides[ 0 ].offsetWidth,
							currScroll = thisScroll.scrollLeft,
							slideNum = Math.round( currScroll / slideWidth ),
							next = (e.type !== "keydown" && target.className.indexOf( "next" ) > -1) || e.keyCode === 39,
							newSlide = slideNum + ( next ? 1 : -1 ),
							newScroll = slideWidth * newSlide,
							scrollWidth = thisScroll.scrollWidth - slidesWidth;

						// if can't go left, go to end
						if( rewind ){

							if( newScroll < 0 ){
								newScroll = scrollWidth;
							}
							else if( newScroll > scrollWidth ){
								newScroll = 0;
							}
						}
						else {
							if( newScroll < 0 ){
								newScroll = 0;
							}
							else if( newScroll > scrollWidth ){
								newScroll = scrollWidth;
							}
						}

						var newActive = getActiveSlides( newScroll );

						// if we're planning to show the last slide, force the scroll out to the
						// end of the scrollable area. Necessary to force partially displayed
						// elements when the scroll is manual (not snapped) or the elements are
						// fixed width

						// TODO might be jarring, consider sorting the active slides as a right
						//			offset instead of just forcing the last distance.
						if( newActive[newActive.length - 1] == slides.length - 1 ) {
							newScroll = thisScroll.querySelector( "ul" ).offsetWidth - slidesWidth;
						}

						// TODO probably only need the second condition1
						if( newActive[ 0 ] !== slideNum || newScroll !== currScroll ){

							o.toss( thisScroll, { left: newScroll } );

							sendEvent(
								thisSideScroll, // elem to receive event
								next ? evtNext : evtPrev, // evt name
								{
									active: newActive, // active slides
									originalEvent: e
								},
								ieID
							);
						}

						setTimeout( function(){ handled = false; }, 900 );
					}
				}

				var scrollStart = false;
				function handleSnap( e ){
					o.intercept();
					var slideWidth = thisScroll.querySelector( "li" ).offsetWidth,
						currScroll = thisScroll.scrollLeft,
						newSlide = Math.round( currScroll / slideWidth );

					if( scrollStart !== false ){
						var distScrolled = currScroll - scrollStart;
						if( Math.abs( distScrolled ) > snapTolerance ){
							newSlide = slideNum + ( distScrolled > 0 ? 1 : -1 );
						}

					}

					var newScroll = slideWidth * newSlide;

					o.toss( thisScroll, { left: newScroll, duration: 20 } );

					if( slideNum !== newSlide ){
						sendEvent(
							thisSideScroll, // elem to receive event
							newSlide > slideNum ? evtNext : evtPrev, // evt name
							{
								active: getActiveSlides( newScroll ), // active slides
								originalEvent: e
							},
							ieID
						);
						slideNum = newSlide;
					}
				}

				var debounce;
				function handleResize( e ){
					clearTimeout(debounce);
					debounce = setTimeout(function(){
						handleSnap( e );
					}, 100);
				}

				var debouncedos;
				function handleScroll( e ){
					if( overthrow.tossing ){
						return;
					}
					if( scrollStart === false ){
						scrollStart = thisScroll.scrollLeft;
					}
					clearTimeout( debouncedos );
					debouncedos = setTimeout(function(){
						if( snapScroll ){
							handleSnap( e );
						} else {
							sendEvent( thisSideScroll,	evtPrefix + "-scroll", {}, ieID );
						}

						scrollStart = false;
					}, 200);
				}

				function handleKey( e ){
					if( e.keyCode === 39 || e.keyCode === 37 ){
						handleClick( e );
					}
				}

				if( w.document.addEventListener ){
					nextPrev.addEventListener( "click", handleClick, false );
					nextPrev.addEventListener( "touchend", handleClick, false );
					w.addEventListener( "resize", handleResize, false );
					scrolls[ i ].addEventListener( "keydown", handleKey, false );

					thisScroll.addEventListener( "scroll", handleScroll, false );
				}
		 		else if( w.document.attachEvent ){
		 			nextPrev.attachEvent( "onclick", handleClick, false );
					w.attachEvent( "onresize", handleResize, false );
					scrolls[ i ].attachEvent( "onkeydown", handleKey, false );

					thisScroll.attachEvent( "onscroll", handleScroll, false );
		 		}

				thisSideScroll.insertBefore( nextPrev, thisScroll );

				if( !options || !options.fixedItemWidth ) {
					setSlideWidths();
				} else {
					setScrollableWidth();
				}

				// Todo this seems really fragile
				// side scroller init for plugins
				sendEvent(
					w.document.documentElement,
					evtPrefix + "-init",
					{ sideScroll: thisSideScroll, options: options },
					w.document.documentElement.ieID
				);
			}());
		}
	};

	// setup the document element to work with overthrow init
	// TODO use the body element, this feels super iffy
	if( w.document.attachEvent ){
		var initId = "overthrow-init" + (new Date().getTime());

		w.document.documentElement[ initId ] = {};
		w.document.documentElement[ initId ][ "overthrow-init" ] = 0;
		w.document.documentElement.ieID = initId;
	}

	o.sidescroller.onEvent = function( evt, elem, callback ){
		function cb( args ){
			var e = {
				type: evt,
				target: elem,
				overthrow: args.overthrow
			};
			callback( e );
		}
		if( w.document.addEventListener ){
			elem.addEventListener( evt, cb );
		}
		else if( w.document.attachEvent ){
			w.document.documentElement.attachEvent( "onpropertychange", function( event ) {
				if( event.propertyName === evt ){
					cb( w.document.documentElement[ elem.ieID ][ evt ] );
				}
			});
		}
	};
}( this, this.overthrow ));<|endoftext|>


<commit_before><commit_msg>Update project umbrella to 1.1.2<commit_after><|endoftext|>


<commit_before>'use strict';

/**
 * The processor handling a single asset.
 */

var Q = require('q'),
  path = require('path'),
  fs = require('fs'),
  _ = require('lodash'),
  assetMapping = require('../../lib/asset-mapping.js'),
  cip = require('../../lib/services/natmus-cip'),
  config = require('../../lib/config/config');

var DATA_REGEXP = new RegExp('\\d+');
var CM_PR_IN = 2.54;

const PREFIXED_NUMBERS_LETTERS_AND_DOTS = /^[\dA-Z\.]+ (- )?/;
const PREFIXED_SPECIAL_CASE_ONE = /^\w+\d\w\s-\s/;

const CONFIG_DIR = path.join(__dirname, '..', '..', 'lib', 'config');
const TAGS_BLACKLIST_PATH = path.join(CONFIG_DIR, 'tags-blacklist.txt');
var tagsBlacklist = fs.readFileSync(TAGS_BLACKLIST_PATH).toString();
    // Remove any linebreak from Linux, Windows or Mac and seperate tags
    tagsBlacklist = tagsBlacklist.replace(/(\r\n|\n|\r)/gm,'\n').split('\n');

const TAGS_VISION_FIELD = '{6864395c-c433-2148-8b05-56edf606d4d4}';

function relatedFilenameComparison(assetA, assetB) {
  var filenameA = assetA.filename;
  var filenameB = assetB.filename;
  return filenameA.localeCompare(filenameB);
}

function saveVisionTags(metadata, tags) {
  var values = {};
  values[TAGS_VISION_FIELD] = tags;

  return cip.initSession().then(function(nm) {
    return cip.setFieldValues(nm, metadata.catalog, metadata.id, 'web', values);
  });
}

// This list of transformations are a list of functions that takes two
// arguments (cip_client, metadata) and returns a mutated metadata, which
// is passed on to the next function in the list.
var METADATA_TRANSFORMATIONS = [
  function transformFieldNames(state, metadata) {
    var transformedMetadata = assetMapping.formatResult(metadata);
    // The catalog will be removed when formatting.
    transformedMetadata.catalog = metadata.catalog;
    return transformedMetadata;
  },
  function transformModificationTime(state, metadata) {
    var modificationTime = DATA_REGEXP.exec(metadata['modification_time']);
    if (modificationTime && modificationTime.length > 0) {
      metadata['modification_time'] = parseInt(modificationTime[0], 10);
    }
    return metadata;
  },
  function transformCategoriesAndDeriveSuggest(state, metadata) {
    // Transforms the categories.
    if (metadata.categories !== undefined) {
      metadata['categories_int'] = [];

      for (var j = 0; j < metadata.categories.length; ++j) {
        if (metadata.categories[j].path.indexOf('$Categories') !== 0) {
          continue;
        }
        var category = metadata.categories[j].id;
        var path = state.categories[metadata.catalog].getPath(category);
        if (path) {
          for (var k = 0; k < path.length; k++) {
            metadata['categories_int'].push(path[k].id);
            if (path[k].name.indexOf('$Categories') === 0) {
              continue;
            }
          }
        }
      }
    }
    return metadata;
  },
  function transformRelations(state, metadata) {
    // Transforms the binary representations of each relation.
    metadata['related_master_assets'] = cip.parseBinaryRelations(
      metadata['related_master_assets']);
    metadata['related_sub_assets'] = cip.parseBinaryRelations(
      metadata['related_sub_assets']);
    // Sort these by their filename.
    metadata['related_master_assets'].sort(relatedFilenameComparison);
    metadata['related_sub_assets'].sort(relatedFilenameComparison);
    return metadata;
  },
  function derive_in_artifact_rotation_series(state, metadata) {
    // Let's assume it's not.
    metadata.in_artifact_rotation_series = false;
    // Finds out if this asset is in a rotation series.
    // Initially looking at the assets categories - is this the front
    // facing asset in the rotation series?
    for (var c in metadata.categories) {
      var category = metadata.categories[c];
      if (category.name === 'Rotationsbilleder') {
        metadata.in_artifact_rotation_series = true;
        // The asset in Rotationsbilleder is always rank 0.
        metadata.artifact_rotation_series_rank = 0;
        return metadata;
      }
    }
    // Loop through the assets related master assets to find if a
    // master asset is in fact in the correct category to make this
    // asset a part of an artifact rotation series.
    var rotationalMasterAsset;
    for (var r in metadata.related_master_assets) {
      var masterAsset = metadata.related_master_assets[r];
      if (masterAsset.relation === '9ed0887f-40e8-4091-a91c-de356c869251') {
        rotationalMasterAsset = masterAsset;
      }
    }

    // TODO: Implement this as a post-processing step reading asset metadata
    // of related assets from the index instead of from the CIP.
    // If we found a rotational master asset.
    if (rotationalMasterAsset) {
      // Get the asset's metadata, to check it's categories.
      return cip.getAsset(state.cip, metadata.catalog, rotationalMasterAsset.id)
        .then(function(masterAsset) {
          var masterAssetMetadata = masterAsset.fields;
          masterAssetMetadata = assetMapping.formatResult(
            masterAssetMetadata);
          for (var c in masterAssetMetadata.categories) {
            var category = masterAssetMetadata.categories[c];
            if (category.name === 'Rotationsbilleder') {
              metadata.in_artifact_rotation_series = true;
              // TODO: Consider computing the artifact_rotation_series_rank
              return metadata;
            }
          }
          return metadata;
        }, function(reason) {
          // An error occured when getting the master asset, let's not fail
          // on this sub-asset just because of that.
          console.error('Could not get the master asset', reason);
          // Let's return a new promise to overwrite the failed.
          return new Q(metadata);
        });
    } else {
      return metadata;
    }
  },
  function derive_dimensions_in_cm(state, metadata) {
    metadata.width_cm = metadata.width_in * CM_PR_IN;
    metadata.height_cm = metadata.height_in * CM_PR_IN;
    return metadata;
  },
  function derive_is_searchable(state, metadata) {
    // Compute a value on if it's drafted, part of a rotational series
    // or an original that has more representable croppings.
    // Adds an is_searchable field to the metadata.
    metadata.is_searchable = true; // Let's assume that it is.
    if (metadata.cropping_status &&
      metadata.cropping_status.id === 2) {
      // The croping status is 'has been cropped' / 'Er friskåret'
      metadata.is_searchable = false;
    } else if (!metadata.review_state ||
      (metadata.review_state.id !== 3 && metadata.review_state.id !== 4)) {
      // The asset's review state is neither 3 or 4 (public).
      metadata.is_searchable = false;
    } else if (metadata.in_artifact_rotation_series &&
      metadata.artifact_rotation_series_rank !== 0) {
      // The asset is part of a rotation series but it's not the front.
      metadata.is_searchable = false;
    }
    // Return the updated metedata.
    return metadata;
  },
  function derive_latitude_and_longitude(state, metadata) {
    var coordinates;
    if (metadata.google_maps_coordinates) {
      coordinates = metadata.google_maps_coordinates;
    } else if (metadata.google_maps_coordinates_crowd) {
      coordinates = metadata.google_maps_coordinates_crowd;
    }
    if (coordinates) {
      coordinates = coordinates.split(',').map(parseFloat);
      if (coordinates.length >= 2) {
        metadata.latitude = coordinates[0];
        metadata.longitude = coordinates[1];
      } else {
        throw new Error(
          'Encountered unexpected format when parsing coordinates.');
      }
    }
    return metadata;
  },
  function derive_tags(state, metadata) {
    var tagsPerCategory = metadata.categories.map(function(category) {
      var catalogsCategoryTree = state.categories[metadata.catalog];
      var path = catalogsCategoryTree.getPath(category.id) || [];
      return path.map(function(categoryOnPath) {
        var name = categoryOnPath.name;
        // Categories that starts with the dollar-sign are system categories.
        if (name.indexOf('$') === 0) {
          return null;
        }
        // Remove prefixed numbers, letters and dots.
        name = name.replace(PREFIXED_NUMBERS_LETTERS_AND_DOTS, '');
        // Remove special prefix e.g. "F01a - "
        name = name.replace(PREFIXED_SPECIAL_CASE_ONE, '');
        // We made it this far - let's consider tags lowercase only
        name = name.toLowerCase();
        // Don't include catalogs that are blacklisted.
        if (tagsBlacklist.indexOf(name) !== -1) {
          return null;
        }
        // Let's lower the case.
        return name;
      });
    });

    // Concat all the tags from every path into a single array.
    metadata.tags = _.union.apply(null, tagsPerCategory).filter(function(tag) {
      return !!tag; // Filter out null or undefined values.
    }).sort();

    return metadata;
  },
  function deriveVisionTags(state, metadata) {
    // Let's save some cost and bandwidth and not analyze the asset unless
    // explicitly told. As in run only if one of the indexVison args are specified.
    var shouldRun = state.indexVisionTagsForce || state.indexVisionTags;
    // And run only if the asset doesn't have tags already or if we force it.
    shouldRun = shouldRun && (!metadata.tags_vision || state.indexVisionTagsForce);
    // Return if we shouldn't be here.
    if (!shouldRun) { return metadata; }
    // Still here. Let's grab the image directly from Cumulus.
    var url = config.cipBaseURL + '/preview/thumbnail/' + metadata.catalog + '/' + metadata.id;

    return motif.fetchSuggestions(url).then(function (tags) {
      // Filter out tags that are blacklisted and convert tags to string.
      var filteredTags = tags.filter(function (tag) {
        // Include the tag if it's not in the blacklist
        if (tagsBlacklist.indexOf(tag) === -1) {
          return true;
        }
      }).join();

      // Save the tags to Cumulus
      return saveVisionTags(metadata, filteredTags).then(function(response) {
        if(response.statusCode !== 200) {
          throw new Error('Failed to set the field values');
        }
        metadata.tags_vision = filteredTags;
        return metadata;
      });
    });
  },
  function transformTags (state, metadata) {
    // We want Cumulus to have a string, but elasticsearch to have an array.
    if (metadata.tags_vision && typeof(metadata.tags_vision) === 'string'){
      metadata.tags_vision = metadata.tags_vision.split(',');
    }
    if (metadata.tags_crowd && typeof(metadata.tags_crowd) === 'string'){
      metadata.tags_crowd = metadata.tags_crowd.split(',');
    }
    return metadata;
  }
  /*function cracy_fails(state, metadata) {
  	throw new Error('Catch me if you can ... ' + metadata.id);
  }*/
];

function transformMetadata(state, metadata, transformations) {
  return transformations.reduce(function(metadata, transformation) {
    return Q.when(metadata).then(function(metadata) {
      return transformation(state, metadata);
    });
  }, new Q(metadata));
}

function processAsset(state, metadata, transformations) {
  //console.log('Processing an asset.');
  // Use all transformations by default.
  if (typeof(transformations) === 'undefined') {
    transformations = METADATA_TRANSFORMATIONS;
  }
  // Perform additional transformations and index the result.
  return transformMetadata(state, metadata, transformations)
    .then(function(metadata) {
      var es_id = metadata.catalog + '-' + metadata.id;
      return state.es.index({
        index: process.env.ES_INDEX || 'assets',
        type: 'asset',
        id: es_id,
        body: metadata
      });
    })
    .then(function(resp) {
      console.log('Successfully indexed ' + resp._id);
      return resp._id;
    }, function(err) {
      err.catalogAlias = metadata.catalog;
      err.assetId = metadata.id;
      console.error(err.stack);
      return err;
    });
}

module.exports = processAsset;
module.exports.METADATA_TRANSFORMATIONS = METADATA_TRANSFORMATIONS;
<commit_msg>Fixed motif-tagging.js not being loaded in assets proccesing<commit_after>'use strict';

/**
 * The processor handling a single asset.
 */

var Q = require('q'),
  path = require('path'),
  fs = require('fs'),
  _ = require('lodash'),
  assetMapping = require('../../lib/asset-mapping.js'),
  cip = require('../../lib/services/natmus-cip'),
  config = require('../../lib/config/config');

var DATA_REGEXP = new RegExp('\\d+');
var CM_PR_IN = 2.54;

const PREFIXED_NUMBERS_LETTERS_AND_DOTS = /^[\dA-Z\.]+ (- )?/;
const PREFIXED_SPECIAL_CASE_ONE = /^\w+\d\w\s-\s/;

const CONFIG_DIR = path.join(__dirname, '..', '..', 'lib', 'config');
const TAGS_BLACKLIST_PATH = path.join(CONFIG_DIR, 'tags-blacklist.txt');
var tagsBlacklist = fs.readFileSync(TAGS_BLACKLIST_PATH).toString();
    // Remove any linebreak from Linux, Windows or Mac and seperate tags
    tagsBlacklist = tagsBlacklist.replace(/(\r\n|\n|\r)/gm,'\n').split('\n');

const TAGS_VISION_FIELD = '{6864395c-c433-2148-8b05-56edf606d4d4}';

function relatedFilenameComparison(assetA, assetB) {
  var filenameA = assetA.filename;
  var filenameB = assetB.filename;
  return filenameA.localeCompare(filenameB);
}

function saveVisionTags(metadata, tags) {
  var values = {};
  values[TAGS_VISION_FIELD] = tags;

  return cip.initSession().then(function(nm) {
    return cip.setFieldValues(nm, metadata.catalog, metadata.id, 'web', values);
  });
}

// This list of transformations are a list of functions that takes two
// arguments (cip_client, metadata) and returns a mutated metadata, which
// is passed on to the next function in the list.
var METADATA_TRANSFORMATIONS = [
  function transformFieldNames(state, metadata) {
    var transformedMetadata = assetMapping.formatResult(metadata);
    // The catalog will be removed when formatting.
    transformedMetadata.catalog = metadata.catalog;
    return transformedMetadata;
  },
  function transformModificationTime(state, metadata) {
    var modificationTime = DATA_REGEXP.exec(metadata['modification_time']);
    if (modificationTime && modificationTime.length > 0) {
      metadata['modification_time'] = parseInt(modificationTime[0], 10);
    }
    return metadata;
  },
  function transformCategoriesAndDeriveSuggest(state, metadata) {
    // Transforms the categories.
    if (metadata.categories !== undefined) {
      metadata['categories_int'] = [];

      for (var j = 0; j < metadata.categories.length; ++j) {
        if (metadata.categories[j].path.indexOf('$Categories') !== 0) {
          continue;
        }
        var category = metadata.categories[j].id;
        var path = state.categories[metadata.catalog].getPath(category);
        if (path) {
          for (var k = 0; k < path.length; k++) {
            metadata['categories_int'].push(path[k].id);
            if (path[k].name.indexOf('$Categories') === 0) {
              continue;
            }
          }
        }
      }
    }
    return metadata;
  },
  function transformRelations(state, metadata) {
    // Transforms the binary representations of each relation.
    metadata['related_master_assets'] = cip.parseBinaryRelations(
      metadata['related_master_assets']);
    metadata['related_sub_assets'] = cip.parseBinaryRelations(
      metadata['related_sub_assets']);
    // Sort these by their filename.
    metadata['related_master_assets'].sort(relatedFilenameComparison);
    metadata['related_sub_assets'].sort(relatedFilenameComparison);
    return metadata;
  },
  function derive_in_artifact_rotation_series(state, metadata) {
    // Let's assume it's not.
    metadata.in_artifact_rotation_series = false;
    // Finds out if this asset is in a rotation series.
    // Initially looking at the assets categories - is this the front
    // facing asset in the rotation series?
    for (var c in metadata.categories) {
      var category = metadata.categories[c];
      if (category.name === 'Rotationsbilleder') {
        metadata.in_artifact_rotation_series = true;
        // The asset in Rotationsbilleder is always rank 0.
        metadata.artifact_rotation_series_rank = 0;
        return metadata;
      }
    }
    // Loop through the assets related master assets to find if a
    // master asset is in fact in the correct category to make this
    // asset a part of an artifact rotation series.
    var rotationalMasterAsset;
    for (var r in metadata.related_master_assets) {
      var masterAsset = metadata.related_master_assets[r];
      if (masterAsset.relation === '9ed0887f-40e8-4091-a91c-de356c869251') {
        rotationalMasterAsset = masterAsset;
      }
    }

    // TODO: Implement this as a post-processing step reading asset metadata
    // of related assets from the index instead of from the CIP.
    // If we found a rotational master asset.
    if (rotationalMasterAsset) {
      // Get the asset's metadata, to check it's categories.
      return cip.getAsset(state.cip, metadata.catalog, rotationalMasterAsset.id)
        .then(function(masterAsset) {
          var masterAssetMetadata = masterAsset.fields;
          masterAssetMetadata = assetMapping.formatResult(
            masterAssetMetadata);
          for (var c in masterAssetMetadata.categories) {
            var category = masterAssetMetadata.categories[c];
            if (category.name === 'Rotationsbilleder') {
              metadata.in_artifact_rotation_series = true;
              // TODO: Consider computing the artifact_rotation_series_rank
              return metadata;
            }
          }
          return metadata;
        }, function(reason) {
          // An error occured when getting the master asset, let's not fail
          // on this sub-asset just because of that.
          console.error('Could not get the master asset', reason);
          // Let's return a new promise to overwrite the failed.
          return new Q(metadata);
        });
    } else {
      return metadata;
    }
  },
  function derive_dimensions_in_cm(state, metadata) {
    metadata.width_cm = metadata.width_in * CM_PR_IN;
    metadata.height_cm = metadata.height_in * CM_PR_IN;
    return metadata;
  },
  function derive_is_searchable(state, metadata) {
    // Compute a value on if it's drafted, part of a rotational series
    // or an original that has more representable croppings.
    // Adds an is_searchable field to the metadata.
    metadata.is_searchable = true; // Let's assume that it is.
    if (metadata.cropping_status &&
      metadata.cropping_status.id === 2) {
      // The croping status is 'has been cropped' / 'Er friskåret'
      metadata.is_searchable = false;
    } else if (!metadata.review_state ||
      (metadata.review_state.id !== 3 && metadata.review_state.id !== 4)) {
      // The asset's review state is neither 3 or 4 (public).
      metadata.is_searchable = false;
    } else if (metadata.in_artifact_rotation_series &&
      metadata.artifact_rotation_series_rank !== 0) {
      // The asset is part of a rotation series but it's not the front.
      metadata.is_searchable = false;
    }
    // Return the updated metedata.
    return metadata;
  },
  function derive_latitude_and_longitude(state, metadata) {
    var coordinates;
    if (metadata.google_maps_coordinates) {
      coordinates = metadata.google_maps_coordinates;
    } else if (metadata.google_maps_coordinates_crowd) {
      coordinates = metadata.google_maps_coordinates_crowd;
    }
    if (coordinates) {
      coordinates = coordinates.split(',').map(parseFloat);
      if (coordinates.length >= 2) {
        metadata.latitude = coordinates[0];
        metadata.longitude = coordinates[1];
      } else {
        throw new Error(
          'Encountered unexpected format when parsing coordinates.');
      }
    }
    return metadata;
  },
  function derive_tags(state, metadata) {
    var tagsPerCategory = metadata.categories.map(function(category) {
      var catalogsCategoryTree = state.categories[metadata.catalog];
      var path = catalogsCategoryTree.getPath(category.id) || [];
      return path.map(function(categoryOnPath) {
        var name = categoryOnPath.name;
        // Categories that starts with the dollar-sign are system categories.
        if (name.indexOf('$') === 0) {
          return null;
        }
        // Remove prefixed numbers, letters and dots.
        name = name.replace(PREFIXED_NUMBERS_LETTERS_AND_DOTS, '');
        // Remove special prefix e.g. "F01a - "
        name = name.replace(PREFIXED_SPECIAL_CASE_ONE, '');
        // We made it this far - let's consider tags lowercase only
        name = name.toLowerCase();
        // Don't include catalogs that are blacklisted.
        if (tagsBlacklist.indexOf(name) !== -1) {
          return null;
        }
        // Let's lower the case.
        return name;
      });
    });

    // Concat all the tags from every path into a single array.
    metadata.tags = _.union.apply(null, tagsPerCategory).filter(function(tag) {
      return !!tag; // Filter out null or undefined values.
    }).sort();

    return metadata;
  },
  function deriveVisionTags(state, metadata) {
    // Let's save some cost and bandwidth and not analyze the asset unless
    // explicitly told. As in run only if one of the indexVison args are specified.
    var shouldRun = state.indexVisionTagsForce || state.indexVisionTags;
    // And run only if the asset doesn't have tags already or if we force it.
    shouldRun = shouldRun && (!metadata.tags_vision || state.indexVisionTagsForce);
    // Return if we shouldn't be here.
    if (!shouldRun) { return metadata; }
    // Still here. Let's grab the image directly from Cumulus.
    var url = config.cipBaseURL + '/preview/thumbnail/' + metadata.catalog + '/' + metadata.id;

    // Loading here to prevent circular dependency.
    var motif = require('../../lib/controllers/motif-tagging');

    return motif.fetchSuggestions(url).then(function (tags) {
      // Filter out tags that are blacklisted and convert tags to string.
      var filteredTags = tags.filter(function (tag) {
        // Include the tag if it's not in the blacklist
        if (tagsBlacklist.indexOf(tag) === -1) {
          return true;
        }
      }).join();

      // Save the tags to Cumulus
      return saveVisionTags(metadata, filteredTags).then(function(response) {
        if(response.statusCode !== 200) {
          throw new Error('Failed to set the field values');
        }
        metadata.tags_vision = filteredTags;
        return metadata;
      });
    });
  },
  function transformTags (state, metadata) {
    // We want Cumulus to have a string, but elasticsearch to have an array.
    if (metadata.tags_vision && typeof(metadata.tags_vision) === 'string'){
      metadata.tags_vision = metadata.tags_vision.split(',');
    }
    if (metadata.tags_crowd && typeof(metadata.tags_crowd) === 'string'){
      metadata.tags_crowd = metadata.tags_crowd.split(',');
    }
    return metadata;
  }
  /*function cracy_fails(state, metadata) {
  	throw new Error('Catch me if you can ... ' + metadata.id);
  }*/
];

function transformMetadata(state, metadata, transformations) {
  return transformations.reduce(function(metadata, transformation) {
    return Q.when(metadata).then(function(metadata) {
      return transformation(state, metadata);
    });
  }, new Q(metadata));
}

function processAsset(state, metadata, transformations) {
  //console.log('Processing an asset.');
  // Use all transformations by default.
  if (typeof(transformations) === 'undefined') {
    transformations = METADATA_TRANSFORMATIONS;
  }
  // Perform additional transformations and index the result.
  return transformMetadata(state, metadata, transformations)
    .then(function(metadata) {
      var es_id = metadata.catalog + '-' + metadata.id;
      return state.es.index({
        index: process.env.ES_INDEX || 'assets',
        type: 'asset',
        id: es_id,
        body: metadata
      });
    })
    .then(function(resp) {
      console.log('Successfully indexed ' + resp._id);
      return resp._id;
    }, function(err) {
      err.catalogAlias = metadata.catalog;
      err.assetId = metadata.id;
      console.error(err.stack);
      return err;
    });
}

module.exports = processAsset;
module.exports.METADATA_TRANSFORMATIONS = METADATA_TRANSFORMATIONS;
<|endoftext|>


<commit_before>const path = require('path');
const gzipPreprocessor = require('./gzip/gzippreprocessor');
const resolve = (pkg) => path.join(__dirname, '../../packages', pkg, 'dist', 'index.dev.esm.js');

const customLaunchers = {
  slChrome: {
    base: 'SauceLabs',
    browserName: 'chrome',
    browserVersion: 'latest',
    platform: "Windows 11"
  },
  slSafari14: {
    base: 'SauceLabs',
    browserName: 'safari',
    browserVersion: '14'
  },
  slSafari15: {
    base: 'SauceLabs',
    browserName: 'safari',
    browserVersion: '15'
  },
  slEdge: {
    base: 'SauceLabs',
    browserName: 'MicrosoftEdge',
    browserVersion: 'latest',
    platform: 'Windows 11'
  },
  sl_mac_chrome: {
    base: 'SauceLabs',
    browserName: 'chrome',
    browserVersion: 'latest',
    platform: 'macOS 12'
  },
  slFirefox: {
    base: 'SauceLabs',
    browserName: 'firefox',
    platform: "Windows 11",
    browserVersion: 'latest'
  }
};

module.exports = function (config) {
  config.set({
    basePath: '../../',

    frameworks: ['jasmine'],

    files: [path.join(__dirname, '../../fixtures/browser/test.index.js')],

    preprocessors: {
      './fixtures/browser/test.index.js': ['webpack', 'gzip']
    },

    client: {
      jasmine: {
        random: false // Adding jasmine.random false disables test random order
      }
    },

    reporters: ['dots', 'saucelabs'],
    sauceLabs: {
      build: 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')',
      tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
      startConnect: true,
      testName: `InfernoJS`,
      recordVideo: false,
      recordScreenshots: false,
      videoUploadOnPass: false,
      recordLogs: false,
      captureHtml: false,
      commandTimeout: 400
    },

    plugins: ['karma-jasmine', 'karma-webpack', gzipPreprocessor, 'karma-sauce-launcher'],

    customLaunchers: customLaunchers,
    browsers: Object.keys(customLaunchers),

    reportSlowerThan: 2000,

    captureTimeout: 600000,
    browserNoActivityTimeout: 600000,
    browserDisconnectTolerance: 2,
    processKillTimeout: 20000,
    browserDisconnectTimeout: 10000,

    browserConsoleLogOptions: {
      level: 'warn',
      terminal: false
    },
    colors: true,
    singleRun: true,
    autoWatch: false,
    concurrency: 1,

    webpack: {
      devtool: false,
      mode: 'none',
      optimization: {
        splitChunks: false,
        runtimeChunk: false,
        minimize: true
      },
      target: ['web', 'es5'],
      module: {
        rules: [
          {
            test: /\.(js|jsx|tsx|ts)$/,
            loader: path.join(__dirname, 'node_modules/babel-loader'),
            options: {
              babelrc: false,
              presets: [
                [
                  '@babel/preset-env',
                  {
                    loose: true,
                    targets: {
                      ie: '11',
                      safari: '8'
                    }
                  }
                ],
                '@babel/typescript'
              ],
              plugins: [
                ['babel-plugin-inferno', { imports: true }],
                ['@babel/plugin-proposal-class-properties', { loose: true }]
              ]
            }
          }
        ]
      },
      resolve: {
        alias: {
          inferno: resolve('inferno'),
          'inferno-animation': resolve('inferno-animation'),
          'inferno-compat': resolve('inferno-compat'),
          'inferno-create-class': resolve('inferno-create-class'),
          'inferno-create-element': resolve('inferno-create-element'),
          'inferno-hydrate': resolve('inferno-hydrate'),
          'inferno-extras': resolve('inferno-extras'),
          'inferno-hyperscript': resolve('inferno-hyperscript'),
          'inferno-mobx': resolve('inferno-mobx'),
          'inferno-redux': resolve('inferno-redux'),
          'inferno-router': resolve('inferno-router'),
          'inferno-server': resolve('inferno-server'),
          'inferno-shared': resolve('inferno-shared'),
          'inferno-test-utils': resolve('inferno-test-utils'),
          'inferno-utils': path.join(__dirname, '../../packages', 'inferno-utils', 'src', 'index.ts'),
          'inferno-vnode-flags': resolve('inferno-vnode-flags'),
          'inferno-clone-vnode': resolve('inferno-clone-vnode'),
          mobx: path.join(__dirname, '../../node_modules/mobx/dist/mobx.esm.js')
        },
        extensions: ['.js', '.jsx', '.tsx', '.ts'],
        mainFields: ['browser', 'main']
      },
      devServer: {
        noInfo: true
      },
      stats: 'errors-only',
      performance: {
        hints: false
      }
    }
  });
};
<commit_msg>Added platform to saucelabs safari configs<commit_after>const path = require('path');
const gzipPreprocessor = require('./gzip/gzippreprocessor');
const resolve = (pkg) => path.join(__dirname, '../../packages', pkg, 'dist', 'index.dev.esm.js');

const customLaunchers = {
  slChrome: {
    base: 'SauceLabs',
    browserName: 'chrome',
    browserVersion: 'latest',
    platform: "Windows 11"
  },
  slSafari14: {
    base: 'SauceLabs',
    browserName: 'safari',
    browserVersion: '14',
    platform: 'macOS 11'
  },
  slSafari15: {
    base: 'SauceLabs',
    browserName: 'safari',
    browserVersion: '15',
    platform: 'macOS 12'
  },
  slEdge: {
    base: 'SauceLabs',
    browserName: 'MicrosoftEdge',
    browserVersion: 'latest',
    platform: 'Windows 11'
  },
  sl_mac_chrome: {
    base: 'SauceLabs',
    browserName: 'chrome',
    browserVersion: 'latest',
    platform: 'macOS 12'
  },
  slFirefox: {
    base: 'SauceLabs',
    browserName: 'firefox',
    platform: "Windows 11",
    browserVersion: 'latest'
  }
};

module.exports = function (config) {
  config.set({
    basePath: '../../',

    frameworks: ['jasmine'],

    files: [path.join(__dirname, '../../fixtures/browser/test.index.js')],

    preprocessors: {
      './fixtures/browser/test.index.js': ['webpack', 'gzip']
    },

    client: {
      jasmine: {
        random: false // Adding jasmine.random false disables test random order
      }
    },

    reporters: ['dots', 'saucelabs'],
    sauceLabs: {
      build: 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')',
      tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
      startConnect: true,
      testName: `InfernoJS`,
      recordVideo: false,
      recordScreenshots: false,
      videoUploadOnPass: false,
      recordLogs: false,
      captureHtml: false,
      commandTimeout: 400
    },

    plugins: ['karma-jasmine', 'karma-webpack', gzipPreprocessor, 'karma-sauce-launcher'],

    customLaunchers: customLaunchers,
    browsers: Object.keys(customLaunchers),

    reportSlowerThan: 2000,

    captureTimeout: 600000,
    browserNoActivityTimeout: 600000,
    browserDisconnectTolerance: 2,
    processKillTimeout: 20000,
    browserDisconnectTimeout: 10000,

    browserConsoleLogOptions: {
      level: 'warn',
      terminal: false
    },
    colors: true,
    singleRun: true,
    autoWatch: false,
    concurrency: 1,

    webpack: {
      devtool: false,
      mode: 'none',
      optimization: {
        splitChunks: false,
        runtimeChunk: false,
        minimize: true
      },
      target: ['web', 'es5'],
      module: {
        rules: [
          {
            test: /\.(js|jsx|tsx|ts)$/,
            loader: path.join(__dirname, 'node_modules/babel-loader'),
            options: {
              babelrc: false,
              presets: [
                [
                  '@babel/preset-env',
                  {
                    loose: true,
                    targets: {
                      ie: '11',
                      safari: '8'
                    }
                  }
                ],
                '@babel/typescript'
              ],
              plugins: [
                ['babel-plugin-inferno', { imports: true }],
                ['@babel/plugin-proposal-class-properties', { loose: true }]
              ]
            }
          }
        ]
      },
      resolve: {
        alias: {
          inferno: resolve('inferno'),
          'inferno-animation': resolve('inferno-animation'),
          'inferno-compat': resolve('inferno-compat'),
          'inferno-create-class': resolve('inferno-create-class'),
          'inferno-create-element': resolve('inferno-create-element'),
          'inferno-hydrate': resolve('inferno-hydrate'),
          'inferno-extras': resolve('inferno-extras'),
          'inferno-hyperscript': resolve('inferno-hyperscript'),
          'inferno-mobx': resolve('inferno-mobx'),
          'inferno-redux': resolve('inferno-redux'),
          'inferno-router': resolve('inferno-router'),
          'inferno-server': resolve('inferno-server'),
          'inferno-shared': resolve('inferno-shared'),
          'inferno-test-utils': resolve('inferno-test-utils'),
          'inferno-utils': path.join(__dirname, '../../packages', 'inferno-utils', 'src', 'index.ts'),
          'inferno-vnode-flags': resolve('inferno-vnode-flags'),
          'inferno-clone-vnode': resolve('inferno-clone-vnode'),
          mobx: path.join(__dirname, '../../node_modules/mobx/dist/mobx.esm.js')
        },
        extensions: ['.js', '.jsx', '.tsx', '.ts'],
        mainFields: ['browser', 'main']
      },
      devServer: {
        noInfo: true
      },
      stats: 'errors-only',
      performance: {
        hints: false
      }
    }
  });
};
<|endoftext|>


<commit_before>function Acceleration(x, y, z)
{
  this.x = x;
  this.y = y;
  this.z = z;
  this.timestamp = new Date().getTime();
  this.win = null;
  this.fail = null;
}

var accelListeners = [];

/**
 * This class provides access to device accelerometer data.
 * @constructor
 */
function Accelerometer() {
	/**
	 * The last known acceleration.
	 */
	this.lastAcceleration = null;
}

/**
 * Asynchronously aquires the current acceleration.
 * @param {Function} successCallback The function to call when the acceleration
 * data is available
 * @param {Function} errorCallback The function to call when there is an error
 * getting the acceleration data.
 * @param {AccelerationOptions} options The options for getting the accelerometer data
 * such as timeout.
 */
Accelerometer.prototype.getCurrentAcceleration = function(successCallback, errorCallback, options) {
	// If the acceleration is available then call success
	// If the acceleration is not available then call error

	// Created for iPhone, Iphone passes back _accel obj litteral
	if (typeof successCallback == "function") {
		if(this.lastAcceleration)
		  successCallback(accel);
		else
		{
			watchAcceleration(this.gotCurrentAcceleration, this.fail);
		}
	}
}

Accelerometer.prototype.gotAccel = function(key, x, y, z)
{
	console.log('we won');
    var a = new Acceleration(x,y,z);
    a.x = x;
    a.y = y;
    a.x = z;
    a.win = accelListeners[key].win;
    a.fail = accelListeners[key].fail;
    this.timestamp = new Date().getTime();
    this.lastAcceleration = a;
    accelListeners[key] = a;
    if (typeof a.win == "function") {
      a.win(a);
    }
}

/**
 * Asynchronously aquires the acceleration repeatedly at a given interval.
 * @param {Function} successCallback The function to call each time the acceleration
 * data is available
 * @param {Function} errorCallback The function to call when there is an error
 * getting the acceleration data.
 * @param {AccelerationOptions} options The options for getting the accelerometer data
 * such as timeout.
 */

Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallback, options) {
	// TODO: add the interval id to a list so we can clear all watches
  var frequency = (options != undefined)? options.frequency : 10000;
  var accel = Acceleration(0,0,0);
  accel.win = successCallback;
  accel.fail = errorCallback;
  accel.opts = options;
  var key = accelListeners.push( accel ) - 1;
  Accel.start(frequency, key);
}

/**
 * Clears the specified accelerometer watch.
 * @param {String} watchId The ID of the watch returned from #watchAcceleration.
 */
Accelerometer.prototype.clearWatch = function(watchId) {
	Accel.stop(watchId);
}

Accelerometer.prototype.epicFail = function(watchId, message) {
  accelWatcher[key].fail();
}

PhoneGap.addConstructor(function() {
    if (typeof navigator.accelerometer == "undefined") navigator.accelerometer = new Accelerometer();
});
<commit_msg>Removed console.log in accel<commit_after>function Acceleration(x, y, z)
{
  this.x = x;
  this.y = y;
  this.z = z;
  this.timestamp = new Date().getTime();
  this.win = null;
  this.fail = null;
}

var accelListeners = [];

/**
 * This class provides access to device accelerometer data.
 * @constructor
 */
function Accelerometer() {
	/**
	 * The last known acceleration.
	 */
	this.lastAcceleration = null;
}

/**
 * Asynchronously aquires the current acceleration.
 * @param {Function} successCallback The function to call when the acceleration
 * data is available
 * @param {Function} errorCallback The function to call when there is an error
 * getting the acceleration data.
 * @param {AccelerationOptions} options The options for getting the accelerometer data
 * such as timeout.
 */
Accelerometer.prototype.getCurrentAcceleration = function(successCallback, errorCallback, options) {
	// If the acceleration is available then call success
	// If the acceleration is not available then call error

	// Created for iPhone, Iphone passes back _accel obj litteral
	if (typeof successCallback == "function") {
		if(this.lastAcceleration)
		  successCallback(accel);
		else
		{
			watchAcceleration(this.gotCurrentAcceleration, this.fail);
		}
	}
}

Accelerometer.prototype.gotAccel = function(key, x, y, z)
{
    var a = new Acceleration(x,y,z);
    a.x = x;
    a.y = y;
    a.x = z;
    a.win = accelListeners[key].win;
    a.fail = accelListeners[key].fail;
    this.timestamp = new Date().getTime();
    this.lastAcceleration = a;
    accelListeners[key] = a;
    if (typeof a.win == "function") {
      a.win(a);
    }
}

/**
 * Asynchronously aquires the acceleration repeatedly at a given interval.
 * @param {Function} successCallback The function to call each time the acceleration
 * data is available
 * @param {Function} errorCallback The function to call when there is an error
 * getting the acceleration data.
 * @param {AccelerationOptions} options The options for getting the accelerometer data
 * such as timeout.
 */

Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallback, options) {
	// TODO: add the interval id to a list so we can clear all watches
  var frequency = (options != undefined)? options.frequency : 10000;
  var accel = Acceleration(0,0,0);
  accel.win = successCallback;
  accel.fail = errorCallback;
  accel.opts = options;
  var key = accelListeners.push( accel ) - 1;
  Accel.start(frequency, key);
}

/**
 * Clears the specified accelerometer watch.
 * @param {String} watchId The ID of the watch returned from #watchAcceleration.
 */
Accelerometer.prototype.clearWatch = function(watchId) {
	Accel.stop(watchId);
}

Accelerometer.prototype.epicFail = function(watchId, message) {
  accelWatcher[key].fail();
}

PhoneGap.addConstructor(function() {
    if (typeof navigator.accelerometer == "undefined") navigator.accelerometer = new Accelerometer();
});
<|endoftext|>


<commit_before>exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db";
//exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;";
//exports.connectionString = process.env.ODBC_CONNETION_STRING;

exports.connectionObject = {
	DRIVER : "{SQLITE3}",
	DATABASE : "data/sqlite-test.db"
};

exports.connections = [
	{
		DRIVER : "{SQLITE3}",
		DATABASE : "data/sqlite-test.db"
	}
];

exports.databaseName = "MAIN";
exports.tableName = "NODE_ODBC_TEST_TABLE";

exports.dropTables = function (db, cb) {
  db.query("drop table " + exports.tableName, cb);
};

exports.createTables = function (db, cb) {
  db.query("create table " + exports.tableName + " (COLINT INTEGER, COLDATETIME DATETIME, COLTEXT TEXT)", cb);
};
<commit_msg>add a couple library paths for testing<commit_after>var odbc = require("../");
//odbc.library = '/usr/lib/odbc/libsqlite3odbc-0.91';
//odbc.library = '/usr/lib/x86_64-linux-gnu/odbc/libtdsodbc';

exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db";
//exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;";
//exports.connectionString = process.env.ODBC_CONNETION_STRING;

exports.connectionObject = {
	DRIVER : "{SQLITE3}",
	DATABASE : "data/sqlite-test.db"
};

exports.connections = [
	{
		DRIVER : "{SQLITE3}",
		DATABASE : "data/sqlite-test.db"
	}
];

exports.databaseName = "MAIN";
exports.tableName = "NODE_ODBC_TEST_TABLE";

exports.dropTables = function (db, cb) {
  db.query("drop table " + exports.tableName, cb);
};

exports.createTables = function (db, cb) {
  db.query("create table " + exports.tableName + " (COLINT INTEGER, COLDATETIME DATETIME, COLTEXT TEXT)", cb);
};
<|endoftext|>


<commit_before>/* Copyright 2015 Christine S. MacNeill

  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 appli cable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

var test = require('tape');
var codecadon = require('../../codecadon');

function makeBufArray(bytesPerBuf, numBuffers) {
  var a = new Array(numBuffers);
  var c = 0;
  for (var b=0; b<numBuffers; ++b) {
    var buf = new Buffer(bytesPerBuf);
    for (var i=0; i<bytesPerBuf; ++i) {
      buf[i] = (c++)&0xff;
    }
    a[b] = buf;
  }
  return a;
}

function concatTest(description, format, width, height, fn) {
  test(description, function (t) {
    var concater = new codecadon.Concater(format, width, height);
    concater.on('exit', function() {
      concater.finish();
      t.end();
    });
    concater.on('error', function(err) {
      t.fail(err);
    });

    fn(t, concater, format, width, height, function() {
      concater.quit(function() {});
    });
  });
};

function createBad1(format, width, height) {
  new codecadon.Concater('4175', 21, 0);
};
function createBad2(format, width, height) {
  new codecadon.Concater('4176', 1920, 1080);
};

test('Handle bad image dimensions', function (t) {
  t.throws(createBad1);
  t.end();
});

test('Handle bad image format', function (t) {
  t.throws(createBad2);
  t.end();
});

concatTest('Startup a concatenator', '4175', 1920, 1080, function (t, concater, format, width, height, done) {
  var dstBufLen = concater.start();
  var numBytesExpected = width * height * 5/2;
  t.equal(dstBufLen, numBytesExpected, 'testing buffer size calculation');
  done();
});

concatTest('Perform concatenation', '4175', 1920, 1080, function (t, concater, format, width, height, done) {
  var numBytes = width * height * 5/2;
  var bufArray = makeBufArray(numBytes / width, width);
  var dstBufLen = concater.start();
  var dstBuf = new Buffer(dstBufLen);
  var numQueued = concater.concat(bufArray, dstBuf, function(err, result) {
    t.notOk(err, 'no error');
    var testDstBuf = makeBufArray(numBytes, 1)[0];
    t.deepEquals(result, testDstBuf, 'testing concatenation result')
  });
  t.equal(numQueued, 1, 'testing queueing');
  done();
});

concatTest('Handle undefined source', '4175', 1920, 1080, function (t, concater, format, width, height, done) {
  var bufArray;
  var dstBufLen = concater.start();
  var dstBuf = new Buffer(dstBufLen);
  concater.concat(bufArray, dstBuf, function(err, result) {
    t.ok(err, 'should return error');
  });
  done();
});

concatTest('Handle undefined destination', '4175', 1920, 1080, function (t, concater, format, width, height, done) {
  var numBytes = width * height * 5/2;
  var bufArray = makeBufArray(numBytes / width, width);
  concater.start();
  var dstBuf;
  concater.concat(bufArray, dstBuf, function(err, result) {
    t.ok(err, 'should return error');
  });
  done();
});

concatTest('Handle source bytes greater than destination bytes', '4175', 1920, 1080, function (t, concater, format, width, height, done) {
  var numBytes = width * height * 5/2;
  var bufArray = makeBufArray(numBytes / width, width + 10);
  var dstBufLen = concater.start();
  var dstBuf = new Buffer(dstBufLen);
  concater.concat(bufArray, dstBuf, function(err, result) {
    t.notOk(err, 'no error');
    var testDstBuf = makeBufArray(numBytes, 1)[0];
    t.deepEquals(result, testDstBuf, 'testing concatenation result')
  });
  done();
});

<commit_msg>Fix race condition in tests<commit_after>/* Copyright 2015 Christine S. MacNeill

  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 appli cable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

var test = require('tape');
var codecadon = require('../../codecadon');

function makeBufArray(bytesPerBuf, numBuffers) {
  var a = new Array(numBuffers);
  var c = 0;
  for (var b=0; b<numBuffers; ++b) {
    var buf = new Buffer(bytesPerBuf);
    for (var i=0; i<bytesPerBuf; ++i) {
      buf[i] = (c++)&0xff;
    }
    a[b] = buf;
  }
  return a;
}

function concatTest(description, format, width, height, fn) {
  test(description, function (t) {
    var concater = new codecadon.Concater(format, width, height);
    concater.on('exit', function() {
      concater.finish();
      t.end();
    });
    concater.on('error', function(err) {
      t.fail(err);
    });

    fn(t, concater, format, width, height, function() {
      concater.quit(function() {});
    });
  });
};

function createBad1(format, width, height) {
  new codecadon.Concater('4175', 21, 0);
};
function createBad2(format, width, height) {
  new codecadon.Concater('4176', 1920, 1080);
};

test('Handle bad image dimensions', function (t) {
  t.throws(createBad1);
  t.end();
});

test('Handle bad image format', function (t) {
  t.throws(createBad2);
  t.end();
});

concatTest('Startup a concatenator', '4175', 1920, 1080, function (t, concater, format, width, height, done) {
  var dstBufLen = concater.start();
  var numBytesExpected = width * height * 5/2;
  t.equal(dstBufLen, numBytesExpected, 'testing buffer size calculation');
  done();
});

concatTest('Perform concatenation', '4175', 1920, 1080, function (t, concater, format, width, height, done) {
  var numBytes = width * height * 5/2;
  var bufArray = makeBufArray(numBytes / width, width);
  var dstBufLen = concater.start();
  var dstBuf = new Buffer(dstBufLen);
  var numQueued = concater.concat(bufArray, dstBuf, function(err, result) {
    t.notOk(err, 'no error');
    var testDstBuf = makeBufArray(numBytes, 1)[0];
    t.deepEquals(result, testDstBuf, 'testing concatenation result')
    done();
  });
  t.equal(numQueued, 1, 'testing queueing');
});

concatTest('Handle undefined source', '4175', 1920, 1080, function (t, concater, format, width, height, done) {
  var bufArray;
  var dstBufLen = concater.start();
  var dstBuf = new Buffer(dstBufLen);
  concater.concat(bufArray, dstBuf, function(err, result) {
    t.ok(err, 'should return error');
    done();
  });
});

concatTest('Handle undefined destination', '4175', 1920, 1080, function (t, concater, format, width, height, done) {
  var numBytes = width * height * 5/2;
  var bufArray = makeBufArray(numBytes / width, width);
  concater.start();
  var dstBuf;
  concater.concat(bufArray, dstBuf, function(err, result) {
    t.ok(err, 'should return error');
    done();
  });
});

concatTest('Handle source bytes greater than destination bytes', '4175', 1920, 1080, function (t, concater, format, width, height, done) {
  var numBytes = width * height * 5/2;
  var bufArray = makeBufArray(numBytes / width, width + 10);
  var dstBufLen = concater.start();
  var dstBuf = new Buffer(dstBufLen);
  concater.concat(bufArray, dstBuf, function(err, result) {
    t.notOk(err, 'no error');
    var testDstBuf = makeBufArray(numBytes, 1)[0];
    t.deepEquals(result, testDstBuf, 'testing concatenation result')
    done();
  });
});

<|endoftext|>


<commit_before>import { assert } from "chai"
import { Config } from "../src/config.js"

describe("Config", () => {

	it('can create', () => {
		var config = new Config;
		assert.instanceOf(config, Config);
	});

});
<commit_msg>test: Config can set definition<commit_after>import { assert } from "chai"
import { Config } from "../src/config.js"

describe("Config", () => {

	it('can create', () => {
		var config = new Config;
		assert.instanceOf(config, Config);
	});

	it('can set definition', () => {
		var definition = {
			"experiment-1": {
				"variants": [
					"control",
					"variant-1"
				]
			}
		};

		var config = new Config;
		config.setDefintion(definition);
		assert.equal(config._definition, definition);
	});

});
<|endoftext|>


<commit_before>/**
 * @class  	UIFactory.ZoomImageView
 * @author  Flavio De Stefano <flavio.destefano@caffeinalab.com>
 *
 * An ImageView with the pinch-to-zoom feature included.
 */

module.exports = function(args) {
	_.defaults(args, {

		/**
		 * @property zoomScale
		 * @type {Number}
		 * The current zoom scale
		 */
		zoomScale: 1,

		/**
		 * @property minZoomScale
		 * @type {Number}
		 * The min zoom scale
		 */
		minZoomScale: 1,

		/**
		 * @property maxZoomScale
		 * @type {Number}
		 * The max zoom scale
		 */
		maxZoomScale: 2,

		/**
		 * @property image
		 * @type {String}
		 * The image to show
		 */
		image: null

	});

	if (OS_IOS) {
		return (function() {

			var $this = Ti.UI.createScrollView({
				zoomScale: args.zoomScale,
				minZoomScale: args.minZoomScale,
				maxZoomScale: args.maxZoomScale
			});

			// Double tab listener to zoom in
			$this.addEventListener('doubletap', function(e) {
				var m = ( $this.maxZoomScale - $this.minZoomScale ) / 2;
				var upper = ( $this.zoomScale > m );
				$this.setZoomScale(upper ? $this.minZoomScale : $this.maxZoomScale, { animated: true });
			});

			/**
			 * @method setImage
			 * @param {String} image
			 */
			$this.setImage = function(image) {
				$this.imageView.image = image;
			};

			$this.imageView = Ti.UI.createImageView({
				defaultImage: null,
				image: args.image,
				width: Alloy.Globals.SCREEN_WIDTH,
				height: Ti.UI.SIZE
			});
			$this.add($this.imageView);

			return $this;

		})();
	} else if (OS_ANDROID) {
		return (function() {

			return require('org.iotashan.TiTouchImageView').createView({
				minZoom: args.minZoomScale,
				image: args.image
			});

		})();
	}
};
<commit_msg>Fixed double tap<commit_after>/**
 * @class  	UIFactory.ZoomImageView
 * @author  Flavio De Stefano <flavio.destefano@caffeinalab.com>
 *
 * An ImageView with the pinch-to-zoom feature included.
 */

module.exports = function(args) {
	_.defaults(args, {

		/**
		 * @property zoomScale
		 * @type {Number}
		 * The current zoom scale
		 */
		zoomScale: 1,

		/**
		 * @property minZoomScale
		 * @type {Number}
		 * The min zoom scale
		 */
		minZoomScale: 1,

		/**
		 * @property maxZoomScale
		 * @type {Number}
		 * The max zoom scale
		 */
		maxZoomScale: 2,

		/**
		 * @property image
		 * @type {String}
		 * The image to show
		 */
		image: null

	});

	if (OS_IOS) {
		return (function() {

			var $this = Ti.UI.createScrollView({
				zoomScale: args.zoomScale,
				minZoomScale: args.minZoomScale,
				maxZoomScale: args.maxZoomScale
			});

			// Double tab listener to zoom in
			$this.addEventListener('doubletap', function(e) {
				var m = ( $this.maxZoomScale + $this.minZoomScale ) / 2;
				var upper = ( $this.zoomScale > m );
				$this.setZoomScale(upper ? $this.minZoomScale : $this.maxZoomScale, { animated: true });
			});

			/**
			 * @method setImage
			 * @param {String} image
			 */
			$this.setImage = function(image) {
				$this.imageView.image = image;
			};

			$this.imageView = Ti.UI.createImageView({
				defaultImage: null,
				image: args.image,
				width: Alloy.Globals.SCREEN_WIDTH,
				height: Ti.UI.SIZE
			});
			$this.add($this.imageView);

			return $this;

		})();
	} else if (OS_ANDROID) {
		return (function() {

			return require('org.iotashan.TiTouchImageView').createView({
				minZoom: args.minZoomScale,
				image: args.image
			});

		})();
	}
};
<|endoftext|>


<commit_before>var config = require('../lib/config');

exports['invalid if nothing'] = function(test) {
    test.equals(config.valid({}), false);

    test.done();
}

exports['valid if all supplied'] = function(test) {
    test.equals(config.valid({
        DB_PATH: 'x'
    }), true);

    test.done();
}
<commit_msg>Fix tests<commit_after>var config = require('../lib/config');

exports['invalid if nothing'] = function(test) {
    test.equals(config.valid({}), false);

    test.done();
};

exports['valid if all supplied'] = function(test) {
    test.equals(config.valid({
        DB_PATH: 'x',
        COOKIE_SECRET: 'x',
        SESSION_SECRET: 'x'
    }), true);

    test.done();
};
<|endoftext|>