<commit_before>(function ($, $$) {

    "use strict";

    var action = "share", shipment;

    var shipments = {
            123:
            {id : 123, status : "Waiting",  description : "Release Planning at HQ",  date: "09/20/2013",
                flights : [
                    {airline : "United", flight : "UA702",
                        departs : {city : "Denver, CO", airport : "DEN", day : "09/20/2013", time : "08:15 AM"},
                        arrives : {city : "San Francisco, CA", airport : "SFO", day : "09/20/2013", time : "09:30 AM"}},
                    {airline : "United", flight : "UA123",
                        departs : {city : "San Francisco, CA",airport : "SFO", day : "09/23/2013", time : "07:53 PM"},
                        arrives : {city : "Denver, CO", airport : "DEN", day : "09/23/2013", time : "10:30 PM"}}
                ],
                hotel : {name : "Marriott Marquis", address : {street : "55 Fourth Street", city : "San Francisco", state : "CA", zip : "94103"},
                    checkin : "09/01/2013", days : "2", rooms : "1", rate : "$230 USD"},
                total : "$2300.00"
            },
            124:
            {id : 124, status : "Approved", description : "Sales Kickoff - Vegas",  date: "09/10/2013",
                flights : [
                    {airline : "Southwest", flight : "UA702",
                        departs : {city : "Denver, CO", airport : "DEN", day : "09/10/2013", time : "08:15 AM"},
                        arrives : {city : "Las Vegas, NV", airport : "SFO", day : "09/10/2013", time : "09:30 AM"}},
                    {airline : "Southwest", flight : "UA123",
                        departs : {city : "Las Vegas, NV", airport : "SFO", day : "09/14/2013", time : "07:53 PM"},
                        arrives : {city : "Denver, CO", airport : "DEN", day : "09/14/2013", time : "10:30 PM"}}
                ],
                hotel : {name : "Cosmopolitan", address : {street : "Strip", city : "Las Vegas", state : "NV", zip : "84103"},
                    checkin : "09/01/2013", days : "1", rooms : "1", rate : "$530 USD"},
                total : "$1500.00"
            },
            125:
            {id : 125, status : "Complete", description : "Trip to HQ Sprint Planning",  date: "06/18/2013",
                flights : [
                    {airline : "Delta", flight : "UA702",
                        departs : {city : "Denver, CO", airport : "DEN", day : "06/18/2013", time : "08:15 AM"},
                        arrives : {city : "San Francisco, CA", airport : "SFO", day : "06/18/2013", time : "09:30 AM"}},
                    {airline : "Delta", flight : "UA123",
                        departs : {city : "San Francisco, CA", airport : "SFO", day : "06/22/2013", time : "07:53 PM"},
                        arrives : {city : "Denver, CO", airport : "DEN", day : "06/22/2013", time : "10:30 PM"}}
                ],
                hotel : {name : "Marriott Marquis", address : {street : "55 Fourth Street", city : "San Francisco", state : "CA", zip : "94103"},
                    checkin : "09/01/2013", days : "2", rooms : "1", rate : "$420 USD"},
                total : "$1300.00"
            },
            126:
            {id : 126, status : "Complete", description : "Hawaii Vacation",  date: "04/02/2013",
                flights : [
                    {airline : "United", flight : "UA702",
                        departs : {city : "Denver, CO", airport : "DEN", day : "04/02/2013", time : "08:15 AM"},
                        arrives : {city : "Honolulu, HI", airport : "SFO", day : "04/02/2013", time : "09:30 AM"}},
                    {airline : "United", flight : "UA123",
                        departs : {city : "Honolulu, HI", airport : "SFO", day : "04/12/2013", time : "07:53 PM"},
                        arrives : {city : "Denver, CO", airport : "DEN", day : "04/12/2013", time : "10:30 PM"}}
                ],
                hotel : {name : "Waikiki Beach Marriott", address : {street : "2552 Kalakaua Avenue", city : "Oahu", state : "HI", zip : "96815"},
                    checkin : "09/01/2013", days : "3", rooms : "1", rate : "$660 USD"},
                total : "$3300.00"
            }
        };

    var module = {

        instance : function (sr) {
            var payload;

            function refresh(id) {
                var v, $tbody;
                if (!$$.isNil(id)) {
                    $tbody = $("#reservation tbody");
                    v = shipments[id];
                    $tbody.append(
                        "
" + v.status  +
                            "	" + v.description + "	" + v.total + "	" + v.date + "
");
                }
                else {
                    $tbody = $("#reservations tbody");
                    $.each(shipments, function(i, v) {
                        $tbody.append(
                            "
" + v.status  +
                                "	" + v.description + "	" + v.date + "
");
                    });
                }
            }

            function prettyPrint(id) {

                var it = shipments[id];

                var text = "My Shipment For: " + it.description + "\n";
                	text += "Status: " + it.status + "\n";
                	text += "Date: " + it.date + "\n\n";
                    for (var i = 0; i < it.flights.length; i++) {
                        var f = it.flights[i];
                        text += "Flight: " + f.airline + " " + f.flight + "- " + f.departs.city + " (" + f.departs.airport + ") to " +
                                f.arrives.city + " (" + f.arrives.airport +")\n";
                        text += "Departs: " + f.departs.city + ": " + f.departs.day + " " + f.departs.time + "\n";
                        text += "Arrives: " + f.arrives.city + ": " + f.departs.day + " " + f.arrives.time + "\n\n";
                    }
                    text += "Hotel: " + it.hotel.name + ", " + it.hotel.address.street + ", " + it.hotel.address.city + ", " + it.hotel.address.state + "\n";
                    text += "Room " + it.hotel.rooms + ", Days " + it.hotel.days + ", Guests 1\n";
                    text += "Daily Rate: " + it.hotel.rate + "\n";
                    text += "Check In: " + it.hotel.checkin;

                return text;
            }

            function approve(id) {

                $(function() {
                    $("button" )
                        .button()
                        .click(function( event ) {
                            event.preventDefault();
                            action = $(this).attr("id");
                            $("#status").empty().append(("approve" === action) ? "Approved" : "Denied");
                        });
                });

                console.log("Approve...." + id);
                refresh(id);
            }

            function draw() {

                $("#reservations").chromatable({
                    width: "100%",
                    height: "100%",
                    scrolling: "yes"
                });

                $("#reservations").selectable({
                    filter:'tr',
                    selected: function(event, ui){
                        var s = $(this).find('.ui-selected');
                        shipment = s[0].id;
                        console.log("Shipment: " + shipment);

                        // @review - generate named methods instead (fireEnable())
                        console.log("Fire event from client");
                        $$.client.publish(sr.client, {name : 'publisher.setValidForSubmit', payload : true});
                    }
                });

                $( "#radio" ).buttonset();
                $("#radio :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    action = $(this).attr("id");
                    console.log("Action: " + action);
                });

                $( "#switch" ).buttonset();
                $("#switch :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    var tempAction = $(this).attr("id");
                  	action = "share";
                    $('#reservations .ui-selected').removeClass('ui-selected')
                    console.log("Action: " + action);
                });

                $( "#radio2" ).buttonset();
				$("#radio2 :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    action = $(this).attr("id");
                    console.log("Action: " + action);
                });

                refresh();
            }

            var handlers = {
                onSetupPanel : function(payload) {
                	console.log("EH Module setupPanel..", payload);
                },
                onShowPanel : function(payload) {
                    console.log("EH Module showPanel", payload);
                },
                onClearPanelState : function(payload) {
                    console.log("EH Module clearPanelState");
                    // Clear the selected reservation
                    $('#reservations .ui-selected').removeClass('ui-selected')

                    // Clear the selected radio buttons
                    $('input[name="radio"]').prop('checked', false);

                    // Re enable the default selection
                    action = "share";
                    $('#share').prop('checked', true);
                    $("#radio :radio").button( "refresh");
                    $('#itin').prop('checked', true);
                },
                onSuccess : function() {
                    console.log("EH Module onSuccess");
                },
                onFailure : function () {
                    console.log("EH Module onFailure");
                },
                onGetPayload : function () {
                    var p = {};
                    console.log("EH Module getPayload");
                    if ("share" === action) {
                        p.feedItemType = "TextPost";
                        p.auxText = prettyPrint(shipment);
                    }
                    else if ("link" === action) {
                        p.feedItemType = "LinkPost";
                        p.auxText = "Please Approve my trip: " + shipments[shipment].description;
                        p.url = "https://canvas-publisher-feed.herokuapp.com/signed-request.jsp?intinerary=" + shipment;
                        p.urlName = shipments[shipment].description;
                    }
                    else if ("approval" === action) {
                         p.feedItemType = "CanvasPost";
                         p.auxText = "Please Approve my trip: " + shipments[shipment].description;
                         p.namespace =  sr.context.application.namespace;
                         p.developerName =  sr.context.application.developerName;
                         p.thumbnailUrl = "https://icons.iconarchive.com/icons/aha-soft/perfect-transport/48/Airplane-icon.png";
                         p.parameters =  "{\"shipment\":\"" + shipment + "\"}";
                         p.title =  "Shipment - " +shipments[shipment].description;
                         p.description = "This is a travel shipment for Shipment - " + shipments[shipment].description + ".  Click the link to open the Canvas App.";
                    }

                    // Note: we can extend the payload here to include more then just value.
                    $$.client.publish(sr.client, {name : 'publisher.setPayload', payload : p});
                }
            };

            var that = {

                draw : draw,
                refresh : refresh,
                approve : approve,

                // Subscriptions to callbacks from publisher...
                subscriptions : [
                    {name : 'publisher.setupPanel', onData : handlers.onSetupPanel},
                    {name : 'publisher.showPanel', onData : handlers.onShowPanel},
                    {name : 'publisher.clearPanelState',  onData : handlers.onClearPanelState},
                    {name : 'publisher.failure', onData : handlers.onFailure},
                    {name : 'publisher.success', onData : handlers.onSuccess},
                    {name : 'publisher.getPayload', onData : handlers.onGetPayload}
                ]
            };
            return that;
        }
    };

    // Replace with module pattern
    window.shipment = module;

}(jQuery, Sfdc.canvas));<commit_msg>second commit<commit_after>(function ($, $$) {

    "use strict";

    var action = "share", shipment;

    var shipments = {
            123:
            {id : 123, status : "Waiting",  description : "Cellular Accessories and Phones",  date: "12/20/2013",
                trucks : [
                    {truck : "Box Truck 304", driverNumber : "D4702",
                        departs : {city : "Denver, CO", day : "12/18/2013", time : "08:15 AM"},
                        arrives : {city : "San Francisco, CA", day : "09/20/2013", time : "09:30 AM"}},
                    {truck : "Box Truck 512", driverNumber : "D0123",
                        departs : {city : "Los Angeles, CA", day : "12/19/2013", time : "08:00 AM"},
                        arrives : {city : "San Francisco, CA", day : "12/19/2013", time : "5:00 PM"}}
                ],
                destination : {name : "The Tech Store", address : {street : "55 Fourth Street", city : "San Francisco", state : "CA", zip : "94103"},
                total : "$23500.00"
            },
            124:
            {id : 124, status : "Approved", description : "Laptop Supplies",  date: "09/10/2013",
                trucks : [
                    {truck : "Bus 005", driverNumber : "D2702",
                        departs : {city : "Austin, TX", day : "09/5/2013", time : "08:15 AM"},
                        arrives : {city : "San Francisco, CA", day : "09/10/2013", time : "09:30 AM"}},
                    {truck : "Lowboy 201", driverNumber : "UA123",
                        departs : {city : "Las Vegas, NV", day : "09/8/2013", time : "08:00 AM"},
                        arrives : {city : "San Francisco, CA", day : "09/10/2013", time : "5:00 PM"}}
                ],
                destination : {name : "DigiMedia", address : {street : "150 Van Ness St", city : "San Francisco", state : "CA", zip : "94134"},
                total : "$17750.00"
            },
            125:
            {id : 125, status : "Complete", description : "Tablets",  date: "06/18/2013",
                trucks : [
                    {truck : "Box Truck 117", driverNumber : "D6702",
                        departs : {city : "Salt Lake City, UT", day : "06/14/2013", time : "08:00 AM"},
                        arrives : {city : "San Francisco, CA", day : "06/18/2013", time : "09:30 AM"}}
                ],
                destination : {name : "Munder Difflin Paperless Company", address : {street : "160 Brannan St", city : "San Francisco", state : "CA", zip : "94103"},
                total : "$13000.00"
            },
            126:
            {id : 126, status : "Complete", description : "TVs, Projectors, and Desktops, oh my!",  date: "04/02/2013",
                trucks : [
                    {truck : "Bus 006", driverNumber : "D2202",
                        departs : {city : "Portland, OR", day : "04/02/2013", time : "08:15 AM"},
                        arrives : {city : "San Francisco, CA", day : "04/05/2013", time : "09:30 AM"}},
                    {truck : "Box Truck 215", driverNumber : "UA123",
                        departs : {city : "San Diego, CA", day : "04/10/2013", time : "03:00 PM"},
                        arrives : {city : "San Francisco, CA", day : "04/12/2013", time : "5:30 PM"}}
                ],
                destination : {name : "Salesforce", address : {street : "1 Market St", city : "San Francisco", state : "CA", zip : "94105"},
                total : "$3300.00"
            }
        };

    var module = {

        instance : function (sr) {
            var payload;

            function refresh(id) {
                var v, $tbody;
                if (!$$.isNil(id)) {
                    $tbody = $("#reservation tbody");
                    v = shipments[id];
                    $tbody.append(
                        "
" + v.status  +
                            "	" + v.description + "	" + v.total + "	" + v.date + "
");
                }
                else {
                    $tbody = $("#reservations tbody");
                    $.each(shipments, function(i, v) {
                        $tbody.append(
                            "
" + v.status  +
                                "	" + v.description + "	" + v.date + "
");
                    });
                }
            }

            function prettyPrint(id) {

                var it = shipments[id];

                var text = "My Shipment For: " + it.description + "\n";
                	text += "Status: " + it.status + "\n";
                	text += "Date: " + it.date + "\n\n";
                    for (var i = 0; i < it.trucks.length; i++) {
                        var f = it.trucks[i];
                        text += "Truck: " + f.truck + " " + f.driverNumber + "- " + f.departs.city +"\n";
                        text += "Departs: " + f.departs.city + ": " + f.departs.day + " " + f.departs.time + "\n";
                        text += "Arrives: " + f.arrives.city + ": " + f.departs.day + " " + f.arrives.time + "\n\n";
                    }
                    text += "Hotel: " + it.destination.name + ", " + it.destination.address.street + ", " + it.destination.address.city + ", " + it.destination.address.state + "\n";
                    text += "Room " + it.destination.rooms + ", Days " + it.destination.days + ", Guests 1\n";
                    text += "Daily Rate: " + it.destination.rate + "\n";
                    text += "Check In: " + it.destination.checkin;

                return text;
            }

            function approve(id) {

                $(function() {
                    $("button" )
                        .button()
                        .click(function( event ) {
                            event.preventDefault();
                            action = $(this).attr("id");
                            $("#status").empty().append(("approve" === action) ? "Approved" : "Denied");
                        });
                });

                console.log("Approve...." + id);
                refresh(id);
            }

            function draw() {

                $("#reservations").chromatable({
                    width: "100%",
                    height: "100%",
                    scrolling: "yes"
                });

                $("#reservations").selectable({
                    filter:'tr',
                    selected: function(event, ui){
                        var s = $(this).find('.ui-selected');
                        shipment = s[0].id;
                        console.log("Shipment: " + shipment);

                        // @review - generate named methods instead (fireEnable())
                        console.log("Fire event from client");
                        $$.client.publish(sr.client, {name : 'publisher.setValidForSubmit', payload : true});
                    }
                });

                $( "#radio" ).buttonset();
                $("#radio :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    action = $(this).attr("id");
                    console.log("Action: " + action);
                });

                $( "#switch" ).buttonset();
                $("#switch :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    var tempAction = $(this).attr("id");
                  	action = "share";
                    $('#reservations .ui-selected').removeClass('ui-selected')
                    console.log("Action: " + action);
                });

                $( "#radio2" ).buttonset();
				$("#radio2 :radio").click(function(e) {
                    var rawElement = this;
                    var $element = $(this);
                    action = $(this).attr("id");
                    console.log("Action: " + action);
                });

                refresh();
            }

            var handlers = {
                onSetupPanel : function(payload) {
                	console.log("EH Module setupPanel..", payload);
                },
                onShowPanel : function(payload) {
                    console.log("EH Module showPanel", payload);
                },
                onClearPanelState : function(payload) {
                    console.log("EH Module clearPanelState");
                    // Clear the selected reservation
                    $('#reservations .ui-selected').removeClass('ui-selected')

                    // Clear the selected radio buttons
                    $('input[name="radio"]').prop('checked', false);

                    // Re enable the default selection
                    action = "share";
                    $('#share').prop('checked', true);
                    $("#radio :radio").button( "refresh");
                    $('#itin').prop('checked', true);
                },
                onSuccess : function() {
                    console.log("EH Module onSuccess");
                },
                onFailure : function () {
                    console.log("EH Module onFailure");
                },
                onGetPayload : function () {
                    var p = {};
                    console.log("EH Module getPayload");
                    if ("share" === action) {
                        p.feedItemType = "TextPost";
                        p.auxText = prettyPrint(shipment);
                    }
                    else if ("link" === action) {
                        p.feedItemType = "LinkPost";
                        p.auxText = "Please Approve my trip: " + shipments[shipment].description;
                        p.url = "https://canvas-publisher-feed.herokuapp.com/signed-request.jsp?intinerary=" + shipment;
                        p.urlName = shipments[shipment].description;
                    }
                    else if ("approval" === action) {
                         p.feedItemType = "CanvasPost";
                         p.auxText = "Please Approve my trip: " + shipments[shipment].description;
                         p.namespace =  sr.context.application.namespace;
                         p.developerName =  sr.context.application.developerName;
                         p.thumbnailUrl = "https://icons.iconarchive.com/icons/aha-soft/perfect-transport/48/Airplane-icon.png";
                         p.parameters =  "{\"shipment\":\"" + shipment + "\"}";
                         p.title =  "Shipment - " +shipments[shipment].description;
                         p.description = "This is a travel shipment for Shipment - " + shipments[shipment].description + ".  Click the link to open the Canvas App.";
                    }

                    // Note: we can extend the payload here to include more then just value.
                    $$.client.publish(sr.client, {name : 'publisher.setPayload', payload : p});
                }
            };

            var that = {

                draw : draw,
                refresh : refresh,
                approve : approve,

                // Subscriptions to callbacks from publisher...
                subscriptions : [
                    {name : 'publisher.setupPanel', onData : handlers.onSetupPanel},
                    {name : 'publisher.showPanel', onData : handlers.onShowPanel},
                    {name : 'publisher.clearPanelState',  onData : handlers.onClearPanelState},
                    {name : 'publisher.failure', onData : handlers.onFailure},
                    {name : 'publisher.success', onData : handlers.onSuccess},
                    {name : 'publisher.getPayload', onData : handlers.onGetPayload}
                ]
            };
            return that;
        }
    };

    // Replace with module pattern
    window.shipment = module;

}(jQuery, Sfdc.canvas));<|endoftext|>


<commit_before>/**
 * Module : neoui-tooltip
 * Author : Kvkens(yueming@yonyou.com)
 * Date   : 2016-08-06 13:26:06
 */
import {extend} from 'tinper-sparrow/js/extend';
import {on} from 'tinper-sparrow/js/event';
import {makeDOM,addClass,removeClass,getZIndex,showPanelByEle} from 'tinper-sparrow/js/dom';

var Tooltip = function(element, options) {
	this.init(element, options)
		//this.show()
}

Tooltip.prototype = {
	defaults: {
		animation: true,
		placement: 'top',
		//selector: false,
		template: '

',
		trigger: 'hover focus',
		title: '',
		delay: 0,
		html: false,
		container: false,
		viewport: {
			selector: 'body',
			padding: 0
		},
		showFix: false
	},
	init: function(element, options) {
		this.element = element
		this.options = extend({}, this.defaults, options);
		this._viewport = this.options.viewport && document.querySelector(this.options.viewport.selector || this.options.viewport);

		var triggers = this.options.trigger.split(' ')

		for(var i = triggers.length; i--;) {
			var trigger = triggers[i]
			if(trigger == 'click') {
				on(this.element, 'click', this.toggle.bind(this));
			} else if(trigger != 'manual') {
				var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
				var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
				on(this.element, eventIn, this.enter.bind(this));
				on(this.element, eventOut, this.leave.bind(this));
			}
		}
		this.options.title = this.options.title || this.element.getAttribute('title');
		this.element.removeAttribute('title');
		if(this.options.delay && typeof this.options.delay == 'number') {
			this.options.delay = {
				show: this.options.delay,
				hide: this.options.delay
			}
		};
		//tip模板对应的dom
		this.tipDom = makeDOM(this.options.template);
		addClass(this.tipDom, this.options.placement);
		if(this.options.colorLevel) {
			addClass(this.tipDom, this.options.colorLevel);
		}
		this.arrrow = this.tipDom.querySelector('.tooltip-arrow');

		// tip容器,默认为当前元素的parent
		this.container = this.options.container ? document.querySelector(this.options.container) : this.element.parentNode;
	},
	enter: function() {
		var self = this;
		clearTimeout(this.timeout);
		this.hoverState = 'in';
		if(!this.options.delay || !this.options.delay.show) return this.show();

		this.timeout = setTimeout(function() {
			if(self.hoverState == 'in') self.show()
		}, this.options.delay.show)
	},
	leave: function() {
		var self = this;
		clearTimeout(this.timeout);
		self.hoverState = 'out'
		if(!self.options.delay || !self.options.delay.hide) return self.hide()
		self.timeout = setTimeout(function() {
			if(self.hoverState == 'out') self.hide()
		}, self.options.delay.hide)
	},
	show: function() {
		var self = this;
		this.tipDom.querySelector('.tooltip-inner').innerHTML = this.options.title;
		this.tipDom.style.zIndex = getZIndex();

		if(this.options.showFix) {
			document.body.appendChild(this.tipDom);
			this.tipDom.style.position = 'fixed';
			showPanelByEle({
				ele: this.element,
				panel: this.tipDom,
				position: "top"
			});
			// fix情况下滚动时隐藏
			on(document, 'scroll', function() {
				self.hide();
			})
		} else {
			this.container.appendChild(this.tipDom);
			var inputLeft = this.element.offsetLeft;
			var inputTop = this.element.offsetTop;
			var inputWidth = this.element.offsetWidth;
			var inputHeight = this.element.offsetHeight;
			var topWidth = this.tipDom.offsetWidth;
			var topHeight = this.tipDom.offsetHeight;
			if(this.options.placement == 'top') {
				this.left = this.element.offsetLeft + inputWidth / 2;
				this.top = this.element.offsetTop - topHeight;
			}
			// 水平居中
			this.tipDom.style.left = this.left - this.tipDom.clientWidth / 2 + 'px';
			// this.tipDom.style.left = this.left + 'px';
			this.tipDom.style.top = this.top + 'px';
		}

		addClass(this.tipDom, 'active');

		// var placement = this.options.placement;
		// var pos = this.getPosition()
		// var actualWidth = this.tipDom.offsetWidth
		// var actualHeight = this.tipDom.offsetHeight
		// var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)

		// this.applyPlacement(calculatedOffset, placement)

	},
	hide: function() {
		if(this.options.showFix) {
			if(document.body.contains(this.tipDom)) {
				removeClass(this.tipDom, 'active');
				document.body.removeChild(this.tipDom);
			}
		} else {
			if(this.container.contains(this.tipDom)) {
				removeClass(this.tipDom, 'active');
				this.container.removeChild(this.tipDom);
			}
		}

	},
	applyPlacement: function(offset, placement) {
		var width = this.tipDom.offsetWidth
		var height = this.tipDom.offsetHeight

		// manually read margins because getBoundingClientRect includes difference
		var marginTop = parseInt(this.tipDom.style.marginTop, 10)
		var marginLeft = parseInt(this.tipDom.style.marginTop, 10)

		// we must check for NaN for ie 8/9
		if(isNaN(marginTop)) marginTop = 0
		if(isNaN(marginLeft)) marginLeft = 0

		offset.top = offset.top + marginTop
		offset.left = offset.left + marginLeft

		// $.fn.offset doesn't round pixel values
		// so we use setOffset directly with our own function B-0
		this.tipDom.style.left = offset.left + 'px';
		this.tipDom.style.top = offset.top + 'px';

		addClass(this.tipDom, 'active');

		// check to see if placing tip in new offset caused the tip to resize itself
		var actualWidth = this.tipDom.offsetWidth
		var actualHeight = this.tipDom.offsetHeight

		if(placement == 'top' && actualHeight != height) {
			offset.top = offset.top + height - actualHeight
		}
		var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)

		if(delta.left) offset.left += delta.left
		else offset.top += delta.top

		var isVertical = /top|bottom/.test(placement)
		var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
		var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'

		//$tip.offset(offset)
		this.tipDom.style.left = offset.left + 'px';
		this.tipDom.style.top = offset.top - 4 + 'px';

		// this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)

	},
	getCalculatedOffset: function(placement, pos, actualWidth, actualHeight) {
		return placement == 'bottom' ? {
				top: pos.top + pos.height,
				left: pos.left + pos.width / 2 - actualWidth / 2
			} :
			placement == 'top' ? {
				top: pos.top - actualHeight,
				left: pos.left + pos.width / 2 - actualWidth / 2
			} :
			placement == 'left' ? {
				top: pos.top + pos.height / 2 - actualHeight / 2,
				left: pos.left - actualWidth
			} :
			/* placement == 'right' */
			{
				top: pos.top + pos.height / 2 - actualHeight / 2,
				left: pos.left + pos.width
			}
	},
	getPosition: function(el) {
		el = el || this.element;
		var isBody = el.tagName == 'BODY';
		var elRect = el.getBoundingClientRect()
		if(elRect.width == null) {
			// width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
			elRect = extend({}, elRect, {
				width: elRect.right - elRect.left,
				height: elRect.bottom - elRect.top
			})
		}
		var elOffset = isBody ? {
			top: 0,
			left: 0
		} : {
			top: el.offsetTop,
			left: el.offsetLeft
		};
		var scroll = {
			scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : el.scrollTop
		}
		var outerDims = isBody ? {
				width: window.innerWidth || document.body.clientWidth,
				height: window.innerHeight || document.body.clientHeight
			} : null
			//return extend({}, elRect, scroll, outerDims, elOffset)
		return extend({}, elRect, scroll, outerDims)

	},
	getViewportAdjustedDelta: function(placement, pos, actualWidth, actualHeight) {
		var delta = {
			top: 0,
			left: 0
		}
		if(!this._viewport) return delta

		var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
		var viewportDimensions = this.getPosition(this._viewport)

		if(/right|left/.test(placement)) {
			var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
			var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
			if(topEdgeOffset < viewportDimensions.top) { // top overflow
				delta.top = viewportDimensions.top - topEdgeOffset
			} else if(bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
				delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
			}
		} else {
			var leftEdgeOffset = pos.left - viewportPadding
			var rightEdgeOffset = pos.left + viewportPadding + actualWidth
			if(leftEdgeOffset < viewportDimensions.left) { // left overflow
				delta.left = viewportDimensions.left - leftEdgeOffset
			} else if(rightEdgeOffset > viewportDimensions.width) { // right overflow
				delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
			}
		}

		return delta
	},
	replaceArrow: function(delta, dimension, isHorizontal) {
		if(isHorizontal) {
			this.arrow.style.left = 50 * (1 - delta / dimension) + '%';
			this.arrow.style.top = '';
		} else {
			this.arrow.style.top = 50 * (1 - delta / dimension) + '%';
			this.arrow.style.left = '';
		}
	},
	destory: function() {

	},
	setTitle: function(title) {
		this.options.title = title;
	}

};

export{
    Tooltip
}
<commit_msg>fix: tooltip不同位置显示效果<commit_after>/**
 * Module : neoui-tooltip
 * Author : Kvkens(yueming@yonyou.com)
 * Date   : 2016-08-06 13:26:06
 */
import {extend} from 'tinper-sparrow/js/extend';
import {on} from 'tinper-sparrow/js/event';
import {makeDOM,addClass,removeClass,getZIndex,showPanelByEle} from 'tinper-sparrow/js/dom';

var Tooltip = function(element, options) {
	this.init(element, options)
		//this.show()
}

Tooltip.prototype = {
	defaults: {
		animation: true,
		placement: 'top',
		//selector: false,
		template: '

',
		trigger: 'hover focus',
		title: '',
		delay: 0,
		html: false,
		container: false,
		viewport: {
			selector: 'body',
			padding: 0
		},
		showFix: false
	},
	init: function(element, options) {
		this.element = element
		this.options = extend({}, this.defaults, options);
		this._viewport = this.options.viewport && document.querySelector(this.options.viewport.selector || this.options.viewport);

		var triggers = this.options.trigger.split(' ')

		for(var i = triggers.length; i--;) {
			var trigger = triggers[i]
			if(trigger == 'click') {
				on(this.element, 'click', this.toggle.bind(this));
			} else if(trigger != 'manual') {
				var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
				var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
				on(this.element, eventIn, this.enter.bind(this));
				on(this.element, eventOut, this.leave.bind(this));
			}
		}
		this.options.title = this.options.title || this.element.getAttribute('title');
		this.element.removeAttribute('title');
		if(this.options.delay && typeof this.options.delay == 'number') {
			this.options.delay = {
				show: this.options.delay,
				hide: this.options.delay
			}
		};
		//tip模板对应的dom
		this.tipDom = makeDOM(this.options.template);
		addClass(this.tipDom, this.options.placement);
		if(this.options.colorLevel) {
			addClass(this.tipDom, this.options.colorLevel);
		}
		this.arrrow = this.tipDom.querySelector('.tooltip-arrow');

		// tip容器,默认为当前元素的parent
		this.container = this.options.container ? document.querySelector(this.options.container) : this.element.parentNode;
	},
	enter: function() {
		var self = this;
		clearTimeout(this.timeout);
		this.hoverState = 'in';
		if(!this.options.delay || !this.options.delay.show) return this.show();

		this.timeout = setTimeout(function() {
			if(self.hoverState == 'in') self.show()
		}, this.options.delay.show)
	},
	leave: function() {
		var self = this;
		clearTimeout(this.timeout);
		self.hoverState = 'out'
		if(!self.options.delay || !self.options.delay.hide) return self.hide()
		self.timeout = setTimeout(function() {
			if(self.hoverState == 'out') self.hide()
		}, self.options.delay.hide)
	},
	show: function() {
		var self = this;
		this.tipDom.querySelector('.tooltip-inner').innerHTML = this.options.title;
		this.tipDom.style.zIndex = getZIndex();

		if(this.options.showFix) {
			document.body.appendChild(this.tipDom);
			this.tipDom.style.position = 'fixed';
			showPanelByEle({
				ele: this.element,
				panel: this.tipDom,
				position: "top"
			});
			// fix情况下滚动时隐藏
			on(document, 'scroll', function() {
				self.hide();
			})
		} else {
			this.container.appendChild(this.tipDom);
			var inputLeft = this.element.offsetLeft;
			var inputTop = this.element.offsetTop;
			var inputWidth = this.element.offsetWidth;
			var inputHeight = this.element.offsetHeight;
			var topWidth = this.tipDom.offsetWidth;
			var topHeight = this.tipDom.offsetHeight;
			var tipDomleft,tipDomTop;

			if(this.options.placement == 'top') {
				// 上部提示

				this.left = this.element.offsetLeft + inputWidth / 2;
				this.top = this.element.offsetTop - topHeight;
				// 水平居中
				tipDomleft = this.left - this.tipDom.clientWidth / 2 + 'px';
				tipDomTop = this.top + 'px';
			}else if (this.options.placement == 'bottom') {
				// 下边提示
				this.left = this.element.offsetLeft + inputWidth / 2;
				this.top = this.element.offsetTop + topHeight;
				// 水平居中
				tipDomleft = this.left - this.tipDom.clientWidth / 2 + 'px';
				tipDomTop = this.top + 'px';
			}else if (this.options.placement == 'left') {
				// 左边提示
				this.left = this.element.offsetLeft ;
				this.top = this.element.offsetTop + topHeight/2;
				tipDomleft = this.left - this.tipDom.clientWidth + 'px';

				tipDomTop = this.top - this.tipDom.clientHeight/2 +'px';
			}else {
				// 右边提示

				this.left = this.element.offsetLeft + inputWidth;
				this.top = this.element.offsetTop + topHeight/2;
				tipDomleft = this.left + 'px';
				tipDomTop = this.top - this.tipDom.clientHeight/2 +'px';
			}

			this.tipDom.style.left = tipDomleft;
			this.tipDom.style.top = tipDomTop;
		}

		addClass(this.tipDom, 'active');

		// var placement = this.options.placement;
		// var pos = this.getPosition()
		// var actualWidth = this.tipDom.offsetWidth
		// var actualHeight = this.tipDom.offsetHeight
		// var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)

		// this.applyPlacement(calculatedOffset, placement)

	},
	hide: function() {
		if(this.options.showFix) {
			if(document.body.contains(this.tipDom)) {
				removeClass(this.tipDom, 'active');
				document.body.removeChild(this.tipDom);
			}
		} else {
			if(this.container.contains(this.tipDom)) {
				removeClass(this.tipDom, 'active');
				this.container.removeChild(this.tipDom);
			}
		}

	},
	applyPlacement: function(offset, placement) {
		var width = this.tipDom.offsetWidth
		var height = this.tipDom.offsetHeight

		// manually read margins because getBoundingClientRect includes difference
		var marginTop = parseInt(this.tipDom.style.marginTop, 10)
		var marginLeft = parseInt(this.tipDom.style.marginTop, 10)

		// we must check for NaN for ie 8/9
		if(isNaN(marginTop)) marginTop = 0
		if(isNaN(marginLeft)) marginLeft = 0

		offset.top = offset.top + marginTop
		offset.left = offset.left + marginLeft

		// $.fn.offset doesn't round pixel values
		// so we use setOffset directly with our own function B-0
		this.tipDom.style.left = offset.left + 'px';
		this.tipDom.style.top = offset.top + 'px';

		addClass(this.tipDom, 'active');

		// check to see if placing tip in new offset caused the tip to resize itself
		var actualWidth = this.tipDom.offsetWidth
		var actualHeight = this.tipDom.offsetHeight

		if(placement == 'top' && actualHeight != height) {
			offset.top = offset.top + height - actualHeight
		}
		var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)

		if(delta.left) offset.left += delta.left
		else offset.top += delta.top

		var isVertical = /top|bottom/.test(placement)
		var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
		var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'

		//$tip.offset(offset)
		this.tipDom.style.left = offset.left + 'px';
		this.tipDom.style.top = offset.top - 4 + 'px';

		// this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)

	},
	getCalculatedOffset: function(placement, pos, actualWidth, actualHeight) {
		return placement == 'bottom' ? {
				top: pos.top + pos.height,
				left: pos.left + pos.width / 2 - actualWidth / 2
			} :
			placement == 'top' ? {
				top: pos.top - actualHeight,
				left: pos.left + pos.width / 2 - actualWidth / 2
			} :
			placement == 'left' ? {
				top: pos.top + pos.height / 2 - actualHeight / 2,
				left: pos.left - actualWidth
			} :
			/* placement == 'right' */
			{
				top: pos.top + pos.height / 2 - actualHeight / 2,
				left: pos.left + pos.width
			}
	},
	getPosition: function(el) {
		el = el || this.element;
		var isBody = el.tagName == 'BODY';
		var elRect = el.getBoundingClientRect()
		if(elRect.width == null) {
			// width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
			elRect = extend({}, elRect, {
				width: elRect.right - elRect.left,
				height: elRect.bottom - elRect.top
			})
		}
		var elOffset = isBody ? {
			top: 0,
			left: 0
		} : {
			top: el.offsetTop,
			left: el.offsetLeft
		};
		var scroll = {
			scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : el.scrollTop
		}
		var outerDims = isBody ? {
				width: window.innerWidth || document.body.clientWidth,
				height: window.innerHeight || document.body.clientHeight
			} : null
			//return extend({}, elRect, scroll, outerDims, elOffset)
		return extend({}, elRect, scroll, outerDims)

	},
	getViewportAdjustedDelta: function(placement, pos, actualWidth, actualHeight) {
		var delta = {
			top: 0,
			left: 0
		}
		if(!this._viewport) return delta

		var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
		var viewportDimensions = this.getPosition(this._viewport)

		if(/right|left/.test(placement)) {
			var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
			var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
			if(topEdgeOffset < viewportDimensions.top) { // top overflow
				delta.top = viewportDimensions.top - topEdgeOffset
			} else if(bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
				delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
			}
		} else {
			var leftEdgeOffset = pos.left - viewportPadding
			var rightEdgeOffset = pos.left + viewportPadding + actualWidth
			if(leftEdgeOffset < viewportDimensions.left) { // left overflow
				delta.left = viewportDimensions.left - leftEdgeOffset
			} else if(rightEdgeOffset > viewportDimensions.width) { // right overflow
				delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
			}
		}

		return delta
	},
	replaceArrow: function(delta, dimension, isHorizontal) {
		if(isHorizontal) {
			this.arrow.style.left = 50 * (1 - delta / dimension) + '%';
			this.arrow.style.top = '';
		} else {
			this.arrow.style.top = 50 * (1 - delta / dimension) + '%';
			this.arrow.style.left = '';
		}
	},
	destory: function() {

	},
	setTitle: function(title) {
		this.options.title = title;
	}

};

export{
    Tooltip
}
<|endoftext|>


<commit_before>/**
 * @author alteredq / http://alteredqualia.com/
 *
 * parameters = {
 *  color: <hex>,
 *  opacity: <float>,
 *
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  linewidth: <float>,
 *
 *  scale: <float>,
 *  dashSize: <float>,
 *  gapSize: <float>,
 *
 *  vertexColors: <bool>
 *
 *  fog: <bool>
 * }
 */

THREE.LineDashedMaterial = function ( parameters ) {

	THREE.Material.call( this );

	this.type = 'LineDashedMaterial';

	this.color = new THREE.Color( 0xffffff );

	this.linewidth = 1;

	this.scale = 1;
	this.dashSize = 3;
	this.gapSize = 1;

	this.vertexColors = false;

	this.fog = true;

	this.setValues( parameters );

};

THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial;

THREE.LineBasicMaterial.prototype.copy = function ( source ) {

	THREE.Material.prototype.copy.call( this, source );

	this.color.copy( source.color );

	this.scale = source.scale;
	this.dashSize = source.dashSize;
	this.gapSize = source.gapSize;

	this.vertexColors = source.vertexColors;

	this.fog = source.fog;

	return this;

};
<commit_msg>Readd `linewidth` to `copy` in LineBasicMaterial<commit_after>/**
 * @author alteredq / http://alteredqualia.com/
 *
 * parameters = {
 *  color: <hex>,
 *  opacity: <float>,
 *
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  linewidth: <float>,
 *
 *  scale: <float>,
 *  dashSize: <float>,
 *  gapSize: <float>,
 *
 *  vertexColors: <bool>
 *
 *  fog: <bool>
 * }
 */

THREE.LineDashedMaterial = function ( parameters ) {

	THREE.Material.call( this );

	this.type = 'LineDashedMaterial';

	this.color = new THREE.Color( 0xffffff );

	this.linewidth = 1;

	this.scale = 1;
	this.dashSize = 3;
	this.gapSize = 1;

	this.vertexColors = false;

	this.fog = true;

	this.setValues( parameters );

};

THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial;

THREE.LineBasicMaterial.prototype.copy = function ( source ) {

	THREE.Material.prototype.copy.call( this, source );

	this.color.copy( source.color );

	this.linewidth = source.linewidth;

	this.scale = source.scale;
	this.dashSize = source.dashSize;
	this.gapSize = source.gapSize;

	this.vertexColors = source.vertexColors;

	this.fog = source.fog;

	return this;

};
<|endoftext|>


<commit_before>/**
 * A simple function to check if an array of strings includes a certain value.
 * @param {array} arr is the array of string
 * @param {string} s is the value to be checked if included
 */
const stringInArray = (arr, s) => {
  let found = false;
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] === s)
      found = true;
  }
  return found;
}

export { stringInArray }

/**
 * A very simple function to perform a basic
 * deep clone of an object.
 * @param {object} o is the object to perform the deep clone on
 */
const cloneDeep = (o) => {
  let ret = {};
  for (const p in o)
    ret[p] = o[p];
  return ret;
}

export { cloneDeep };

/**
 * A function to recursively merge to object, copying over all
 * properties of the old object missing from the target object.
 * In case a prop in is an object, the method is called recursively.
 * @param {object} target is the target object with the updates values.
 * @param {object} old is the old object from which the missing props are copied.
 */
const merge = (target, old) => {
  const oldKeys = Object.keys(old);
  // iterate over props of old
  for (let p in old) {
    if (!(p in target)) {
      //otherwise copy it over
      target[p] = old[p];
    }
    else {
      // in case the prop itself is an obj,
      // call method recursively,
      if (typeof target[p] === 'object' && typeof old[p] === 'object' && !Array.isArray(old[p])) {
        target[p] = merge((typeof target[p] === 'object' ? target[p] : {}), old[p]);
      }
    }
  }
  return target;
}

export { merge };

/**
 * A function get the intersection of two arrays. IE9+.
 * @param {array} arr1 is the first array of which to get the intersection
 * @param {array} arr2 is the second array of which to get the intersection
 */
const intersection = (arr1, arr2) => {
  return Array.prototype.filter.call(arr1, (n) => ~arr2.indexOf(n));
}

export { intersection };

/**
 * Debounce of Underscore.js
 */
const debounce = function (func, wait, immediate) {
  let timeout;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      timeout = null;
      if (!immediate) func.apply(context, args);
    }, wait);
    if (immediate && !timeout) func.apply(context, args);
  };
};

export { debounce };

/**
 * Fisher-Yates shuffle ES6 non-mutating implementation.
 * @param {Array} array the array to shuffle
 * @return {Array} shuffled array without mutating the initial array.
 */
const shuffle = (array) => {
  // perform deep clone on array to mutate
  let cloned = array.slice(0);
  // array to return
  let randomizedArray = [];
  // perform shuffle
  while (cloned.length !== 0) {
    let rIndex = Math.floor(cloned.length * Math.random());
    randomizedArray.push(cloned[rIndex]);
    cloned.splice(rIndex, 1)
  }
  return randomizedArray;
};

export { shuffle };

/**
 * Simple method to check if two arrays of FilterItems
 * are sorted in the same manner or not.
 * @param {Array} arr1 the first array of FilterItems
 * @param {Array} arr2 the second array of FilterItems
 * @return {Boolean} equality
 */
const filterItemArraysHaveSameSorting = (a1, a2) => {
  // Exit case if arrays do not have equal length
  if (a1.length !== a2.length) return false;
  // Iterate over first array and compare indices with second
  for (let i = 0; i < a1.length; i++) {
    const index1 = a1[i].props.index;
    const index2 = a2[i].props.index;
    // Means arrays do not have identical sorting
    if (index1 !== index2) return false;
  }
  return true;
}

export { filterItemArraysHaveSameSorting };

/**
 * Simple non-mutating sorting function for arrays of objects by a property
 * @param {Array} array to sort
 * @param {Function} propFn fetches the property by which to sort
 * @return {Array} a new sorted array
 */
const sortBy = (array, propFn) => {
  let cloned = array.slice(0); // perform deep copy of array

  const comparator = (propFn) => {
    return (a, b) => {
      const propA = propFn(a);
      const propB = propFn(b);
      if (propA < propB) {
        return -1;
      } else if (propA > propB) {
        return 1;
      } else {
        return 0;
      }
    }
  }

  return cloned.sort(comparator(propFn));
}

export { sortBy }
<commit_msg>Update docs<commit_after>/**
 * A simple function to check if an array of strings includes a certain value.
 * @param {Array} arr is the array of string
 * @param {String} s is the value to be checked if included
 */
const stringInArray = (arr, s) => {
  let found = false;
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] === s)
      found = true;
  }
  return found;
}

export { stringInArray }

/**
 * A very simple function to perform a basic
 * deep clone of an object.
 * @param {Object} o is the object to perform the deep clone on
 * @return {Object} deep clone
 */
const cloneDeep = (o) => {
  let ret = {};
  for (const p in o)
    ret[p] = o[p];
  return ret;
}

export { cloneDeep };

/**
 * A function to recursively merge to object, copying over all
 * properties of the old object missing from the target object.
 * In case a prop in is an object, the method is called recursively.
 * @param {Object} target is the target object with the updates values.
 * @param {Object} old is the old object from which the missing props are copied.
 */
const merge = (target, old) => {
  const oldKeys = Object.keys(old);
  // iterate over props of old
  for (let p in old) {
    if (!(p in target)) {
      //otherwise copy it over
      target[p] = old[p];
    }
    else {
      // in case the prop itself is an obj,
      // call method recursively,
      if (typeof target[p] === 'object' && typeof old[p] === 'object' && !Array.isArray(old[p])) {
        target[p] = merge((typeof target[p] === 'object' ? target[p] : {}), old[p]);
      }
    }
  }
  return target;
}

export { merge };

/**
 * A function get the intersection of two arrays. IE9+.
 * @param {Array} arr1 is the first array of which to get the intersection
 * @param {Array} arr2 is the second array of which to get the intersection
 */
const intersection = (arr1, arr2) => {
  return Array.prototype.filter.call(arr1, (n) => ~arr2.indexOf(n));
}

export { intersection };

/**
 * Debounce of Underscore.js
 */
const debounce = function (func, wait, immediate) {
  let timeout;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      timeout = null;
      if (!immediate) func.apply(context, args);
    }, wait);
    if (immediate && !timeout) func.apply(context, args);
  };
};

export { debounce };

/**
 * Fisher-Yates shuffle ES6 non-mutating implementation.
 * @param {Array} array the array to shuffle
 * @return {Array} shuffled array without mutating the initial array.
 */
const shuffle = (array) => {
  // perform deep clone on array to mutate
  let cloned = array.slice(0);
  // array to return
  let randomizedArray = [];
  // perform shuffle
  while (cloned.length !== 0) {
    let rIndex = Math.floor(cloned.length * Math.random());
    randomizedArray.push(cloned[rIndex]);
    cloned.splice(rIndex, 1)
  }
  return randomizedArray;
};

export { shuffle };

/**
 * Simple method to check if two arrays of FilterItems
 * are sorted in the same manner or not.
 * @param {Array} arr1 the first array of FilterItems
 * @param {Array} arr2 the second array of FilterItems
 * @return {Boolean} equality
 */
const filterItemArraysHaveSameSorting = (a1, a2) => {
  // Exit case if arrays do not have equal length
  if (a1.length !== a2.length) return false;
  // Iterate over first array and compare indices with second
  for (let i = 0; i < a1.length; i++) {
    const index1 = a1[i].props.index;
    const index2 = a2[i].props.index;
    // Means arrays do not have identical sorting
    if (index1 !== index2) return false;
  }
  return true;
}

export { filterItemArraysHaveSameSorting };

/**
 * Simple non-mutating sorting function for arrays of objects by a property
 * @param {Array} array to sort
 * @param {Function} propFn fetches the property by which to sort
 * @return {Array} a new sorted array
 */
const sortBy = (array, propFn) => {
  let cloned = array.slice(0); // perform deep copy of array

  const comparator = (propFn) => {
    return (a, b) => {
      const propA = propFn(a);
      const propB = propFn(b);
      if (propA < propB) {
        return -1;
      } else if (propA > propB) {
        return 1;
      } else {
        return 0;
      }
    }
  }

  return cloned.sort(comparator(propFn));
}

export { sortBy }
<|endoftext|>


<commit_before>'use strict';

// Load modules
const i18n = require('../services/i18n');
const fs = require('fs');
const Remarkable = require('remarkable');
const path = require('path');
const logger = require('../services/logger');

// Setup
const md = new Remarkable({
  html: true
});

/**
 * Concatenates first and second.
 * @param first
 * @param second
 */
exports.concat = (first, second) => first + second;

/**
 * Returns true if v1 == v2.
 * @param v1
 * @param v2
 * @param options
 * @returns {*}
 */
exports.ifCond = function (v1, v2, options) {
  if (v1 === v2) {
    return options.fn(this);
  }

  return options.inverse(this);
};

/**
 * Render markdown from content/mdFileName to html
 * @param mdFileName
 * @returns rendered html
 */
exports.markdown = function renderMarkdown(mdFileName, context) {
  const rawMd = loadFileContent(mdFileName);
  const html = md.render(rawMd);
  return html;
};

exports.md = function renderMarkdown(rawMd, context) {
  const html = md.render(rawMd);
  return html;
};

function loadFileContent(mdFileName) {
  const path = getFilepath(mdFileName);
  return fs.readFileSync(path, 'utf-8');
}

function getFilepath(mdFileName) {
  const contentFolderPath = path.resolve('content/');
  return `${contentFolderPath}/${mdFileName}.md`;
}

/**
 * Tries to find the matching translation for the language the browser sent us.
 * @param text
 * @param options
 * @private
 */
exports.__ = (text, options) => {

  if (!options.data.root.language) {
    throw 'You did not pass the language to handlebars!';
  }

  const view = options.data.exphbs.view;
  let viewArr = [];

  if (view) {
    if (view.indexOf('\\') > -1) {
      viewArr = view.split('\\');
    } else {
      viewArr = view.split('/');
    }
  }

  if (text.indexOf('.') > -1) {
    viewArr = text.split('.');
    text = viewArr.pop();
  } else if (!view) {
    logger.error(`Could not parse view in ${options.data.exphbs}`);
  }

  return i18n.translate(viewArr[viewArr.length - 1].toUpperCase(), text.toUpperCase(), options.data.root.language);
};

exports.ifOr = function (v1, v2, options) {
  if (v1 || v2) {
    return options.fn(this);
  }

  return options.inverse(this);
};

exports.json = function (context) {
  return JSON.stringify(context);
};

exports.relativeTime = function (timestamp) {

  function leftPad(zahlen) {
    let string = '00' + zahlen;
    return string.substring(string.length - 2);
  }

  const MONTHS = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'];
  let dO = new Date(timestamp * 1000);
  let now = Date.now();
  let difference = now - dO.getTime();
  if (difference < 60 * 1000) {
    return 'Gerade eben';
  } else if (difference < 60 * 60 * 1000) {
    return `vor ${Math.floor(difference / 60 / 1000)} Minuten`;
  } else if (difference < 60 * 60 * 24 * 1000) {
    return `vor ${Math.floor(difference / 60 / 60 / 1000)} Stunden`;
  } else {
    return `am ${dO.getDate()}. ${MONTHS[dO.getMonth()]} um ${dO.getHours()}:${leftPad(dO.getMinutes())}`;
  }
};

exports.length = function (array) {
  return array.length;
};

exports.strColor = (str) => {
  var hash = 0;
  for (let i = 0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 5) - hash);
  }
  var colour = '#';
  for (let i = 0; i < 3; i++) {
    var value = (hash >> (i * 8)) & 0xFF;
    colour += ('00' + value.toString(16)).substr(-2);
  }
  return colour;
};

exports.smallestImage = (sizes) => {

  if (Array.isArray(sizes) && sizes.length > 0) {
    var minSize = sizes[0].size;
    var url = sizes[0].url;

    sizes.forEach((thisSize) => {
      if (thisSize.size < minSize) {
        minSize = thisSize.size;
        url = thisSize.url;
      }
    });

    return url;
  }
};

exports.getImageByWidth = (width, sizes) => {

  width = parseFloat(width);

  if (Array.isArray(sizes)) {
    sizes = sizes.filter((size) => size.type === 'IMAGE');
    if (sizes.length > 0) {

      var minDiff = 100000000000;
      var bestFit = sizes[0].url;
      sizes.forEach(s => {
        let currDiff = s.width - width;
        if (currDiff < 0) currDiff = currDiff * -8;

        if (currDiff < minDiff) {
          minDiff = currDiff;
          bestFit = s.url;
        }
      });

      return bestFit;
    }
  }
};

exports.getH264VideoByWidth = (width, sizes) => {

  width = parseFloat(width);

  if (Array.isArray(sizes)) {
    sizes = sizes.filter((size) => size.type === 'VIDEO' && size.url.endsWith('.mp4'));

    if (sizes.length > 0) {

      var minDiff = 100000000000;
      var bestFit = sizes[0].url;
      sizes.forEach(s => {
        let currDiff = s.width - width;
        if (currDiff < 0) currDiff = currDiff * -8;

        if (currDiff < minDiff) {
          minDiff = currDiff;
          bestFit = s.url;
        }
      });

      return bestFit;
    }
  }
};

exports.getWebmVideoByWidth = (width, sizes) => {

  width = parseFloat(width);

  if (Array.isArray(sizes)) {
    sizes = sizes.filter((size) => size.type === 'VIDEO' && size.url.endsWith('.webm'));

    if (sizes.length > 0) {

      var minDiff = 100000000000;
      var bestFit = sizes[0].url;
      sizes.forEach(s => {
        let currDiff = s.width - width;
        if (currDiff < 0) currDiff = currDiff * -8;

        if (currDiff < minDiff) {
          minDiff = currDiff;
          bestFit = s.url;
        }
      });

      return bestFit;
    }
  }
};

exports.round = (amount) => {
  return Math.round(parseFloat(amount)).toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
};

exports.roundWithoutFormat = (amount) => {
  return Math.round(parseFloat(amount));
};

exports.addOne = (amount) => {
  return (parseFloat(amount) + 1);
};

exports.prettyLocation = (location) => {
  //Check if it exists.
  if (!location) return '';

  var locString = '';

  //Check for best Level
  if (location.hasOwnProperty('LOCALITY')) {
    locString = location.LOCALITY;
  } else if (location.hasOwnProperty('ADMINISTRATIVE_AREA_LEVEL_3')) {
    locString = location.ADMINISTRATIVE_AREA_LEVEL_3;
  } else if (location.hasOwnProperty('ADMINISTRATIVE_AREA_LEVEL_2')) {
    locString = location.ADMINISTRATIVE_AREA_LEVEL_2;
  } else if (location.hasOwnProperty('ADMINISTRATIVE_AREA_LEVEL_1')) {
    locString = location.ADMINISTRATIVE_AREA_LEVEL_1;
  }

  if (location.hasOwnProperty('COUNTRY')) {
    if (locString !== '') {
      locString += ', ';
    }
    locString += location.COUNTRY;
  }
  if (locString !== '') {
    locString = ' in ' + locString;
  }
  return locString;
};

exports.getSmallestAudio = (sizes) => {
  if (Array.isArray(sizes) && sizes.length > 0) {

    var filteredsizes = sizes.filter((size) => size.type === 'AUDIO').filter((s) => s.url.endsWith('.mp3'));

    if (filteredsizes.length === 0) {
      filteredsizes = sizes;
    }

    var minsize = 100000000000;
    var url = filteredsizes[0].url;

    filteredsizes.forEach(s => {
      if (s.size < minsize) {
        minsize = s.size;
        url = s.url;
      }
    });

    return url;
  }
};

exports.challengeHasProof = (status) => {
  if (status === 'WITH_PROOF' || status === 'PROOF_ACCEPTED') {
    return true;
  }

  return false;
};<commit_msg>Add handlebars helper for contentful images<commit_after>'use strict';

// Load modules
const i18n = require('../services/i18n');
const fs = require('fs');
const Remarkable = require('remarkable');
const path = require('path');
const logger = require('../services/logger');

// Setup
const md = new Remarkable({
  html: true
});

/**
 * Concatenates first and second.
 * @param first
 * @param second
 */
exports.concat = (first, second) => first + second;

/**
 * Returns true if v1 == v2.
 * @param v1
 * @param v2
 * @param options
 * @returns {*}
 */
exports.ifCond = function (v1, v2, options) {
  if (v1 === v2) {
    return options.fn(this);
  }

  return options.inverse(this);
};

/**
 * Render markdown from content/mdFileName to html
 * @param mdFileName
 * @returns rendered html
 */
exports.markdown = function renderMarkdown(mdFileName, context) {
  const rawMd = loadFileContent(mdFileName);
  const html = md.render(rawMd);
  return html;
};

exports.md = function renderMarkdown(rawMd, context) {
  const html = md.render(rawMd);
  return html;
};

exports.contentfulImage = function (imageObject, clazz, id, context) {

  clazz = (clazz != null) ? clazz : '';
  id = (id != null) ? id : '';

  const url = imageObject.fields.file.url;
  const alt = (imageObject.fields.description != null) ? imageObject.fields.description : '';

  return `<img src="${url}" alt="${alt}" class="${clazz}" id="${id}"/>`;
};

function loadFileContent(mdFileName) {
  const path = getFilepath(mdFileName);
  return fs.readFileSync(path, 'utf-8');
}

function getFilepath(mdFileName) {
  const contentFolderPath = path.resolve('content/');
  return `${contentFolderPath}/${mdFileName}.md`;
}

/**
 * Tries to find the matching translation for the language the browser sent us.
 * @param text
 * @param options
 * @private
 */
exports.__ = (text, options) => {

  if (!options.data.root.language) {
    throw 'You did not pass the language to handlebars!';
  }

  const view = options.data.exphbs.view;
  let viewArr = [];

  if (view) {
    if (view.indexOf('\\') > -1) {
      viewArr = view.split('\\');
    } else {
      viewArr = view.split('/');
    }
  }

  if (text.indexOf('.') > -1) {
    viewArr = text.split('.');
    text = viewArr.pop();
  } else if (!view) {
    logger.error(`Could not parse view in ${options.data.exphbs}`);
  }

  return i18n.translate(viewArr[viewArr.length - 1].toUpperCase(), text.toUpperCase(), options.data.root.language);
};

exports.ifOr = function (v1, v2, options) {
  if (v1 || v2) {
    return options.fn(this);
  }

  return options.inverse(this);
};

exports.json = function (context) {
  return JSON.stringify(context);
};

exports.relativeTime = function (timestamp) {

  function leftPad(zahlen) {
    let string = '00' + zahlen;
    return string.substring(string.length - 2);
  }

  const MONTHS = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'];
  let dO = new Date(timestamp * 1000);
  let now = Date.now();
  let difference = now - dO.getTime();
  if (difference < 60 * 1000) {
    return 'Gerade eben';
  } else if (difference < 60 * 60 * 1000) {
    return `vor ${Math.floor(difference / 60 / 1000)} Minuten`;
  } else if (difference < 60 * 60 * 24 * 1000) {
    return `vor ${Math.floor(difference / 60 / 60 / 1000)} Stunden`;
  } else {
    return `am ${dO.getDate()}. ${MONTHS[dO.getMonth()]} um ${dO.getHours()}:${leftPad(dO.getMinutes())}`;
  }
};

exports.length = function (array) {
  return array.length;
};

exports.strColor = (str) => {
  var hash = 0;
  for (let i = 0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 5) - hash);
  }
  var colour = '#';
  for (let i = 0; i < 3; i++) {
    var value = (hash >> (i * 8)) & 0xFF;
    colour += ('00' + value.toString(16)).substr(-2);
  }
  return colour;
};

exports.smallestImage = (sizes) => {

  if (Array.isArray(sizes) && sizes.length > 0) {
    var minSize = sizes[0].size;
    var url = sizes[0].url;

    sizes.forEach((thisSize) => {
      if (thisSize.size < minSize) {
        minSize = thisSize.size;
        url = thisSize.url;
      }
    });

    return url;
  }
};

exports.getImageByWidth = (width, sizes) => {

  width = parseFloat(width);

  if (Array.isArray(sizes)) {
    sizes = sizes.filter((size) => size.type === 'IMAGE');
    if (sizes.length > 0) {

      var minDiff = 100000000000;
      var bestFit = sizes[0].url;
      sizes.forEach(s => {
        let currDiff = s.width - width;
        if (currDiff < 0) currDiff = currDiff * -8;

        if (currDiff < minDiff) {
          minDiff = currDiff;
          bestFit = s.url;
        }
      });

      return bestFit;
    }
  }
};

exports.getH264VideoByWidth = (width, sizes) => {

  width = parseFloat(width);

  if (Array.isArray(sizes)) {
    sizes = sizes.filter((size) => size.type === 'VIDEO' && size.url.endsWith('.mp4'));

    if (sizes.length > 0) {

      var minDiff = 100000000000;
      var bestFit = sizes[0].url;
      sizes.forEach(s => {
        let currDiff = s.width - width;
        if (currDiff < 0) currDiff = currDiff * -8;

        if (currDiff < minDiff) {
          minDiff = currDiff;
          bestFit = s.url;
        }
      });

      return bestFit;
    }
  }
};

exports.getWebmVideoByWidth = (width, sizes) => {

  width = parseFloat(width);

  if (Array.isArray(sizes)) {
    sizes = sizes.filter((size) => size.type === 'VIDEO' && size.url.endsWith('.webm'));

    if (sizes.length > 0) {

      var minDiff = 100000000000;
      var bestFit = sizes[0].url;
      sizes.forEach(s => {
        let currDiff = s.width - width;
        if (currDiff < 0) currDiff = currDiff * -8;

        if (currDiff < minDiff) {
          minDiff = currDiff;
          bestFit = s.url;
        }
      });

      return bestFit;
    }
  }
};

exports.round = (amount) => {
  return Math.round(parseFloat(amount)).toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
};

exports.roundWithoutFormat = (amount) => {
  return Math.round(parseFloat(amount));
};

exports.addOne = (amount) => {
  return (parseFloat(amount) + 1);
};

exports.prettyLocation = (location) => {
  //Check if it exists.
  if (!location) return '';

  var locString = '';

  //Check for best Level
  if (location.hasOwnProperty('LOCALITY')) {
    locString = location.LOCALITY;
  } else if (location.hasOwnProperty('ADMINISTRATIVE_AREA_LEVEL_3')) {
    locString = location.ADMINISTRATIVE_AREA_LEVEL_3;
  } else if (location.hasOwnProperty('ADMINISTRATIVE_AREA_LEVEL_2')) {
    locString = location.ADMINISTRATIVE_AREA_LEVEL_2;
  } else if (location.hasOwnProperty('ADMINISTRATIVE_AREA_LEVEL_1')) {
    locString = location.ADMINISTRATIVE_AREA_LEVEL_1;
  }

  if (location.hasOwnProperty('COUNTRY')) {
    if (locString !== '') {
      locString += ', ';
    }
    locString += location.COUNTRY;
  }
  if (locString !== '') {
    locString = ' in ' + locString;
  }
  return locString;
};

exports.getSmallestAudio = (sizes) => {
  if (Array.isArray(sizes) && sizes.length > 0) {

    var filteredsizes = sizes.filter((size) => size.type === 'AUDIO').filter((s) => s.url.endsWith('.mp3'));

    if (filteredsizes.length === 0) {
      filteredsizes = sizes;
    }

    var minsize = 100000000000;
    var url = filteredsizes[0].url;

    filteredsizes.forEach(s => {
      if (s.size < minsize) {
        minsize = s.size;
        url = s.url;
      }
    });

    return url;
  }
};

exports.challengeHasProof = (status) => {
  if (status === 'WITH_PROOF' || status === 'PROOF_ACCEPTED') {
    return true;
  }

  return false;
};<|endoftext|>


<commit_before>/**
 * momentjs-business.js
 * businessDiff (mStartDate)
 * businessAdd (numberOfDays)
 */
(function () {
  var moment;
  moment = (typeof require !== "undefined" && require !== null) &&
           !require.amd ? require("moment") : this.moment;

  moment.fn.businessDiff = function (start) {
    start = moment(start);
    var end = this.clone();
    var start_offset = start.day() - 7;
    var end_offset = end.day();

    var end_sunday = end.clone().subtract('d', end_offset).startOf('day');
    var start_sunday = start.clone().subtract('d', start_offset).startOf('day');
    var weeks = end_sunday.diff(start_sunday, 'days') / 7;

    start_offset = Math.abs(start_offset);
    if(start_offset == 7)
      start_offset = 5;
    else if(start_offset == 1)
      start_offset = 0;
    else
      start_offset -= 2;

    if(end_offset == 6)
      end_offset--;

    return weeks * 5 + start_offset + end_offset;
  };

  moment.fn.businessAdd = function (days) {
    var d = this.clone().add('d', Math.floor(days / 5) * 7);
    var remaining = days % 5;
    while(remaining){
      d.add('d', 1);
      if(d.day() !== 0 && d.day() !== 6)
        remaining--;
    }
    return d;
  };

}).call(this);
<commit_msg>Update momentjs-business.js<commit_after>/**
 * momentjs-business.js
 * businessDiff (mStartDate)
 * businessAdd (numberOfDays)
 */
(function () {
  var moment;
  moment = (typeof require !== "undefined" && require !== null) &&
           !require.amd ? require("moment") : this.moment;

  moment.fn.businessDiff = function (param) {
    param = moment(param);
    var signal = param.unix() < this.unix()?1:-1;
    var start = moment.min(param, this).clone();
    var end = moment.max(param, this).clone();
    var start_offset = start.day() - 7;
    var end_offset = end.day();

    var end_sunday = end.clone().subtract('d', end_offset);
    var start_sunday = start.clone().subtract('d', start_offset);
    var weeks = end_sunday.diff(start_sunday, 'days') / 7;

    start_offset = Math.abs(start_offset);
    if(start_offset == 7)
      start_offset = 5;
    else if(start_offset == 1)
      start_offset = 0;
    else
      start_offset -= 2;

    if(end_offset == 6)
      end_offset--;

    return signal * (weeks * 5 + start_offset + end_offset);
  };

  moment.fn.businessAdd = function (days) {
    var signal = days<0?-1:1;
    days = Math.abs(days);
    var d = this.clone().add(Math.floor(days / 5) * 7 * signal, 'd');
    var remaining = days % 5;
    while(remaining){
      d.add(signal, 'd');
      if(d.day() !== 0 && d.day() !== 6)
        remaining--;
    }
    return d;
  };

  moment.fn.businessSubtract = function(days){
    return this.businessAdd(-days);
  };

}).call(this);
<|endoftext|>


<commit_before>// OpenWrap v2
// Author: Nuno Aguiar
// Metrics

OpenWrap.metrics = function() {
	return ow.metrics;
};

OpenWrap.metrics.prototype.__m = {
    mem: () => {
        var res = {
            max: Number(java.lang.Runtime.getRuntime().maxMemory()),
            total: Number(java.lang.Runtime.getRuntime().totalMemory()),
            free: Number(java.lang.Runtime.getRuntime().freeMemory())
        };
        res.used = res.total - res.free;
        return res;
    },
    cpu: () => ({
        load1 : getCPULoad(),
        load2 : getCPULoad(true),
        cores : getNumberOfCores(),
        _cores: __cpucores
    }),
    oaf: () => {
        var res = {
            libs : __loadedLibs,
            nlibs: Object.keys(__loadedLibs).length,
            nscopes: af.getScopeIds().length,
            preCompileLevel: __preCompileLevel,
            version: getVersion(),
            path   : getOpenAFPath(),
            java   : String(java.lang.System.getProperty("java.version")),
            javapath: String(java.lang.System.getProperty("java.home")),
            os     : String(java.lang.System.getProperty("os.name")),
            init   : __oafInit,
            now    : now(),
            logErr : __clogErr.get(),
            logWarn: __clogWarn.get(),
            cpuCores: __cpucores,
            threadPoolFactor: __threadPoolFactor,
            bottlenecks: (isDef(global.__bottleneck) ? Object.keys(global.__bottleneck).map(r => ({
                name    : r,
                running : global.__bottleneck[r].atomic.get(),
                maxWait : global.__bottleneck[r].attw,
                maxExecs: global.__bottleneck[r].ance
            })) : "n/a"),
            cache: (isDef(global.__cache) ? Object.keys(global.__cache).map(r => ({
                name: r,
                chName: global.__cache[r].name,
                ttl : global.__cache[r].ttl,
                maxSize: global.__cache[r].msize,
                size: global.__cache[r].size(),
                hits: ow.ch.__types.cache.__cacheStats[global.__cache[r].name].hits,
                miss: ow.ch.__types.cache.__cacheStats[global.__cache[r].name].miss,
                avgExecTime: ow.ch.__types.cache.__cacheStats[global.__cache[r].name].avg,
            })) : "n/a"),
            rest: (isDef(global.__openaf_rest) ? Object.keys(global.__openaf_rest.urls).map(r => ({
                url: r,
                hits: _$(global.__openaf_rest.urls[r].c).default(0),
                miss: _$(global.__openaf_rest.urls[r].f).default(0)
            })) : "n/a"),
            fns    : ow.metrics.__fnMetrics
        };
        if (isDef(res.fns)) {
            Object.keys(res.fns).map(r => {
                res.fns[r].avg = res.fns[r].sum / (res.fns[r].err + res.fns[r].end);
            });
        }
        return res;
    },
    ojob: () => {
        var res = {};
        if (isDef(ow.oJob)) {
            res = ow.oJob.getLogCh().getAll().map(j => ({
                name : j.name,
                start: j.count,
                //err  : $path(j.log, "[?not_null(error)] | length(@)"),
                //end  : $path(j.log, "[?!not_null(error)] | length(@)"),
                error: j.error,
                sum  : j.totalTime,
                avg  : j.avgTime
            }) );
        }
        return res;
    },
    os: () => ({
        pid: getPid()
    }),
    threads: () => {
        var res = {
            list  : af.fromJavaArray(java.lang.Thread.getAllStackTraces().keySet().toArray()).map(t => ({
                id      : t.getId(),
                name    : String(t.getName()),
                isAlive : t.isAlive(),
                priority: Number(t.getPriority()),
                state   : String(t.getState())
            })),
            active: Number(java.lang.Thread.activeCount()),
            total : Number(java.lang.Thread.getAllStackTraces().size())
        };
        res.states = {};
        res.list.map(r => {
            if (isUnDef(res.states[r.state]))
                res.states[r.state] = 1;
            else
                res.states[r.state] += 1;
        });
        return res;
    }
};

/**
 * <odoc>
 * <key>ow.metrics.add(aName, aFunction)</key>
 * Adds aName metric whose values are a map returned by aFunction.
 * </odoc>
 */
OpenWrap.metrics.prototype.add = function(aName, aFn) {
    if (isUnDef(ow.metrics.__m[aName])) {
        ow.metrics.__m[aName] = aFn;
    }
};

/**
 * <odoc>
 * <key>ow.metrics.getSome(anArrayOfNames) : Map</key>
 * Returns just the metrics in the provided anArrayOfNames.
 * </odoc>
 */
OpenWrap.metrics.prototype.getSome = function(aArray) {
    var r = {}, errors = [];
    aArray.map(f => {
        try {
            r[f] = ow.metrics.__m[f]();
        } catch(e) {
            r[f] = "error";
            logErr(e);
        }
    });
    return r;
};

/**
 * <odoc>
 * <key>ow.metrics.getAll() : Map</key>
 * Returns a map with all registered metrics.
 * </odoc>
 */
OpenWrap.metrics.prototype.getAll = function() {
    return ow.metrics.getSome(Object.keys(ow.metrics.__m));
};

/**
 * <odoc>
 * <key>ow.metrics.startCollecting(aChName, aPeriod, some)</key>
 * Starts collecting metrics on aChName (defaults to '__metrics') every aPeriod ms (defaults to 1000ms) optionally just some (array) metrics.
 * </odoc>
 */
OpenWrap.metrics.prototype.startCollecting = function(aChName, aPeriod, aSome) {
    var createCh = isUnDef(aChName) || $ch().list().indexOf(aChName) < 0;

    if (isUnDef(ow.metrics.__fnMetrics)) ow.metrics.__fnMetrics = {};

    aChName = _$(aChName).isString().default("__metrics");
    aPeriod = _$(aPeriod).isNumber().default(1000);

    if (createCh) $ch(aChName).create(1, "mvs", { file: "metrics.db" });

    if (isUnDef(ow.metrics.__ch)) ow.metrics.__ch = [];
    ow.metrics.__ch.push(aChName);

    if (isUnDef(ow.metrics.__t)) {
        plugin("Threads");
        ow.metrics.__t = new Threads();
        ow.metrics.__t.addScheduleThreadWithFixedDelay(function() {
            var k = { t: now() };
            var v;
            if(isArray(aSome))
               v = merge(k, ow.metrics.getSome(aSome));
            else
	       v = merge(k, ow.metrics.getAll());

            ow.metrics.__ch.map(ch => {
                $ch(ch).set(k, v);
            });
        }, aPeriod);
        ow.metrics.__t.startNoWait();
    }
};

/**
 * <odoc>
 * <key>ow.metrics.stopCollecting(aChName)</key>
 * Stops collecting metrics on aChName (defaults to '__metrics')
 * </odoc>
 */
OpenWrap.metrics.prototype.stopCollecting = function(aChName) {
    aChName = _$(aChName).isString().default("__metrics");
    if (ow.metrics.__ch.indexOf(aChName) < 0) throw "Not collecting into " + aChName;

    ow.metrics.__ch = deleteFromArray(ow.metrics.__ch, ow.metrics.__ch.indexOf(aChName));
    if (isDef(ow.metrics.__t) && ow.metrics.__ch.length <= 0) {
        ow.metrics.__t.stop(true);
        ow.metrics.__t = void 0;
    }
};

/**
 * <odoc>
 * <key>ow.metrics.collectMetrics4Fn(aName, aFn)</key>
 * Adds extra code to an existing aFn to collect functions metrics under the name aName. If the same aName and aFn
 * has been already executed before it will throw an exception "Already collecting for the provided function."
 * </odoc>
 */
OpenWrap.metrics.prototype.collectMetrics4Fn = function(aName, aFn) {
    _$(aName, "name").isString().$_();
    _$(aFn, "fn").isString().$_();

    var aBody = eval(aFn).toString();
    if (isUnDef(ow.metrics.__fnMetrics)) ow.metrics.__fnMetrics = {};
    if (isDef(ow.metrics.__fnMetrics[aName])) throw "Already collecting for the provided function.";

    aBody = aBody.split(/\n/);
    var newBody = [];
    ow.metrics.__fnMetrics[aName] = {
        start: 0,
        err  : 0,
        end  : 0,
        sum  : 0,
        avg  : 0
    };

    for(var ii in aBody) {
        if (ii == 2 || ii == aBody.length -2) {
            if (ii == 2) {
                newBody.push("ow.metrics.__fnMetrics[\"" + aName + "\"].start += 1; var __resMetricsProxyE, __resMetricsProxyR, __resMetricsProxyI = now(); try { __resMetricsProxyR = (function(){");
            } else {
                newBody.push("})(); ow.metrics.__fnMetrics[\"" + aName + "\"].end += 1; ow.metrics.__fnMetrics[\"" + aName + "\"].sum += (now() - __resMetricsProxyI); return __resMetricsProxyR; } catch(__fnMetricsProxyE) { ow.metrics.__fnMetrics[\"" + aName + "\"].err += 1; throw __fnMetricsProxyE; }");
            }
        }
        newBody.push(aBody[ii]);
    }
    eval(aFn + " = " + newBody.join("\n"));
};

/**
 * <odoc>
 * <key>ow.metrics.collectMetrics(aName, aFunction) : Object</key>
 * Executes aFunction while collecting functions metrics under the name aName. Returns whatever the function returns
 * or throws any exception.
 * </odoc>
 */
OpenWrap.metrics.prototype.collectMetrics = function(aName, aFunction) {
    _$(aName, "name").isString().$_();
    _$(aFunction).isFunction().$_();

    if (isUnDef(ow.metrics.__fnMetrics)) ow.metrics.__fnMetrics = {};
    if (isUnDef(ow.metrics.__fnMetrics[aName])) {
        ow.metrics.__fnMetrics[aName] = {
            start: 0,
            err  : 0,
            end  : 0,
            sum  : 0
        };
    }

    var __resMetricsProxyE, __resMetricsProxyR, __resMetricsProxyI = now(), res;
    try {
        ow.metrics.__fnMetrics[aName].start += 1;
        __resMetricsProxyR = aFunction();
        ow.metrics.__fnMetrics[aName].end += 1;
        return __resMetricsProxyR;
    } catch(__resMetricsProxyE) {
        ow.metrics.__fnMetrics[aName].err += 1;
        throw __resMetricsProxyE;
    }
}
<commit_msg>metrics: +mainThreadPool<commit_after>// OpenWrap v2
// Author: Nuno Aguiar
// Metrics

OpenWrap.metrics = function() {
	return ow.metrics;
};

OpenWrap.metrics.prototype.__m = {
    mem: () => {
        var res = {
            max: Number(java.lang.Runtime.getRuntime().maxMemory()),
            total: Number(java.lang.Runtime.getRuntime().totalMemory()),
            free: Number(java.lang.Runtime.getRuntime().freeMemory())
        };
        res.used = res.total - res.free;
        return res;
    },
    cpu: () => ({
        load1 : java.lang.System.getProperty("os.name").indexOf("Windows") < 0 ? getCPULoad() : "n/a",
        load2 : java.lang.System.getProperty("os.name").indexOf("Windows") < 0 ? getCPULoad(true) : "n/a",
        cores : getNumberOfCores(),
        _cores: __cpucores
    }),
    oaf: () => {
        var res = {
            libs : __loadedLibs,
            nlibs: Object.keys(__loadedLibs).length,
            nscopes: af.getScopeIds().length,
            preCompileLevel: __preCompileLevel,
            version: getVersion(),
            path   : getOpenAFPath(),
            java   : String(java.lang.System.getProperty("java.version")),
            javapath: String(java.lang.System.getProperty("java.home")),
            init   : __oafInit,
            now    : now(),
            logErr : __clogErr.get(),
            logWarn: __clogWarn.get(),
            cpuCores: __cpucores,
            threadPoolFactor: __threadPoolFactor,
            mainThreadPool: {
                parallelism: __getThreadPool().getParallelism(),
                size: __getThreadPool().getPoolSize(),
                active: __getThreadPool().getActiveThreadCount(),
                running: __getThreadPool().getRunningThreadCount(),
                steals: __getThreadPool().getStealCount(),
                tasks: __getThreadPool().getQueuedTaskCount(),
                queued: __getThreadPool().getQueuedSubmissionCount()
            },
            bottlenecks: (isDef(global.__bottleneck) ? Object.keys(global.__bottleneck).map(r => ({
                name    : r,
                running : global.__bottleneck[r].atomic.get(),
                maxWait : global.__bottleneck[r].attw,
                maxExecs: global.__bottleneck[r].ance
            })) : "n/a"),
            cache: (isDef(global.__cache) ? Object.keys(global.__cache).map(r => ({
                name: r,
                chName: global.__cache[r].name,
                ttl : global.__cache[r].ttl,
                maxSize: global.__cache[r].msize,
                size: global.__cache[r].size(),
                hits: ow.ch.__types.cache.__cacheStats[global.__cache[r].name].hits,
                miss: ow.ch.__types.cache.__cacheStats[global.__cache[r].name].miss,
                avgExecTime: ow.ch.__types.cache.__cacheStats[global.__cache[r].name].avg,
            })) : "n/a"),
            rest: (isDef(global.__openaf_rest) ? Object.keys(global.__openaf_rest.urls).map(r => ({
                url: r,
                hits: _$(global.__openaf_rest.urls[r].c).default(0),
                miss: _$(global.__openaf_rest.urls[r].f).default(0)
            })) : "n/a"),
            fns    : ow.metrics.__fnMetrics
        };
        if (isDef(res.fns)) {
            Object.keys(res.fns).map(r => {
                res.fns[r].avg = res.fns[r].sum / (res.fns[r].err + res.fns[r].end);
            });
        }
        return res;
    },
    ojob: () => {
        var res = {};
        if (isDef(ow.oJob)) {
            res = ow.oJob.getLogCh().getAll().map(j => ({
                name : j.name,
                start: j.count,
                //err  : $path(j.log, "[?not_null(error)] | length(@)"),
                //end  : $path(j.log, "[?!not_null(error)] | length(@)"),
                error: j.error,
                sum  : j.totalTime,
                avg  : j.avgTime
            }) );
        }
        return res;
    },
    os: () => ({
        pid    : getPid(),
        name   : String(java.lang.System.getProperty("os.name")),
        host   : String(java.net.InetAddress.getLocalHost().getHostName()),
        ip     : String(java.net.InetAddress.getLocalHost().getHostAddress())
    }),
    threads: () => {
        var res = {
            list  : af.fromJavaArray(java.lang.Thread.getAllStackTraces().keySet().toArray()).map(t => ({
                id      : t.getId(),
                name    : String(t.getName()),
                isAlive : t.isAlive(),
                priority: Number(t.getPriority()),
                state   : String(t.getState())
            })),
            active: Number(java.lang.Thread.activeCount()),
            total : Number(java.lang.Thread.getAllStackTraces().size())
        };
        res.states = {};
        res.list.map(r => {
            if (isUnDef(res.states[r.state]))
                res.states[r.state] = 1;
            else
                res.states[r.state] += 1;
        });
        return res;
    }
};

/**
 * <odoc>
 * <key>ow.metrics.add(aName, aFunction)</key>
 * Adds aName metric whose values are a map returned by aFunction.
 * </odoc>
 */
OpenWrap.metrics.prototype.add = function(aName, aFn) {
    if (isUnDef(ow.metrics.__m[aName])) {
        ow.metrics.__m[aName] = aFn;
    }
};

/**
 * <odoc>
 * <key>ow.metrics.getSome(anArrayOfNames) : Map</key>
 * Returns just the metrics in the provided anArrayOfNames.
 * </odoc>
 */
OpenWrap.metrics.prototype.getSome = function(aArray) {
    var r = {}, errors = [];
    aArray.map(f => {
        try {
            r[f] = ow.metrics.__m[f]();
        } catch(e) {
            r[f] = "error";
            logErr(e);
        }
    });
    return r;
};

/**
 * <odoc>
 * <key>ow.metrics.getAll() : Map</key>
 * Returns a map with all registered metrics.
 * </odoc>
 */
OpenWrap.metrics.prototype.getAll = function() {
    return ow.metrics.getSome(Object.keys(ow.metrics.__m));
};

/**
 * <odoc>
 * <key>ow.metrics.startCollecting(aChName, aPeriod, some)</key>
 * Starts collecting metrics on aChName (defaults to '__metrics') every aPeriod ms (defaults to 1000ms) optionally just some (array) metrics.
 * </odoc>
 */
OpenWrap.metrics.prototype.startCollecting = function(aChName, aPeriod, aSome) {
    var createCh = isUnDef(aChName) || $ch().list().indexOf(aChName) < 0;

    if (isUnDef(ow.metrics.__fnMetrics)) ow.metrics.__fnMetrics = {};

    aChName = _$(aChName).isString().default("__metrics");
    aPeriod = _$(aPeriod).isNumber().default(1000);

    if (createCh) $ch(aChName).create(1, "mvs", { file: "metrics.db" });

    if (isUnDef(ow.metrics.__ch)) ow.metrics.__ch = [];
    ow.metrics.__ch.push(aChName);

    if (isUnDef(ow.metrics.__t)) {
        plugin("Threads");
        ow.metrics.__t = new Threads();
        ow.metrics.__t.addScheduleThreadWithFixedDelay(function() {
            var k = { t: now() };
            var v;
            if(isArray(aSome))
               v = merge(k, ow.metrics.getSome(aSome));
            else
	       v = merge(k, ow.metrics.getAll());

            ow.metrics.__ch.map(ch => {
                $ch(ch).set(k, v);
            });
        }, aPeriod);
        ow.metrics.__t.startNoWait();
    }
};

/**
 * <odoc>
 * <key>ow.metrics.stopCollecting(aChName)</key>
 * Stops collecting metrics on aChName (defaults to '__metrics')
 * </odoc>
 */
OpenWrap.metrics.prototype.stopCollecting = function(aChName) {
    aChName = _$(aChName).isString().default("__metrics");
    if (ow.metrics.__ch.indexOf(aChName) < 0) throw "Not collecting into " + aChName;

    ow.metrics.__ch = deleteFromArray(ow.metrics.__ch, ow.metrics.__ch.indexOf(aChName));
    if (isDef(ow.metrics.__t) && ow.metrics.__ch.length <= 0) {
        ow.metrics.__t.stop(true);
        ow.metrics.__t = void 0;
    }
};

/**
 * <odoc>
 * <key>ow.metrics.collectMetrics4Fn(aName, aFn)</key>
 * Adds extra code to an existing aFn to collect functions metrics under the name aName. If the same aName and aFn
 * has been already executed before it will throw an exception "Already collecting for the provided function."
 * </odoc>
 */
OpenWrap.metrics.prototype.collectMetrics4Fn = function(aName, aFn) {
    _$(aName, "name").isString().$_();
    _$(aFn, "fn").isString().$_();

    var aBody = eval(aFn).toString();
    if (isUnDef(ow.metrics.__fnMetrics)) ow.metrics.__fnMetrics = {};
    if (isDef(ow.metrics.__fnMetrics[aName])) throw "Already collecting for the provided function.";

    aBody = aBody.split(/\n/);
    var newBody = [];
    ow.metrics.__fnMetrics[aName] = {
        start: 0,
        err  : 0,
        end  : 0,
        sum  : 0,
        avg  : 0
    };

    for(var ii in aBody) {
        if (ii == 2 || ii == aBody.length -2) {
            if (ii == 2) {
                newBody.push("ow.metrics.__fnMetrics[\"" + aName + "\"].start += 1; var __resMetricsProxyE, __resMetricsProxyR, __resMetricsProxyI = now(); try { __resMetricsProxyR = (function(){");
            } else {
                newBody.push("})(); ow.metrics.__fnMetrics[\"" + aName + "\"].end += 1; ow.metrics.__fnMetrics[\"" + aName + "\"].sum += (now() - __resMetricsProxyI); return __resMetricsProxyR; } catch(__fnMetricsProxyE) { ow.metrics.__fnMetrics[\"" + aName + "\"].err += 1; throw __fnMetricsProxyE; }");
            }
        }
        newBody.push(aBody[ii]);
    }
    eval(aFn + " = " + newBody.join("\n"));
};

/**
 * <odoc>
 * <key>ow.metrics.collectMetrics(aName, aFunction) : Object</key>
 * Executes aFunction while collecting functions metrics under the name aName. Returns whatever the function returns
 * or throws any exception.
 * </odoc>
 */
OpenWrap.metrics.prototype.collectMetrics = function(aName, aFunction) {
    _$(aName, "name").isString().$_();
    _$(aFunction).isFunction().$_();

    if (isUnDef(ow.metrics.__fnMetrics)) ow.metrics.__fnMetrics = {};
    if (isUnDef(ow.metrics.__fnMetrics[aName])) {
        ow.metrics.__fnMetrics[aName] = {
            start: 0,
            err  : 0,
            end  : 0,
            sum  : 0
        };
    }

    var __resMetricsProxyE, __resMetricsProxyR, __resMetricsProxyI = now(), res;
    try {
        ow.metrics.__fnMetrics[aName].start += 1;
        __resMetricsProxyR = aFunction();
        ow.metrics.__fnMetrics[aName].end += 1;
        return __resMetricsProxyR;
    } catch(__resMetricsProxyE) {
        ow.metrics.__fnMetrics[aName].err += 1;
        throw __resMetricsProxyE;
    }
}
<|endoftext|>