<commit_before>// ==UserScript==
// @id             iitc-plugin-cross-links@mcben
// @name           IITC plugin: cross links
// @category       Layer
// @version        1.0.@@DATETIMEVERSION@@
// @namespace      https://github.com/jonatkins/ingress-intel-total-conversion
// @updateURL      @@UPDATEURL@@
// @downloadURL    @@DOWNLOADURL@@
// @description    [McBen] requires drawtool! coloring of link collision
// @include        https://www.ingress.com/intel*
// @include        http://www.ingress.com/intel*
// @match          https://www.ingress.com/intel*
// @match          http://www.ingress.com/intel*
// @grant          none
// ==/UserScript==

@@PLUGINSTART@@

// PLUGIN START ////////////////////////////////////////////////////////

window.plugin.crossLinks = function() {};

/* Great Circle Arc Intersection
    Conecpt in short:
       - build a plane of each arc (p1,p2,center)
       - find intersection line and intersection points on sphere
       - check if a point are on both arcs
    see: http://geospatialmethods.org/spheres/GCAIntersect.html
*/
var PI = Math.PI;
var radians = PI / 180;
var near_0 = 1e-6;

function greatCircleArcIntersect(a0,a1,b0,b1) {

    // Order points
    if (a1.lat < a0.lat) { var t=a1;a1=a0;a0=t;}
    if (b1.lat < b0.lat) { var t=b1;b1=b0;b0=t;}

    var λ0 = a0.lat,
        λ1 = a1.lat,
        λ2 = b0.lat,
        λ3 = b1.lat,
        δλ0 = λ1 - λ0,
        δλ1 = λ3 - λ2,
        sλ0 = δλ0 > 180,
        sλ1 = δλ1 > 180,
        φ0 = a0.lng * radians,
        φ1 = a1.lng * radians,
        φ2 = b0.lng * radians,
        φ3 = b1.lng * radians,
        t;

    // Check if longitude ranges overlap.
    // TODO handle antimeridian crossings.
    if (!sλ0 && !sλ1 && (λ0 > λ3 || λ2 > λ1)) return;

    // Check for polar endpoints.
    if (Math.abs(Math.abs(φ0) - PI / 2) < near_0) λ0 = λ1, δλ0 = 0, sλ0 = false;
    if (Math.abs(Math.abs(φ1) - PI / 2) < near_0) λ1 = λ0, δλ0 = 0, sλ0 = false;
    if (Math.abs(Math.abs(φ2) - PI / 2) < near_0) λ2 = λ3, δλ1 = 0, sλ1 = false;
    if (Math.abs(Math.abs(φ3) - PI / 2) < near_0) λ3 = λ2, δλ1 = 0, sλ1 = false;

    // Check for arcs along meridians.
    var m0 = δλ0 < near_0 || Math.abs(δλ0 - 180) < near_0,
        m1 = δλ1 < near_0 || Math.abs(δλ1 - 180) < near_0;

    λ0 *= radians, λ1 *= radians, λ2 *= radians, λ3 *= radians;

    // Intersect two great circles and check the two intersection points against
    // the longitude ranges.  The intersection points are simply the cross
    // product of the great-circle normals ±n1⨯n2.

    // First plane.
    var cosφ,
        x0 = (cosφ = Math.cos(φ0)) * Math.cos(λ0),
        y0 = cosφ * Math.sin(λ0),
        z0 = Math.sin(φ0),
        x1 = (cosφ = Math.cos(φ1)) * Math.cos(λ1),
        y1 = cosφ * Math.sin(λ1),
        z1 = Math.sin(φ1),
        n0x = y0 * z1 - z0 * y1,
        n0y = z0 * x1 - x0 * z1,
        n0z = x0 * y1 - y0 * x1,
        m = length(n0x, n0y, n0z);

    n0x /= m, n0y /= m, n0z /= m;

    // Second plane.
    var x2 = (cosφ = Math.cos(φ2)) * Math.cos(λ2),
        y2 = cosφ * Math.sin(λ2),
        z2 = Math.sin(φ2),
        x3 = (cosφ = Math.cos(φ3)) * Math.cos(λ3),
        y3 = cosφ * Math.sin(λ3),
        z3 = Math.sin(φ3),
        n1x = y2 * z3 - z2 * y3,
        n1y = z2 * x3 - x2 * z3,
        n1z = x2 * y3 - y2 * x3,
        m = length(n1x, n1y, n1z);

    n1x /= m, n1y /= m, n1z /= m;

    var Nx = n0y * n1z - n0z * n1y,
        Ny = n0z * n1x - n0x * n1z,
        Nz = n0x * n1y - n0y * n1x;

    if (length(Nx, Ny, Nz) < near_0) return;

    var λ = Math.atan2(Ny, Nx);
    if ( (sλ0 ^ (λ0 <= λ && λ <= λ1) || m0 && Math.abs(λ - λ0) < near_0) && (sλ1 ^ (λ2 <= λ && λ <= λ3) || m1 && Math.abs(λ - λ2) < near_0) || (Nz = -Nz,
         (sλ0 ^ (λ0 <= (λ = (λ + 2 * PI) % (2 * PI) - PI) && λ <= λ1) || m0 && Math.abs(λ - λ0) < near_0) && (sλ1 ^ (λ2 <= λ && λ <= λ3) || m1 && Math.abs(λ - λ2) < near_0))) {

        var φ = Math.asin(Nz / length(Nx, Ny, Nz));

        if (m0 || m1) {
          if (m1) φ0 = φ2, φ1 = φ3, λ0 = λ2, λ1 = λ3, δλ0 = δλ1;

          if (δλ0 > near_0)
            return (φ0 + φ1 > 0 ^ φ < (Math.abs(λ - λ0) < near_0 ? φ0 : φ1)) ? [λ / radians, φ / radians] : null;

          // Ensure φ0 ≤ φ1.
          if (φ1 < φ0) t = φ0, φ0 = φ1, φ1 = t;
          return (Math.abs(λ - (m0 ? λ0 : λ2)) < near_0 && φ0 <= φ && φ <= φ1) ? [λ / radians, φ / radians] : null;
        }

        return [λ / radians, φ / radians];
    }
}

function length(x, y, z) {
  return Math.sqrt(x * x + y * y + z * z);
}

/*
 smallCircleIntersect
 idea:
  - build the plane of the small circle: normalvector (center) and p=center+radian // E:n1*x+n2*y+n3*z=n1*p1+n2*p2+n3*p3
  - calc distance to both points // d=(n1*x+n2*y+n3*z- (n1*p1+n2*p2+n3*p3)) / length(n)
  - both >0 = inside ; one >0 = collision
*/

window.plugin.crossLinks.testPolyLine = function (polyline, link,closed) {

    var a = link.getLatLngs();
    var b = polyline.getLatLngs();

    for (var i=0;i<b.length-1;++i) {
        if (greatCircleArcIntersect(a[0],a[1],b[i],b[i+1])) return true;
    }

    if (closed) {
        if (greatCircleArcIntersect(a[0],a[1],b[b.length],b[0])) return true;
    }

    return false;
}

window.plugin.crossLinks.onLinkAdded = function (data) {
  	if (window.plugin.crossLinks.disabled) return;

    plugin.crossLinks.testLink(data.link);
}

window.plugin.crossLinks.checkAllLinks = function() {
    if (window.plugin.crossLinks.disabled) return;

  	console.debug("Cross-Links: checking all links");
	plugin.crossLinks.linkLayer.clearLayers();

  	$.each(window.links, function(guid, link) {
        plugin.crossLinks.testLink(link);
    });
}

window.plugin.crossLinks.testLink = function (link) {
    window.plugin.drawTools.drawnItems.eachLayer( function(layer) {
       if (layer instanceof L.GeodesicPolygon) {
           if (window.plugin.crossLinks.testPolyLine(layer, link,true)) {
               plugin.crossLinks.showLink(link);
           }
        } else if (layer instanceof L.GeodesicPolyline) {
            if (window.plugin.crossLinks.testPolyLine(layer, link)) {
                plugin.crossLinks.showLink(link);
            }
        }
    });
}

window.plugin.crossLinks.showLink = function(link) {

  var poly = L.geodesicPolyline(link.getLatLngs(), {
    color: '#f11',
    opacity: 0.7,
    weight: 4,
    clickable: false,
  });

  poly.addTo(plugin.crossLinks.linkLayer);
}

window.plugin.crossLinks.onMapDataRefreshEnd = function () {
    if (window.plugin.crossLinks.reorderLinkLayer) {
        window.plugin.crossLinks.linkLayer.bringToFront();
        delete window.plugin.crossLinks.reorderLinkLayer;
    }
}

window.plugin.crossLinks.createLayer = function() {
    window.plugin.crossLinks.linkLayer = new L.FeatureGroup();
  	window.addLayerGroup('Cross Links', window.plugin.crossLinks.linkLayer, true);
	window.plugin.crossLinks.reorderLinkLayer = true;

  	map.on('layeradd', function(obj) {
	    if(obj.layer === window.plugin.crossLinks.linkLayer) {
    	    window.plugin.crossLinks.disabled = undefined;
        	window.plugin.crossLinks.checkAllLinks();
    	}
  	});
  	map.on('layerremove', function(obj) {
    	if(obj.layer === window.plugin.crossLinks.linkLayer) {
        	window.plugin.crossLinks.disabled = true;
    	}
  	});
}

var setup = function() {
    if (window.plugin.drawTools === undefined) {
       alert("'Cross-Links' requires 'draw-tools'");
       return;
    }

    window.plugin.crossLinks.createLayer();

    // events
    map.on('draw:created', function(e) {window.plugin.crossLinks.checkAllLinks(); });
    map.on('draw:deleted', function(e) {window.plugin.crossLinks.checkAllLinks(); });
    map.on('draw:edited', function(e) {window.plugin.crossLinks.checkAllLinks();});

    window.addHook('linkAdded', window.plugin.crossLinks.onLinkAdded);
    window.addHook('mapDataRefreshEnd', window.plugin.crossLinks.onMapDataRefreshEnd);
}

// PLUGIN END //////////////////////////////////////////////////////////

@@PLUGINEND@@
<commit_msg>cross-links: process collision links only once (and delete if needed)<commit_after>// ==UserScript==
// @id             iitc-plugin-cross-links@mcben
// @name           IITC plugin: cross links
// @category       Layer
// @version        1.0.@@DATETIMEVERSION@@
// @namespace      https://github.com/jonatkins/ingress-intel-total-conversion
// @updateURL      @@UPDATEURL@@
// @downloadURL    @@DOWNLOADURL@@
// @description    [McBen] requires drawtool! coloring of link collision
// @include        https://www.ingress.com/intel*
// @include        http://www.ingress.com/intel*
// @match          https://www.ingress.com/intel*
// @match          http://www.ingress.com/intel*
// @grant          none
// ==/UserScript==

@@PLUGINSTART@@

// PLUGIN START ////////////////////////////////////////////////////////

window.plugin.crossLinks = function() {};

/* Great Circle Arc Intersection
    Conecpt in short:
       - build a plane of each arc (p1,p2,center)
       - find intersection line and intersection points on sphere
       - check if a point are on both arcs
    see: http://geospatialmethods.org/spheres/GCAIntersect.html
*/
var PI = Math.PI;
var radians = PI / 180;
var near_0 = 1e-6;

function greatCircleArcIntersect(a0,a1,b0,b1) {

    // Order points
    if (a1.lat < a0.lat) { var t=a1;a1=a0;a0=t;}
    if (b1.lat < b0.lat) { var t=b1;b1=b0;b0=t;}

    var λ0 = a0.lat,
        λ1 = a1.lat,
        λ2 = b0.lat,
        λ3 = b1.lat,
        δλ0 = λ1 - λ0,
        δλ1 = λ3 - λ2,
        sλ0 = δλ0 > 180,
        sλ1 = δλ1 > 180,
        φ0 = a0.lng * radians,
        φ1 = a1.lng * radians,
        φ2 = b0.lng * radians,
        φ3 = b1.lng * radians,
        t;

    // Check if longitude ranges overlap.
    // TODO handle antimeridian crossings.
    if (!sλ0 && !sλ1 && (λ0 > λ3 || λ2 > λ1)) return;

    // Check for polar endpoints.
    if (Math.abs(Math.abs(φ0) - PI / 2) < near_0) λ0 = λ1, δλ0 = 0, sλ0 = false;
    if (Math.abs(Math.abs(φ1) - PI / 2) < near_0) λ1 = λ0, δλ0 = 0, sλ0 = false;
    if (Math.abs(Math.abs(φ2) - PI / 2) < near_0) λ2 = λ3, δλ1 = 0, sλ1 = false;
    if (Math.abs(Math.abs(φ3) - PI / 2) < near_0) λ3 = λ2, δλ1 = 0, sλ1 = false;

    // Check for arcs along meridians.
    var m0 = δλ0 < near_0 || Math.abs(δλ0 - 180) < near_0,
        m1 = δλ1 < near_0 || Math.abs(δλ1 - 180) < near_0;

    λ0 *= radians, λ1 *= radians, λ2 *= radians, λ3 *= radians;

    // Intersect two great circles and check the two intersection points against
    // the longitude ranges.  The intersection points are simply the cross
    // product of the great-circle normals ±n1⨯n2.

    // First plane.
    var cosφ,
        x0 = (cosφ = Math.cos(φ0)) * Math.cos(λ0),
        y0 = cosφ * Math.sin(λ0),
        z0 = Math.sin(φ0),
        x1 = (cosφ = Math.cos(φ1)) * Math.cos(λ1),
        y1 = cosφ * Math.sin(λ1),
        z1 = Math.sin(φ1),
        n0x = y0 * z1 - z0 * y1,
        n0y = z0 * x1 - x0 * z1,
        n0z = x0 * y1 - y0 * x1,
        m = length(n0x, n0y, n0z);

    n0x /= m, n0y /= m, n0z /= m;

    // Second plane.
    var x2 = (cosφ = Math.cos(φ2)) * Math.cos(λ2),
        y2 = cosφ * Math.sin(λ2),
        z2 = Math.sin(φ2),
        x3 = (cosφ = Math.cos(φ3)) * Math.cos(λ3),
        y3 = cosφ * Math.sin(λ3),
        z3 = Math.sin(φ3),
        n1x = y2 * z3 - z2 * y3,
        n1y = z2 * x3 - x2 * z3,
        n1z = x2 * y3 - y2 * x3,
        m = length(n1x, n1y, n1z);

    n1x /= m, n1y /= m, n1z /= m;

    var Nx = n0y * n1z - n0z * n1y,
        Ny = n0z * n1x - n0x * n1z,
        Nz = n0x * n1y - n0y * n1x;

    if (length(Nx, Ny, Nz) < near_0) return;

    var λ = Math.atan2(Ny, Nx);
    if ( (sλ0 ^ (λ0 <= λ && λ <= λ1) || m0 && Math.abs(λ - λ0) < near_0) && (sλ1 ^ (λ2 <= λ && λ <= λ3) || m1 && Math.abs(λ - λ2) < near_0) || (Nz = -Nz,
         (sλ0 ^ (λ0 <= (λ = (λ + 2 * PI) % (2 * PI) - PI) && λ <= λ1) || m0 && Math.abs(λ - λ0) < near_0) && (sλ1 ^ (λ2 <= λ && λ <= λ3) || m1 && Math.abs(λ - λ2) < near_0))) {

        var φ = Math.asin(Nz / length(Nx, Ny, Nz));

        if (m0 || m1) {
          if (m1) φ0 = φ2, φ1 = φ3, λ0 = λ2, λ1 = λ3, δλ0 = δλ1;

          if (δλ0 > near_0)
            return (φ0 + φ1 > 0 ^ φ < (Math.abs(λ - λ0) < near_0 ? φ0 : φ1)) ? [λ / radians, φ / radians] : null;

          // Ensure φ0 ≤ φ1.
          if (φ1 < φ0) t = φ0, φ0 = φ1, φ1 = t;
          return (Math.abs(λ - (m0 ? λ0 : λ2)) < near_0 && φ0 <= φ && φ <= φ1) ? [λ / radians, φ / radians] : null;
        }

        return [λ / radians, φ / radians];
    }
}

function length(x, y, z) {
  return Math.sqrt(x * x + y * y + z * z);
}

/*
 smallCircleIntersect
 idea:
  - build the plane of the small circle: normalvector (center) and p=center+radian // E:n1*x+n2*y+n3*z=n1*p1+n2*p2+n3*p3
  - calc distance to both points // d=(n1*x+n2*y+n3*z- (n1*p1+n2*p2+n3*p3)) / length(n)
  - both >0 = inside ; one >0 = collision
*/

window.plugin.crossLinks.testPolyLine = function (polyline, link,closed) {

    var a = link.getLatLngs();
    var b = polyline.getLatLngs();

    for (var i=0;i<b.length-1;++i) {
        if (greatCircleArcIntersect(a[0],a[1],b[i],b[i+1])) return true;
    }

    if (closed) {
        if (greatCircleArcIntersect(a[0],a[1],b[b.length],b[0])) return true;
    }

    return false;
}

window.plugin.crossLinks.onLinkAdded = function (data) {
  	if (window.plugin.crossLinks.disabled) return;

    plugin.crossLinks.testLink(data.link);
}

window.plugin.crossLinks.checkAllLinks = function() {
    if (window.plugin.crossLinks.disabled) return;

  	console.debug("Cross-Links: checking all links");
	plugin.crossLinks.linkLayer.clearLayers();
    plugin.crossLinks.linkLayerGuids = {};

  	$.each(window.links, function(guid, link) {
        plugin.crossLinks.testLink(link);
    });
}

window.plugin.crossLinks.testLink = function (link) {
    if (plugin.crossLinks.linkLayerGuids[link.options.guid]) return;

    for (var i in plugin.drawTools.drawnItems._layers) { // leaflet don't support breaking out of the loop
       var layer = plugin.drawTools.drawnItems._layers[i];
       if (layer instanceof L.GeodesicPolygon) {
           if (plugin.crossLinks.testPolyLine(layer, link,true)) {
               plugin.crossLinks.showLink(link);
               break;
           }
        } else if (layer instanceof L.GeodesicPolyline) {
            if (plugin.crossLinks.testPolyLine(layer, link)) {
                plugin.crossLinks.showLink(link);
                break;
            }
        }
    };
}

window.plugin.crossLinks.showLink = function(link) {

    var poly = L.geodesicPolyline(link.getLatLngs(), {
       color: '#f11',
       opacity: 0.7,
       weight: 4,
       clickable: false,

       guid: link.options.guid
    });

    poly.addTo(plugin.crossLinks.linkLayer);
    plugin.crossLinks.linkLayerGuids[link.options.guid]=poly;
}

window.plugin.crossLinks.onMapDataRefreshEnd = function () {
    if (window.plugin.crossLinks.reorderLinkLayer) {
        window.plugin.crossLinks.linkLayer.bringToFront();
        delete window.plugin.crossLinks.reorderLinkLayer;
    }

    window.plugin.crossLinks.testForDeletedLinks();
}

window.plugin.crossLinks.testForDeletedLinks = function () {
    window.plugin.crossLinks.linkLayer.eachLayer( function(layer) {
        var guid = layer.options.guid;
        if (!this.seenLinksGuid[guid]) {
            console.log("link removed");
            plugin.crossLinks.linkLayer.removeLayer(layer);
            delete plugin.crossLinks.linkLayerGuids[guid];
        }
    });
}

window.plugin.crossLinks.createLayer = function() {
    window.plugin.crossLinks.linkLayer = new L.FeatureGroup();
    window.plugin.crossLinks.linkLayerGuids={};
  	window.addLayerGroup('Cross Links', window.plugin.crossLinks.linkLayer, true);
	window.plugin.crossLinks.reorderLinkLayer = true;

  	map.on('layeradd', function(obj) {
	    if(obj.layer === window.plugin.crossLinks.linkLayer) {
    	    delete window.plugin.crossLinks.disabled;
        	window.plugin.crossLinks.checkAllLinks();
    	}
  	});
  	map.on('layerremove', function(obj) {
    	if(obj.layer === window.plugin.crossLinks.linkLayer) {
        	window.plugin.crossLinks.disabled = true;
    	}
  	});
}

var setup = function() {
    if (window.plugin.drawTools === undefined) {
       alert("'Cross-Links' requires 'draw-tools'");
       return;
    }

    window.plugin.crossLinks.createLayer();

    // events
    map.on('draw:created', function(e) {window.plugin.crossLinks.checkAllLinks(); });
    map.on('draw:deleted', function(e) {window.plugin.crossLinks.checkAllLinks(); });
    map.on('draw:edited', function(e) {window.plugin.crossLinks.checkAllLinks();});

    window.addHook('linkAdded', window.plugin.crossLinks.onLinkAdded);
    window.addHook('mapDataRefreshEnd', window.plugin.crossLinks.onMapDataRefreshEnd);
}

// PLUGIN END //////////////////////////////////////////////////////////

@@PLUGINEND@@
<|endoftext|>


<commit_before>(function($){
 $.fn.airtimeShowSchedule = function(options) {

    var defaults = {
        updatePeriod: 20, //seconds
        sourceDomain: "http://localhost/", //where to get show status from
        text: {onAirToday:"On air today"}
    };
    var options = $.extend(defaults, options);
    options.sourceDomain = addEndingBackslash(options.sourceDomain);

    return this.each(function() {
        var obj = $(this);
        var sd;

        getServerData();

        function updateWidget(){
            var currentShow = sd.getCurrentShow();
            var nextShows = sd.getNextShows();

            var currentShowName = "";
            var nextShowName = ""

            if (currentShow.length > 0){
                currentShowName = currentShow[0].getName();
            }

            if (nextShows.length > 0){
                nextShowName = nextShows[0].getName();
            }

            tableString = "";
            tableString += "
"+options.text.onAirToday+"
";
            tableString += "
"+
                "
";

            var shows=currentShow.concat(nextShows);

            obj.empty();
            for (var i=0; i<shows.length; i++){
                tableString +=
                "
" +
                ""+shows[i].getRange()+"	" +
                ""+shows[i].getName()+"	" +
                "
";
            }

            tableString += "

";

            obj.append(tableString);
        }

        function processData(data){
            sd = new ScheduleData(data);
            updateWidget();
        }

        function airtimeScheduleJsonpError(jqXHR, textStatus, errorThrown){
        }

        function getServerData(){
            $.ajax({ url: options.sourceDomain + "api/live-info/", dataType:"jsonp", success:function(data){
                        processData(data);
                  }, error:airtimeScheduleJsonpError});
            setTimeout(getServerData, options.updatePeriod*1000);
        }
    });
 };
})(jQuery);

(function($){
 $.fn.airtimeLiveInfo = function(options) {

    var defaults = {
        updatePeriod: 5, //seconds
        sourceDomain: "http://localhost/", //where to get show status from
        text: {onAirNow:"On Air Now", offline:"Offline", current:"Current", next:"Next"}
    };
    var options = $.extend(defaults, options);
    options.sourceDomain = addEndingBackslash(options.sourceDomain);

    return this.each(function() {
        var obj = $(this);
        var sd;
        getServerData();

        function updateWidget(){
            var currentShow = sd.getCurrentShow();
            var nextShows = sd.getNextShows();

            var showStatus = options.text.offline;
            var currentShowName = "";
            var timeElapsed = "";
            var timeRemaining = "";

            var nextShowName = "";
            var nextShowRange = "";

            if (currentShow.length > 0){
                showStatus = options.text.onAirNow;
                currentShowName = currentShow[0].getName();

                timeElapsed = sd.getShowTimeElapsed(currentShow[0]);
                timeRemaining = sd.getShowTimeRemaining(currentShow[0]);
            }

            if (nextShows.length > 0){
                nextShowName = nextShows[0].getName();
                nextShowRange = nextShows[0].getRange();
            }

            obj.empty();
            obj.append("
"+showStatus+" >>
");
            obj.append("
" +
                "
"+options.text.current+": "+currentShowName+
                ""+timeElapsed+"" +
                ""+timeRemaining+""+
                "
" +
                "
"+options.text.next+": "+nextShowName+""+nextShowRange+"
" +
                "
");

            //refresh the UI to update the elapsed/remaining time
            setTimeout(updateWidget, 1000);
        }

        function processData(data){
            sd = new ScheduleData(data);
            updateWidget();
        }

        function airtimeScheduleJsonpError(jqXHR, textStatus, errorThrown){
        }

        function getServerData(){
            $.ajax({ url: options.sourceDomain + "api/live-info/", dataType:"jsonp", success:function(data){
                        processData(data);
                  }, error:airtimeScheduleJsonpError});
            setTimeout(getServerData, options.updatePeriod*1000);
        }
    });
 };
})(jQuery);

(function($){
 $.fn.airtimeWeekSchedule = function(options) {

    var defaults = {
        sourceDomain: "http://localhost/", //where to get show status from
        updatePeriod: 600,
        dowText: {monday:"Monday", tuesday:"Tuesday", wednesday:"Wednesday", thursday:"Thursday", friday:"Friday", saturday:"Saturday", sunday:"Sunday"},
        miscText: {time:"Time", programName:"Program Name", details:"Details", readMore:"Read More"}
    };
    var options = $.extend(defaults, options);
    options.sourceDomain = addEndingBackslash(options.sourceDomain);

    return this.each(function() {
        var obj = $(this);
        obj.empty();

        obj.attr("class", "ui-tabs");

        var dow = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];

        var date = new Date();
        //subtract 1 because javascript date function returns
        //sunday as 0 based, but we want Monday to be 0-based.
        var todayInt = (date.getDay()-1);
        if (todayInt < 0)
            todayInt += 7;

        var html = '
';
        for (var i=0; i<dow.length; i++){
            html += '
'+options.dowText[dow[i]]+'
';
        }
        html += '
';

        for (var i=0; i<dow.length; i++){
            html += '

'
        }
        obj.append(html);
        getServerData();

        function updateWidget(data){
            for (var i=0; i<dow.length; i++){
                var html =
                  '
'+
                    '<colgroup>'+
                      '<col width="150" />'+
                      '<col width="350" />'+
                      '<col width="240" />'+
                    '</colgroup>'+
                    '
'+
                      '
'+
                        ''+options.miscText.time+'	'+
                        ''+options.miscText.programName+'	'+
                        ''+options.miscText.details+'	'+
                      '
'+
                    '
'+
                    '
'+
                      '
'+
                        '	'+
                      '
'+
                    '
'+
                    '
';
                var daySchedule = data[dow[i]];
                for (var j=0; j<daySchedule.length; j++){
                    var url = daySchedule[j].url;
                    html +=
                      '
'+
                        ''+getTime(daySchedule[j].show_starts)+ " - " + getTime(daySchedule[j].show_ends)+'	'+
                        ''+
                          '
'+daySchedule[j].show_name+'
'+
                        '	'+
                        ''+
                          '
'+
                            '
'+(url.length > 0 ? ''+options.miscText.readMore+'':'')+'
'+
                          '
'+
                        '	'+
                      '
';
                }
                html +=
                    '
'+
                  '
';

                $("#"+dow[i]).empty();
                $("#"+dow[i]).append(html);
            }
        }

        function processData(data){
            updateWidget(data);
        }

        function airtimeScheduleJsonpError(jqXHR, textStatus, errorThrown){
        }

        function getServerData(){
            $.ajax({ url: options.sourceDomain + "api/week-info/", dataType:"jsonp", success:function(data){
                        processData(data);
                  }, error:airtimeScheduleJsonpError});
            setTimeout(getServerData, options.updatePeriod*1000);
        }
    });
 };
})(jQuery);

function addEndingBackslash(str){
    if (str.charAt(str.length-1) != '/')
        return str+'/';
    else return str;
}

/* ScheduleData class BEGIN */
function ScheduleData(data){
    this.data = data;
    this.estimatedSchedulePosixTime;

    this.currentShow = new Array();
    for (var i=0; i< data.currentShow.length; i++){
        this.currentShow[i] = new Show(data.currentShow[i]);
    }

    this.nextShows = new Array();
    for (var i=0; i< data.nextShow.length; i++){
        this.nextShows[i] = new Show(data.nextShow[i]);
    }

    this.schedulePosixTime = convertDateToPosixTime(data.schedulerTime);
    this.schedulePosixTime += parseInt(data.timezoneOffset)*1000;
    var date = new Date();
    this.localRemoteTimeOffset = date.getTime() - this.schedulePosixTime;
}

ScheduleData.prototype.secondsTimer = function(){
    var date = new Date();
    this.estimatedSchedulePosixTime = date.getTime() - this.localRemoteTimeOffset;
}

ScheduleData.prototype.getCurrentShow = function(){
    return this.currentShow;
}

ScheduleData.prototype.getNextShows = function() {
    return this.nextShows;
}

ScheduleData.prototype.getShowTimeElapsed = function(show) {
    this.secondsTimer();

    var showStart = convertDateToPosixTime(show.getStartTimestamp());
    return convertToHHMMSS(this.estimatedSchedulePosixTime - showStart);
};

ScheduleData.prototype.getShowTimeRemaining = function(show) {
    this.secondsTimer();

    var showEnd = convertDateToPosixTime(show.getEndTimestamp());
    return convertToHHMMSS(showEnd - this.estimatedSchedulePosixTime);
};
/* ScheduleData class END */

/* Show class BEGIN */
function Show(showData){
    this.showData = showData;
}

Show.prototype.getName = function(){
    return this.showData.name;
}
Show.prototype.getRange = function(){
    return getTime(this.showData.start_timestamp) + " - " + getTime(this.showData.end_timestamp);
}
Show.prototype.getStartTimestamp = function(){
    return this.showData.start_timestamp;
}
Show.prototype.getEndTimestamp = function(){
    return this.showData.end_timestamp;
}
/* Show class END */

function getTime(timestamp) {
    var time = timestamp.split(" ")[1].split(":");
    return time[0] + ":" + time[1];
};

/* Takes an input parameter of milliseconds and converts these into
 * the format HH:MM:SS */
function convertToHHMMSS(timeInMS){
	var time = parseInt(timeInMS);

	var hours = parseInt(time / 3600000);
	time -= 3600000*hours;

	var minutes = parseInt(time / 60000);
	time -= 60000*minutes;

	var seconds = parseInt(time / 1000);

	hours = hours.toString();
	minutes = minutes.toString();
	seconds = seconds.toString();

	if (hours.length == 1)
		hours = "0" + hours;
	if (minutes.length == 1)
		minutes = "0" + minutes;
	if (seconds.length == 1)
		seconds = "0" + seconds;
	if (hours == "00")
		return minutes + ":" + seconds;
	else
		return hours + ":" + minutes + ":" + seconds;
}

/* Takes in a string of format similar to 2011-02-07 02:59:57,
 * and converts this to epoch/posix time. */
function convertDateToPosixTime(s){
    var datetime = s.split(" ");

    var date = datetime[0].split("-");
    var time = datetime[1].split(":");

	var year = date[0];
	var month = date[1];
	var day = date[2];
	var hour = time[0];
	var minute = time[1];
    var sec = 0;
    var msec = 0;

    if (time[2].indexOf(".") != -1){
        var temp = time[2].split(".");
        sec = temp[0];
        msec = temp[1];
    } else
        sec = time[2];

	return Date.UTC(year, month-1, day, hour, minute, sec, msec);
}
<commit_msg>CC-2151: Now playing "On Air Now" widget not using URLs -done<commit_after>(function($){
 $.fn.airtimeShowSchedule = function(options) {

    var defaults = {
        updatePeriod: 20, //seconds
        sourceDomain: "http://localhost/", //where to get show status from
        text: {onAirToday:"On air today"}
    };
    var options = $.extend(defaults, options);
    options.sourceDomain = addEndingBackslash(options.sourceDomain);

    return this.each(function() {
        var obj = $(this);
        var sd;

        getServerData();

        function updateWidget(){
            var currentShow = sd.getCurrentShow();
            var nextShows = sd.getNextShows();

            var currentShowName = "";
            var nextShowName = ""

            if (currentShow.length > 0){
                currentShowName = currentShow[0].getName();
            }

            if (nextShows.length > 0){
                nextShowName = nextShows[0].getName();
            }

            tableString = "";
            tableString += "
"+options.text.onAirToday+"
";
            tableString += "
"+
                "
";

            var shows=currentShow.concat(nextShows);

            obj.empty();
            for (var i=0; i<shows.length; i++){
                tableString +=
                "
" +
                ""+shows[i].getRange()+"	";

                var url = shows[i].getURL();

                if (url.length > 0)
                    tableString += ""+shows[i].getName()+"
";
                else
                    tableString += ""+shows[i].getName()+"
";

            }

            tableString += "

";

            obj.append(tableString);
        }

        function processData(data){
            sd = new ScheduleData(data);
            updateWidget();
        }

        function airtimeScheduleJsonpError(jqXHR, textStatus, errorThrown){
        }

        function getServerData(){
            $.ajax({ url: options.sourceDomain + "api/live-info/", dataType:"jsonp", success:function(data){
                        processData(data);
                  }, error:airtimeScheduleJsonpError});
            setTimeout(getServerData, options.updatePeriod*1000);
        }
    });
 };
})(jQuery);

(function($){
 $.fn.airtimeLiveInfo = function(options) {

    var defaults = {
        updatePeriod: 5, //seconds
        sourceDomain: "http://localhost/", //where to get show status from
        text: {onAirNow:"On Air Now", offline:"Offline", current:"Current", next:"Next"}
    };
    var options = $.extend(defaults, options);
    options.sourceDomain = addEndingBackslash(options.sourceDomain);

    return this.each(function() {
        var obj = $(this);
        var sd;
        getServerData();

        function updateWidget(){
            var currentShow = sd.getCurrentShow();
            var nextShows = sd.getNextShows();

            var showStatus = options.text.offline;
            var currentShowName = "";
            var timeElapsed = "";
            var timeRemaining = "";

            var nextShowName = "";
            var nextShowRange = "";

            if (currentShow.length > 0){
                showStatus = options.text.onAirNow;
                currentShowName = currentShow[0].getName();

                timeElapsed = sd.getShowTimeElapsed(currentShow[0]);
                timeRemaining = sd.getShowTimeRemaining(currentShow[0]);
            }

            if (nextShows.length > 0){
                nextShowName = nextShows[0].getName();
                nextShowRange = nextShows[0].getRange();
            }

            obj.empty();
            obj.append("
"+showStatus+" >>
");
            obj.append("
" +
                "
"+options.text.current+": "+currentShowName+
                ""+timeElapsed+"" +
                ""+timeRemaining+""+
                "
" +
                "
"+options.text.next+": "+nextShowName+""+nextShowRange+"
" +
                "
");

            //refresh the UI to update the elapsed/remaining time
            setTimeout(updateWidget, 1000);
        }

        function processData(data){
            sd = new ScheduleData(data);
            updateWidget();
        }

        function airtimeScheduleJsonpError(jqXHR, textStatus, errorThrown){
        }

        function getServerData(){
            $.ajax({ url: options.sourceDomain + "api/live-info/", dataType:"jsonp", success:function(data){
                        processData(data);
                  }, error:airtimeScheduleJsonpError});
            setTimeout(getServerData, options.updatePeriod*1000);
        }
    });
 };
})(jQuery);

(function($){
 $.fn.airtimeWeekSchedule = function(options) {

    var defaults = {
        sourceDomain: "http://localhost/", //where to get show status from
        updatePeriod: 600,
        dowText: {monday:"Monday", tuesday:"Tuesday", wednesday:"Wednesday", thursday:"Thursday", friday:"Friday", saturday:"Saturday", sunday:"Sunday"},
        miscText: {time:"Time", programName:"Program Name", details:"Details", readMore:"Read More"}
    };
    var options = $.extend(defaults, options);
    options.sourceDomain = addEndingBackslash(options.sourceDomain);

    return this.each(function() {
        var obj = $(this);
        obj.empty();

        obj.attr("class", "ui-tabs");

        var dow = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];

        var date = new Date();
        //subtract 1 because javascript date function returns
        //sunday as 0 based, but we want Monday to be 0-based.
        var todayInt = (date.getDay()-1);
        if (todayInt < 0)
            todayInt += 7;

        var html = '
';
        for (var i=0; i<dow.length; i++){
            html += '
'+options.dowText[dow[i]]+'
';
        }
        html += '
';

        for (var i=0; i<dow.length; i++){
            html += '

'
        }
        obj.append(html);
        getServerData();

        function updateWidget(data){
            for (var i=0; i<dow.length; i++){
                var html =
                  '
'+
                    '<colgroup>'+
                      '<col width="150" />'+
                      '<col width="350" />'+
                      '<col width="240" />'+
                    '</colgroup>'+
                    '
'+
                      '
'+
                        ''+options.miscText.time+'	'+
                        ''+options.miscText.programName+'	'+
                        ''+options.miscText.details+'	'+
                      '
'+
                    '
'+
                    '
'+
                      '
'+
                        '	'+
                      '
'+
                    '
'+
                    '
';
                var daySchedule = data[dow[i]];
                for (var j=0; j<daySchedule.length; j++){
                    var url = daySchedule[j].url;
                    html +=
                      '
'+
                        ''+getTime(daySchedule[j].show_starts)+ " - " + getTime(daySchedule[j].show_ends)+'	'+
                        ''+
                          '
'+daySchedule[j].show_name+'
'+
                        '	'+
                        ''+
                          '
'+
                            '
'+(url.length > 0 ? ''+options.miscText.readMore+'':'')+'
'+
                          '
'+
                        '	'+
                      '
';
                }
                html +=
                    '
'+
                  '
';

                $("#"+dow[i]).empty();
                $("#"+dow[i]).append(html);
            }
        }

        function processData(data){
            updateWidget(data);
        }

        function airtimeScheduleJsonpError(jqXHR, textStatus, errorThrown){
        }

        function getServerData(){
            $.ajax({ url: options.sourceDomain + "api/week-info/", dataType:"jsonp", success:function(data){
                        processData(data);
                  }, error:airtimeScheduleJsonpError});
            setTimeout(getServerData, options.updatePeriod*1000);
        }
    });
 };
})(jQuery);

function addEndingBackslash(str){
    if (str.charAt(str.length-1) != '/')
        return str+'/';
    else return str;
}

/* ScheduleData class BEGIN */
function ScheduleData(data){
    this.data = data;
    this.estimatedSchedulePosixTime;

    this.currentShow = new Array();
    for (var i=0; i< data.currentShow.length; i++){
        this.currentShow[i] = new Show(data.currentShow[i]);
    }

    this.nextShows = new Array();
    for (var i=0; i< data.nextShow.length; i++){
        this.nextShows[i] = new Show(data.nextShow[i]);
    }

    this.schedulePosixTime = convertDateToPosixTime(data.schedulerTime);
    this.schedulePosixTime += parseInt(data.timezoneOffset)*1000;
    var date = new Date();
    this.localRemoteTimeOffset = date.getTime() - this.schedulePosixTime;
}

ScheduleData.prototype.secondsTimer = function(){
    var date = new Date();
    this.estimatedSchedulePosixTime = date.getTime() - this.localRemoteTimeOffset;
}

ScheduleData.prototype.getCurrentShow = function(){
    return this.currentShow;
}

ScheduleData.prototype.getNextShows = function() {
    return this.nextShows;
}

ScheduleData.prototype.getShowTimeElapsed = function(show) {
    this.secondsTimer();

    var showStart = convertDateToPosixTime(show.getStartTimestamp());
    return convertToHHMMSS(this.estimatedSchedulePosixTime - showStart);
};

ScheduleData.prototype.getShowTimeRemaining = function(show) {
    this.secondsTimer();

    var showEnd = convertDateToPosixTime(show.getEndTimestamp());
    return convertToHHMMSS(showEnd - this.estimatedSchedulePosixTime);
};
/* ScheduleData class END */

/* Show class BEGIN */
function Show(showData){
    this.showData = showData;
}

Show.prototype.getURL = function(){
    return this.showData.url;
}
Show.prototype.getName = function(){
    return this.showData.name;
}
Show.prototype.getRange = function(){
    return getTime(this.showData.start_timestamp) + " - " + getTime(this.showData.end_timestamp);
}
Show.prototype.getStartTimestamp = function(){
    return this.showData.start_timestamp;
}
Show.prototype.getEndTimestamp = function(){
    return this.showData.end_timestamp;
}
/* Show class END */

function getTime(timestamp) {
    var time = timestamp.split(" ")[1].split(":");
    return time[0] + ":" + time[1];
};

/* Takes an input parameter of milliseconds and converts these into
 * the format HH:MM:SS */
function convertToHHMMSS(timeInMS){
	var time = parseInt(timeInMS);

	var hours = parseInt(time / 3600000);
	time -= 3600000*hours;

	var minutes = parseInt(time / 60000);
	time -= 60000*minutes;

	var seconds = parseInt(time / 1000);

	hours = hours.toString();
	minutes = minutes.toString();
	seconds = seconds.toString();

	if (hours.length == 1)
		hours = "0" + hours;
	if (minutes.length == 1)
		minutes = "0" + minutes;
	if (seconds.length == 1)
		seconds = "0" + seconds;
	if (hours == "00")
		return minutes + ":" + seconds;
	else
		return hours + ":" + minutes + ":" + seconds;
}

/* Takes in a string of format similar to 2011-02-07 02:59:57,
 * and converts this to epoch/posix time. */
function convertDateToPosixTime(s){
    var datetime = s.split(" ");

    var date = datetime[0].split("-");
    var time = datetime[1].split(":");

	var year = date[0];
	var month = date[1];
	var day = date[2];
	var hour = time[0];
	var minute = time[1];
    var sec = 0;
    var msec = 0;

    if (time[2].indexOf(".") != -1){
        var temp = time[2].split(".");
        sec = temp[0];
        msec = temp[1];
    } else
        sec = time[2];

	return Date.UTC(year, month-1, day, hour, minute, sec, msec);
}
<|endoftext|>


<commit_before>//----------------------------------------------------------------------------
//  Copyright (C) 2013 The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// ContainerWidget
//============================================================================

/**
 * @module IPython
 * @namespace IPython
 **/

define(["notebook/js/widget"], function(widget_manager) {

    var set_flex_property = function(element, property_name, enabled) {
        if (enabled) {
            element.addClass(property_name);
        } else {
            element.removeClass(property_name);
        }
    };

    var set_flex_properties = function(context, element) {

        // Apply flexible box model properties by adding and removing
        // corrosponding CSS classes.
        // Defined in IPython/html/static/base/less/flexbox.less
        set_flex_property(element, 'vbox', context.model.get('_vbox'));
        set_flex_property(element, 'hbox', context.model.get('_hbox'));
        set_flex_property(element, 'start', context.model.get('_pack_start'));
        set_flex_property(element, 'center', context.model.get('_pack_center'));
        set_flex_property(element, 'end', context.model.get('_pack_end'));
        set_flex_property(element, 'align-start', context.model.get('_align_start'));
        set_flex_property(element, 'align-center', context.model.get('_align_center'));
        set_flex_property(element, 'align-end', context.model.get('_align_end'));
        set_flex_property(element, 'box-flex0', context.model.get('_flex0'));
        set_flex_property(element, 'box-flex1', context.model.get('_flex1'));
        set_flex_property(element, 'box-flex2', context.model.get('_flex2'));
    };

    var ContainerModel = IPython.WidgetModel.extend({});
    widget_manager.register_widget_model('ContainerWidgetModel', ContainerModel);

    var ContainerView = IPython.WidgetView.extend({

        render: function(){
            this.$el
                .addClass('widget-container');
        },

        update: function(){
            set_flex_properties(this, this.$el);
            return IPython.WidgetView.prototype.update.call(this);
        },

        display_child: function(view) {
            this.$el.append(view.$el);
        },
    });

    widget_manager.register_widget_view('ContainerView', ContainerView);

    var ModalView = IPython.WidgetView.extend({

        render: function(){
            var that = this;
            this.$el
                .html('')
                .on("remove", function(){
                    that.$window.remove();
                });
            this.$window = $('
')
                .addClass('modal widget-modal')
                .appendTo($('#notebook-container'));
            this.$title_bar = $('
')
                .addClass('popover-title')
                .appendTo(this.$window);
            var that = this;
            $('<button />')
                .addClass('close')
                .html('×')
                .appendTo(this.$title_bar)
                .click(function(){
                    that.hide();
                    event.stopPropagation();
                });
            this.$title = $('
')
                .addClass('widget-modal-title')
                .html(' ')
                .appendTo(this.$title_bar);
            this.$body = $('
')
                .addClass('modal-body')
                .addClass('widget-modal-body')
                .addClass('widget-container')
                .appendTo(this.$window);

            this.$show_button = $('<button />')
                .html(' ')
                .addClass('btn btn-info widget-modal-show')
                .appendTo(this.$el)
                .click(function(){
                    that.show();
                });

            this.$window.draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'});
            this.$window.resizable();
            this.$window.on('resize', function(){
                that.$body.outerHeight(that.$window.innerHeight() - that.$title_bar.outerHeight());
            })

            this.$el_to_style = this.$body;
            this._shown_once = false;
        },

        hide: function() {
            this.$window.hide();
            this.$show_button.removeClass('btn-info');
        },

        show: function() {
            this.$show_button.addClass('btn-info');

            this.$window.show();
            this.$window.css("positon", "absolute")
            this.$window.css("top", "0px");
            this.$window.css("left", Math.max(0, (($('body').outerWidth() - this.$window.outerWidth()) / 2) +
                $(window).scrollLeft()) + "px");
        },

        update: function(){
            set_flex_properties(this, this.$body);

            var description = this.model.get('description');
            description = description.replace(/ /g, ' ', 'm');
            description = description.replace(/\n/g, '
\n', 'm');
            if (description.length == 0) {
                this.$title.html(' '); // Preserve title height
            } else {
                this.$title.html(description);
            }

            var button_text = this.model.get('button_text');
            button_text = button_text.replace(/ /g, ' ', 'm');
            button_text = button_text.replace(/\n/g, '
\n', 'm');
            if (button_text.length == 0) {
                this.$show_button.html(' '); // Preserve button height
            } else {
                this.$show_button.html(button_text);
            }

            if (!this._shown_once) {
                this._shown_once = true;
                this.show();
            }

            return IPython.WidgetView.prototype.update.call(this);
        },

        display_child: function(view) {
            this.$body.append(view.$el);
        },

        _get_selector_element: function(selector) {

            // Since the modal actually isn't within the $el in the DOM, we need to extend
            // the selector logic to allow the user to set css on the modal if need be.
            // The convention used is:
            // "modal" - select the modal div
            // "modal [selector]" - select element(s) within the modal div.
            // "[selector]" - select elements within $el
            // "" - select the $el_to_style
            if (selector.substring(0, 5) == 'modal') {
                if (selector == 'modal') {
                    return this.$window;
                } else {
                    return this.$window.find(selector.substring(6));
                }
            } else {
                return IPython.WidgetView.prototype._get_selector_element.call(this, selector);
            }
        },

    });

    widget_manager.register_widget_view('ModalView', ModalView);
});
<commit_msg>- ModalView can now be docked and undocked - Clicking on a ModalView will bring it to the front<commit_after>//----------------------------------------------------------------------------
//  Copyright (C) 2013 The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// ContainerWidget
//============================================================================

/**
 * @module IPython
 * @namespace IPython
 **/

define(["notebook/js/widget"], function(widget_manager) {

    var set_flex_property = function(element, property_name, enabled) {
        if (enabled) {
            element.addClass(property_name);
        } else {
            element.removeClass(property_name);
        }
    };

    var set_flex_properties = function(context, element) {

        // Apply flexible box model properties by adding and removing
        // corrosponding CSS classes.
        // Defined in IPython/html/static/base/less/flexbox.less
        set_flex_property(element, 'vbox', context.model.get('_vbox'));
        set_flex_property(element, 'hbox', context.model.get('_hbox'));
        set_flex_property(element, 'start', context.model.get('_pack_start'));
        set_flex_property(element, 'center', context.model.get('_pack_center'));
        set_flex_property(element, 'end', context.model.get('_pack_end'));
        set_flex_property(element, 'align-start', context.model.get('_align_start'));
        set_flex_property(element, 'align-center', context.model.get('_align_center'));
        set_flex_property(element, 'align-end', context.model.get('_align_end'));
        set_flex_property(element, 'box-flex0', context.model.get('_flex0'));
        set_flex_property(element, 'box-flex1', context.model.get('_flex1'));
        set_flex_property(element, 'box-flex2', context.model.get('_flex2'));
    };

    var ContainerModel = IPython.WidgetModel.extend({});
    widget_manager.register_widget_model('ContainerWidgetModel', ContainerModel);

    var ContainerView = IPython.WidgetView.extend({

        render: function(){
            this.$el
                .addClass('widget-container');
        },

        update: function(){
            set_flex_properties(this, this.$el);
            return IPython.WidgetView.prototype.update.call(this);
        },

        display_child: function(view) {
            this.$el.append(view.$el);
        },
    });

    widget_manager.register_widget_view('ContainerView', ContainerView);

    var ModalView = IPython.WidgetView.extend({

        render: function(){
            var that = this;
            this.$el
                .html('')
                .on("remove", function(){
                    that.$window.remove();
                });
            this.$window = $('
')
                .addClass('modal widget-modal')
                .appendTo($('#notebook-container'))
                .mousedown(function(){
                    that.bring_to_front();
                });
            this.$title_bar = $('
')
                .addClass('popover-title')
                .appendTo(this.$window)
                .mousedown(function(){
                    that.bring_to_front();
                });;
            this.$close = $('<button />')
                .addClass('close icon-remove')
                .css('margin-left', '5px')
                .appendTo(this.$title_bar)
                .click(function(){
                    that.hide();
                    event.stopPropagation();
                });
            this.$minimize = $('<button />')
                .addClass('close icon-arrow-down')
                .appendTo(this.$title_bar)
                .click(function(){
                    that.popped_out = !that.popped_out;
                    if (!that.popped_out) {
                        that.$minimize
                            .removeClass('icon-arrow-down')
                            .addClass('icon-arrow-up');

                        that.$window
                            .draggable('destroy')
                            .resizable('destroy')
                            .removeClass('widget-modal modal')
                            .addClass('docked-widget-modal')
                            .detach()
                            .insertBefore(that.$show_button);
                        that.$show_button.hide();
                        that.$close.hide();
                    } else {
                        that.$minimize
                            .addClass('icon-arrow-down')
                            .removeClass('icon-arrow-up');

                        that.$window
                            .removeClass('docked-widget-modal')
                            .addClass('widget-modal modal')
                            .detach()
                            .appendTo($('#notebook-container'))
                            .draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'})
                            .resizable()
                            .children('.ui-resizable-handle').show();
                        that.show();
                        that.$show_button.show();
                        that.$close.show();
                    }
                    event.stopPropagation();
                });
            this.$title = $('
')
                .addClass('widget-modal-title')
                .html(' ')
                .appendTo(this.$title_bar);
            this.$body = $('
')
                .addClass('modal-body')
                .addClass('widget-modal-body')
                .addClass('widget-container')
                .appendTo(this.$window);

            this.$show_button = $('<button />')
                .html(' ')
                .addClass('btn btn-info widget-modal-show')
                .appendTo(this.$el)
                .click(function(){
                    that.show();
                });

            this.$window.draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'});
            this.$window.resizable();
            this.$window.on('resize', function(){
                that.$body.outerHeight(that.$window.innerHeight() - that.$title_bar.outerHeight());
            })

            this.$el_to_style = this.$body;
            this._shown_once = false;
            this.popped_out = true;
        },

        hide: function() {
            this.$window.hide();
            this.$show_button.removeClass('btn-info');
        },

        show: function() {
            this.$show_button.addClass('btn-info');

            this.$window.show();
            if (this.popped_out) {
                this.$window.css("positon", "absolute")
                this.$window.css("top", "0px");
                this.$window.css("left", Math.max(0, (($('body').outerWidth() - this.$window.outerWidth()) / 2) +
                    $(window).scrollLeft()) + "px");
                this.bring_to_front();
            }
        },

        bring_to_front: function() {
            var $widget_modals = $(".widget-modal");
            var max_zindex = 0;
            $widget_modals.each(function (index, el){
                max_zindex = Math.max(max_zindex, parseInt($(el).css('z-index')));
            });

            // Start z-index of widget modals at 2000
            max_zindex = Math.max(max_zindex, 2000);

            $widget_modals.each(function (index, el){
                $el = $(el)
                if (max_zindex == parseInt($el.css('z-index'))) {
                    $el.css('z-index', max_zindex - 1);
                }
            });
            this.$window.css('z-index', max_zindex);
        },

        update: function(){
            set_flex_properties(this, this.$body);

            var description = this.model.get('description');
            description = description.replace(/ /g, ' ', 'm');
            description = description.replace(/\n/g, '
\n', 'm');
            if (description.length == 0) {
                this.$title.html(' '); // Preserve title height
            } else {
                this.$title.html(description);
            }

            var button_text = this.model.get('button_text');
            button_text = button_text.replace(/ /g, ' ', 'm');
            button_text = button_text.replace(/\n/g, '
\n', 'm');
            if (button_text.length == 0) {
                this.$show_button.html(' '); // Preserve button height
            } else {
                this.$show_button.html(button_text);
            }

            if (!this._shown_once) {
                this._shown_once = true;
                this.show();
            }

            return IPython.WidgetView.prototype.update.call(this);
        },

        display_child: function(view) {
            this.$body.append(view.$el);
        },

        _get_selector_element: function(selector) {

            // Since the modal actually isn't within the $el in the DOM, we need to extend
            // the selector logic to allow the user to set css on the modal if need be.
            // The convention used is:
            // "modal" - select the modal div
            // "modal [selector]" - select element(s) within the modal div.
            // "[selector]" - select elements within $el
            // "" - select the $el_to_style
            if (selector.substring(0, 5) == 'modal') {
                if (selector == 'modal') {
                    return this.$window;
                } else {
                    return this.$window.find(selector.substring(6));
                }
            } else {
                return IPython.WidgetView.prototype._get_selector_element.call(this, selector);
            }
        },

    });

    widget_manager.register_widget_view('ModalView', ModalView);
});
<|endoftext|>


<commit_before>test('getTargetSelector returns the correct target', function() {
	var fixture = new ElementQueries();
	equal(fixture.getTargetSelector('.megapony-rwd-table-container.megapony-max-width-400'), '.megapony-rwd-table-container');
	equal(fixture.getTargetSelector('DIV.csc-textpic.megapony-max-width-6401123 .csc-textpic-imagewrap'), 'DIV.csc-textpic');
	equal(fixture.getTargetSelector('.l-facts header.megapony-max-width-220 + ul'), '.l-facts header');
	equal(fixture.getTargetSelector('.news-list .megapony-object-column.megapony-max-width-410:nth-child(1) + .megapony-object-column'), '.news-list .megapony-object-column:nth-child(1)');
	equal(fixture.getTargetSelector('.megapony-rwd-table-container.megapony-max-width-400 .rwd-table'), '.megapony-rwd-table-container');
	equal(fixture.getTargetSelector('.megapony-max-width-480#extended-header .megapony-delimiter .facts'), '#extended-header');
});

test('getLengthFromSelector returns the correct length', function () {
	var subject = new ElementQueries();
	equal(subject.getLengthFromSelector('.megapony-min-height-', '.news-list .megapony-object-column.megapony-max-height-410:nth-child(1) + .megapony-object-column'), false);
	equal(subject.getLengthFromSelector('.megapony-min-height-', '.news-list .megapony-object-column.megapony-min-height-410:nth-child(1) + .megapony-object-column'), 410);
	equal(subject.getLengthFromSelector('.megapony-max-height-', '.news-list .megapony-object-column.megapony-max-height-410:nth-child(1) + .megapony-object-column'), 410);
	equal(subject.getLengthFromSelector('.megapony-max-height-', '.news-list .megapony-object-column.megapony-min-height-410:nth-child(1) + .megapony-object-column'), false);
	equal(subject.getLengthFromSelector('.megapony-min-width-', '.news-list .megapony-object-column.megapony-max-width-410:nth-child(1) + .megapony-object-column'), false);
	equal(subject.getLengthFromSelector('.megapony-min-width-', '.news-list .megapony-object-column.megapony-min-width-410:nth-child(1) + .megapony-object-column'), 410);
	equal(subject.getLengthFromSelector('.megapony-max-width-', '.news-list .megapony-object-column.megapony-min-width-410:nth-child(1) + .megapony-object-column'), false);
	equal(subject.getLengthFromSelector('.megapony-max-width-', '.news-list .megapony-object-column.megapony-max-width-410:nth-child(1) + .megapony-object-column'), 410);
});

test('applyElementQueries applies max-width class', function() {
	var elements = [
		{
			offsetWidth : 100,
			classList : {
				add : function(cssClass) {
					equal(cssClass, 'megapony-max-width-200')
				}
			}
		}
	]

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {maxW: 200})
});

test('applyElementQueries applies min-width class', function() {
	var elements = [
		{
			offsetWidth : 300,
			classList : {
				add : function(cssClass) {
					equal(cssClass, 'megapony-min-width-200')
				}
			}
		}
	]

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {minW: 200})
});

test('applyElementQueries applies min-height class', function() {
	var elements = [
		{
			offsetHeight : 300,
			classList : {
				add : function(cssClass) {
					equal(cssClass, 'megapony-min-height-200')
				}
			}
		}
	]

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {minH: 200})
});

test('applyElementQueries applies max-height class', function() {
	var elements = [
		{
			offsetHeight : 100,
			classList : {
				add : function(cssClass) {
					equal(cssClass, 'megapony-max-height-200')
				}
			}
		}
	]

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {maxH: 200})
});

test('applyElementQueries applies max-width && min-width class', function() {
	var elements = [
		{
			offsetWidth : 100,
			classList : {
				add : function(cssClass) {
					ok(cssClass == 'megapony-max-width-200' || cssClass == 'megapony-min-width-50')
				}
			}
		}
	]

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {maxW: 200, minW: 50})
});

test('applyElementQueries applies max-height && min-height class', function() {
	var elements = [
		{
			offsetHeight : 100,
			classList : {
				add : function(cssClass) {
					ok(cssClass == 'megapony-max-height-200' || cssClass == 'megapony-min-height-50')
				}
			}
		}
	]

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {maxH: 200, minH: 50})
});

test('getMegaponyStyleSheets returns all stylesheets with media=megapony', function() {
	var stylesheets = [
			{
				title: 'megapony'
			},
			{
				title: ''
			},
			{
				title: 'megapony'
			}
		];

	var subject = new ElementQueries();

	equal(subject.getMegaponyStyleSheets(stylesheets).length, 2);
});<commit_msg>[TASK] JSHint Cleanup<commit_after>test('getTargetSelector returns the correct target', function () {
	var fixture = new ElementQueries();
	equal(fixture.getTargetSelector('.megapony-rwd-table-container.megapony-max-width-400'), '.megapony-rwd-table-container');
	equal(fixture.getTargetSelector('DIV.csc-textpic.megapony-max-width-6401123 .csc-textpic-imagewrap'), 'DIV.csc-textpic');
	equal(fixture.getTargetSelector('.l-facts header.megapony-max-width-220 + ul'), '.l-facts header');
	equal(fixture.getTargetSelector('.news-list .megapony-object-column.megapony-max-width-410:nth-child(1) + .megapony-object-column'), '.news-list .megapony-object-column:nth-child(1)');
	equal(fixture.getTargetSelector('.megapony-rwd-table-container.megapony-max-width-400 .rwd-table'), '.megapony-rwd-table-container');
	equal(fixture.getTargetSelector('.megapony-max-width-480#extended-header .megapony-delimiter .facts'), '#extended-header');
});

test('getLengthFromSelector returns the correct length', function () {
	var subject = new ElementQueries();
	equal(subject.getLengthFromSelector('.megapony-min-height-', '.news-list .megapony-object-column.megapony-max-height-410:nth-child(1) + .megapony-object-column'), false);
	equal(subject.getLengthFromSelector('.megapony-min-height-', '.news-list .megapony-object-column.megapony-min-height-410:nth-child(1) + .megapony-object-column'), 410);
	equal(subject.getLengthFromSelector('.megapony-max-height-', '.news-list .megapony-object-column.megapony-max-height-410:nth-child(1) + .megapony-object-column'), 410);
	equal(subject.getLengthFromSelector('.megapony-max-height-', '.news-list .megapony-object-column.megapony-min-height-410:nth-child(1) + .megapony-object-column'), false);
	equal(subject.getLengthFromSelector('.megapony-min-width-', '.news-list .megapony-object-column.megapony-max-width-410:nth-child(1) + .megapony-object-column'), false);
	equal(subject.getLengthFromSelector('.megapony-min-width-', '.news-list .megapony-object-column.megapony-min-width-410:nth-child(1) + .megapony-object-column'), 410);
	equal(subject.getLengthFromSelector('.megapony-max-width-', '.news-list .megapony-object-column.megapony-min-width-410:nth-child(1) + .megapony-object-column'), false);
	equal(subject.getLengthFromSelector('.megapony-max-width-', '.news-list .megapony-object-column.megapony-max-width-410:nth-child(1) + .megapony-object-column'), 410);
});

test('applyElementQueries applies max-width class', function () {
	var elements = [
		{
			offsetWidth : 100,
			classList : {
				add : function (cssClass) {
					equal(cssClass, 'megapony-max-width-200');
				}
			}
		}
	];

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {maxW: 200});
});

test('applyElementQueries applies min-width class', function () {
	var elements = [
		{
			offsetWidth : 300,
			classList : {
				add : function (cssClass) {
					equal(cssClass, 'megapony-min-width-200');
				}
			}
		}
	];

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {minW: 200});
});

test('applyElementQueries applies min-height class', function () {
	var elements = [
		{
			offsetHeight : 300,
			classList : {
				add : function (cssClass) {
					equal(cssClass, 'megapony-min-height-200');
				}
			}
		}
	];

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {minH: 200});
});

test('applyElementQueries applies max-height class', function () {
	var elements = [
		{
			offsetHeight : 100,
			classList : {
				add : function (cssClass) {
					equal(cssClass, 'megapony-max-height-200');
				}
			}
		}
	];

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {maxH: 200});
});

test('applyElementQueries applies max-width && min-width class', function () {
	var elements = [
		{
			offsetWidth : 100,
			classList : {
				add : function (cssClass) {
					ok(cssClass === 'megapony-max-width-200' || cssClass === 'megapony-min-width-50');
				}
			}
		}
	];

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {maxW: 200, minW: 50});
});

test('applyElementQueries applies max-height && min-height class', function () {
	var elements = [
		{
			offsetHeight : 100,
			classList : {
				add : function (cssClass) {
					ok(cssClass === 'megapony-max-height-200' || cssClass === 'megapony-min-height-50');
				}
			}
		}
	];

	var subject = new ElementQueries();
	subject.applyElementQueries(elements, {maxH: 200, minH: 50});
});

test('getMegaponyStyleSheets returns all stylesheets with media=megapony', function () {
	var stylesheets = [
			{
				title: 'megapony'
			},
			{
				title: ''
			},
			{
				title: 'megapony'
			}
		];

	var subject = new ElementQueries();
	equal(subject.getMegaponyStyleSheets(stylesheets).length, 2);
});<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule PushNotificationIOS
 * @flow
 */
'use strict';

var Map = require('Map');
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var RCTPushNotificationManager = require('NativeModules').PushNotificationManager;
var invariant = require('invariant');

var _notifHandlers = new Map();
var _initialNotification = RCTPushNotificationManager &&
  RCTPushNotificationManager.initialNotification;

var DEVICE_NOTIF_EVENT = 'remoteNotificationReceived';
var NOTIF_REGISTER_EVENT = 'remoteNotificationsRegistered';

/**
 * Handle push notifications for your app, including permission handling and
 * icon badge number.
 *
 * To get up and running, [configure your notifications with Apple](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/ConfiguringPushNotifications/ConfiguringPushNotifications.html)
 * and your server-side system. To get an idea, [this is the Parse guide](https://parse.com/tutorials/ios-push-notifications).
 */
class PushNotificationIOS {
  _data: Object;
  _alert: string | Object;
  _sound: string;
  _badgeCount: number;

  /**
   * Schedules the localNotification for immediate presentation.
   *
   * details is an object containing:
   *
   * - `alertBody` : The message displayed in the notification alert.
   *
   */
  static presentLocalNotification(details: Object) {
    RCTPushNotificationManager.presentLocalNotification(details);
  }

  /**
   * Schedules the localNotification for future presentation.
   *
   * details is an object containing:
   *
   * - `fireDate` : The date and time when the system should deliver the notification.
   * - `alertBody` : The message displayed in the notification alert.
   *
   */
  static scheduleLocalNotification(details: Object) {
    RCTPushNotificationManager.scheduleLocalNotification(details);
  }

  /**
   * Sets the badge number for the app icon on the home screen
   */
  static setApplicationIconBadgeNumber(number: number) {
    RCTPushNotificationManager.setApplicationIconBadgeNumber(number);
  }

  /**
   * Gets the current badge number for the app icon on the home screen
   */
  static getApplicationIconBadgeNumber(callback: Function) {
    RCTPushNotificationManager.getApplicationIconBadgeNumber(callback);
  }

  /**
   * Attaches a listener to remote notification events while the app is running
   * in the foreground or the background.
   *
   * Valid events are:
   *
   * - `notification` : Fired when a remote notification is received. The
   *   handler will be invoked with an instance of `PushNotificationIOS`.
   * - `register`: Fired when the user registers for remote notifications. The
   *   handler will be invoked with a hex string representing the deviceToken.
   */
  static addEventListener(type: string, handler: Function) {
    invariant(
      type === 'notification' || type === 'register',
      'PushNotificationIOS only supports `notification` and `register` events'
    );
    var listener;
    if (type === 'notification') {
      listener =  RCTDeviceEventEmitter.addListener(
        DEVICE_NOTIF_EVENT,
        (notifData) => {
          handler(new PushNotificationIOS(notifData));
        }
      );
    } else if (type === 'register') {
      listener = RCTDeviceEventEmitter.addListener(
        NOTIF_REGISTER_EVENT,
        (registrationInfo) => {
          handler(registrationInfo.deviceToken);
        }
      );
    }
    _notifHandlers.set(handler, listener);
  }

  /**
   * Requests notification permissions from iOS, prompting the user's
   * dialog box. By default, it will request all notification permissions, but
   * a subset of these can be requested by passing a map of requested
   * permissions.
   * The following permissions are supported:
   *
   *   - `alert`
   *   - `badge`
   *   - `sound`
   *
   * If a map is provided to the method, only the permissions with truthy values
   * will be requested.
   */
  static requestPermissions(permissions?: {
    alert?: boolean,
    badge?: boolean,
    sound?: boolean
  }) {
    var requestedPermissions = {};
    if (permissions) {
      requestedPermissions = {
        alert: !!permissions.alert,
        badge: !!permissions.badge,
        sound: !!permissions.sound
      };
    } else {
      requestedPermissions = {
        alert: true,
        badge: true,
        sound: true
      };
    }
    RCTPushNotificationManager.requestPermissions(requestedPermissions);
  }

  /**
   * Unregister for all remote notifications received via Apple Push Notification service.
   *
   * You should call this method in rare circumstances only, such as when a new version of
   * the app removes support for all types of remote notifications. Users can temporarily
   * prevent apps from receiving remote notifications through the Notifications section of
   * the Settings app. Apps unregistered through this method can always re-register.
   */
  static abandonPermissions() {
    RCTPushNotificationManager.abandonPermissions();
  }

  /**
   * See what push permissions are currently enabled. `callback` will be
   * invoked with a `permissions` object:
   *
   *  - `alert` :boolean
   *  - `badge` :boolean
   *  - `sound` :boolean
   */
  static checkPermissions(callback: Function) {
    invariant(
      typeof callback === 'function',
      'Must provide a valid callback'
    );
    RCTPushNotificationManager.checkPermissions(callback);
  }

  /**
   * Removes the event listener. Do this in `componentWillUnmount` to prevent
   * memory leaks
   */
  static removeEventListener(type: string, handler: Function) {
    invariant(
      type === 'notification' || type === 'register',
      'PushNotificationIOS only supports `notification` and `register` events'
    );
    var listener = _notifHandlers.get(handler);
    if (!listener) {
      return;
    }
    listener.remove();
    _notifHandlers.delete(handler);
  }

  /**
   * An initial notification will be available if the app was cold-launched
   * from a notification.
   *
   * The first caller of `popInitialNotification` will get the initial
   * notification object, or `null`. Subsequent invocations will return null.
   */
  static popInitialNotification() {
    var initialNotification = _initialNotification &&
      new PushNotificationIOS(_initialNotification);
    _initialNotification = null;
    return initialNotification;
  }

  /**
   * You will never need to instansiate `PushNotificationIOS` yourself.
   * Listening to the `notification` event and invoking
   * `popInitialNotification` is sufficient
   */
  constructor(nativeNotif: Object) {
    this._data = {};

    // Extract data from Apple's `aps` dict as defined:

    // https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html

    Object.keys(nativeNotif).forEach((notifKey) => {
      var notifVal = nativeNotif[notifKey];
      if (notifKey === 'aps') {
        this._alert = notifVal.alert;
        this._sound = notifVal.sound;
        this._badgeCount = notifVal.badge;
      } else {
        this._data[notifKey] = notifVal;
      }
    });
  }

  /**
   * An alias for `getAlert` to get the notification's main message string
   */
  getMessage(): ?string | ?Object {
    // alias because "alert" is an ambiguous name
    return this._alert;
  }

  /**
   * Gets the sound string from the `aps` object
   */
  getSound(): ?string {
    return this._sound;
  }

  /**
   * Gets the notification's main message from the `aps` object
   */
  getAlert(): ?string | ?Object {
    return this._alert;
  }

  /**
   * Gets the badge count number from the `aps` object
   */
  getBadgeCount(): ?number {
    return this._badgeCount;
  }

  /**
   * Gets the data object on the notif
   */
  getData(): ?Object {
    return this._data;
  }
}

module.exports = PushNotificationIOS;
<commit_msg>Fix dead link in PushNotificationIOS docs.<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule PushNotificationIOS
 * @flow
 */
'use strict';

var Map = require('Map');
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var RCTPushNotificationManager = require('NativeModules').PushNotificationManager;
var invariant = require('invariant');

var _notifHandlers = new Map();
var _initialNotification = RCTPushNotificationManager &&
  RCTPushNotificationManager.initialNotification;

var DEVICE_NOTIF_EVENT = 'remoteNotificationReceived';
var NOTIF_REGISTER_EVENT = 'remoteNotificationsRegistered';

/**
 * Handle push notifications for your app, including permission handling and
 * icon badge number.
 *
 * To get up and running, [configure your notifications with Apple](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/AddingCapabilities/AddingCapabilities.html#//apple_ref/doc/uid/TP40012582-CH26-SW6)
 * and your server-side system. To get an idea, [this is the Parse guide](https://parse.com/tutorials/ios-push-notifications).
 */
class PushNotificationIOS {
  _data: Object;
  _alert: string | Object;
  _sound: string;
  _badgeCount: number;

  /**
   * Schedules the localNotification for immediate presentation.
   *
   * details is an object containing:
   *
   * - `alertBody` : The message displayed in the notification alert.
   *
   */
  static presentLocalNotification(details: Object) {
    RCTPushNotificationManager.presentLocalNotification(details);
  }

  /**
   * Schedules the localNotification for future presentation.
   *
   * details is an object containing:
   *
   * - `fireDate` : The date and time when the system should deliver the notification.
   * - `alertBody` : The message displayed in the notification alert.
   *
   */
  static scheduleLocalNotification(details: Object) {
    RCTPushNotificationManager.scheduleLocalNotification(details);
  }

  /**
   * Sets the badge number for the app icon on the home screen
   */
  static setApplicationIconBadgeNumber(number: number) {
    RCTPushNotificationManager.setApplicationIconBadgeNumber(number);
  }

  /**
   * Gets the current badge number for the app icon on the home screen
   */
  static getApplicationIconBadgeNumber(callback: Function) {
    RCTPushNotificationManager.getApplicationIconBadgeNumber(callback);
  }

  /**
   * Attaches a listener to remote notification events while the app is running
   * in the foreground or the background.
   *
   * Valid events are:
   *
   * - `notification` : Fired when a remote notification is received. The
   *   handler will be invoked with an instance of `PushNotificationIOS`.
   * - `register`: Fired when the user registers for remote notifications. The
   *   handler will be invoked with a hex string representing the deviceToken.
   */
  static addEventListener(type: string, handler: Function) {
    invariant(
      type === 'notification' || type === 'register',
      'PushNotificationIOS only supports `notification` and `register` events'
    );
    var listener;
    if (type === 'notification') {
      listener =  RCTDeviceEventEmitter.addListener(
        DEVICE_NOTIF_EVENT,
        (notifData) => {
          handler(new PushNotificationIOS(notifData));
        }
      );
    } else if (type === 'register') {
      listener = RCTDeviceEventEmitter.addListener(
        NOTIF_REGISTER_EVENT,
        (registrationInfo) => {
          handler(registrationInfo.deviceToken);
        }
      );
    }
    _notifHandlers.set(handler, listener);
  }

  /**
   * Requests notification permissions from iOS, prompting the user's
   * dialog box. By default, it will request all notification permissions, but
   * a subset of these can be requested by passing a map of requested
   * permissions.
   * The following permissions are supported:
   *
   *   - `alert`
   *   - `badge`
   *   - `sound`
   *
   * If a map is provided to the method, only the permissions with truthy values
   * will be requested.
   */
  static requestPermissions(permissions?: {
    alert?: boolean,
    badge?: boolean,
    sound?: boolean
  }) {
    var requestedPermissions = {};
    if (permissions) {
      requestedPermissions = {
        alert: !!permissions.alert,
        badge: !!permissions.badge,
        sound: !!permissions.sound
      };
    } else {
      requestedPermissions = {
        alert: true,
        badge: true,
        sound: true
      };
    }
    RCTPushNotificationManager.requestPermissions(requestedPermissions);
  }

  /**
   * Unregister for all remote notifications received via Apple Push Notification service.
   *
   * You should call this method in rare circumstances only, such as when a new version of
   * the app removes support for all types of remote notifications. Users can temporarily
   * prevent apps from receiving remote notifications through the Notifications section of
   * the Settings app. Apps unregistered through this method can always re-register.
   */
  static abandonPermissions() {
    RCTPushNotificationManager.abandonPermissions();
  }

  /**
   * See what push permissions are currently enabled. `callback` will be
   * invoked with a `permissions` object:
   *
   *  - `alert` :boolean
   *  - `badge` :boolean
   *  - `sound` :boolean
   */
  static checkPermissions(callback: Function) {
    invariant(
      typeof callback === 'function',
      'Must provide a valid callback'
    );
    RCTPushNotificationManager.checkPermissions(callback);
  }

  /**
   * Removes the event listener. Do this in `componentWillUnmount` to prevent
   * memory leaks
   */
  static removeEventListener(type: string, handler: Function) {
    invariant(
      type === 'notification' || type === 'register',
      'PushNotificationIOS only supports `notification` and `register` events'
    );
    var listener = _notifHandlers.get(handler);
    if (!listener) {
      return;
    }
    listener.remove();
    _notifHandlers.delete(handler);
  }

  /**
   * An initial notification will be available if the app was cold-launched
   * from a notification.
   *
   * The first caller of `popInitialNotification` will get the initial
   * notification object, or `null`. Subsequent invocations will return null.
   */
  static popInitialNotification() {
    var initialNotification = _initialNotification &&
      new PushNotificationIOS(_initialNotification);
    _initialNotification = null;
    return initialNotification;
  }

  /**
   * You will never need to instansiate `PushNotificationIOS` yourself.
   * Listening to the `notification` event and invoking
   * `popInitialNotification` is sufficient
   */
  constructor(nativeNotif: Object) {
    this._data = {};

    // Extract data from Apple's `aps` dict as defined:

    // https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html

    Object.keys(nativeNotif).forEach((notifKey) => {
      var notifVal = nativeNotif[notifKey];
      if (notifKey === 'aps') {
        this._alert = notifVal.alert;
        this._sound = notifVal.sound;
        this._badgeCount = notifVal.badge;
      } else {
        this._data[notifKey] = notifVal;
      }
    });
  }

  /**
   * An alias for `getAlert` to get the notification's main message string
   */
  getMessage(): ?string | ?Object {
    // alias because "alert" is an ambiguous name
    return this._alert;
  }

  /**
   * Gets the sound string from the `aps` object
   */
  getSound(): ?string {
    return this._sound;
  }

  /**
   * Gets the notification's main message from the `aps` object
   */
  getAlert(): ?string | ?Object {
    return this._alert;
  }

  /**
   * Gets the badge count number from the `aps` object
   */
  getBadgeCount(): ?number {
    return this._badgeCount;
  }

  /**
   * Gets the data object on the notif
   */
  getData(): ?Object {
    return this._data;
  }
}

module.exports = PushNotificationIOS;
<|endoftext|>


<commit_before>var THREE = require("three");

var parsedPoints = [],
    parsedTags = [],
    maxEuc = 0,
    minEuc = Number.MAX_VALUE,
    maxZ = 0,
    hueOffset = 20,
    total = 0;

var Data = module.exports = {
    pointSize: 2,
    cloudSize2D: 1.5,

    loadData: function (data) {
        parsedPoints = [];
        parsedTags = [];
        total = data.length;
        var hues = [];

        var i;
        for (i = 0; i < data.length; i++) {
            var dataPoint = new THREE.Vector3(data[i][1], data[i][2], data[i][3]);
            dataPoint.url = "audio/" + data[i][4];
            dataPoint.meta = this.parseTags(data[i][5], i);
            parsedPoints.push(dataPoint);

            var x = Math.pow(parsedPoints[i].x, 2);
            var y = Math.pow(parsedPoints[i].y, 2);
            var z = Math.pow(parsedPoints[i].z, 2);
            var hue = Math.sqrt(x + y + z);
            hues.push(hue);

            maxZ = Math.max(maxZ, z);
            maxEuc = Math.max(maxEuc, hue);
            minEuc = Math.min(minEuc, hue);
        }
        for (i = 0; i < data.length; i++) {
            var color = new THREE.Color();
            var lightness = parsedPoints[i].z / (2 * maxZ);
            // should we continue (maxEuc-minEuc+hueOffset):lla? Seemes to make it worse..
            color.setHSL((hues[i] - minEuc + hueOffset) / (maxEuc - minEuc), 1, lightness + 0.5);
            parsedPoints[i].color = color;

        }
    },

    /**
     * Parses tag JSON into tag objects.
     * @param {Array} tags
     * @param {Number} pointIndex
     * @return {Array} meta
     */
    parseTags: function (tags, pointIndex) {
        var meta = [];
        for (var i = 0; i < tags.length; i++) {
            var tagKey = tags[i].key;
            var tagVal = tags[i].val;

            // Parse tag for a point object
            var tagIndex = this.addTagObject(meta, tagKey);
            meta[tagIndex].values.push(tagVal);

            // Parse tag for parsedTag array
            tagIndex = this.addTagObject(parsedTags, tagKey);
            var values = parsedTags[tagIndex].values;
            var valueIndex = this.addValueObject(values, tagVal);
            values[valueIndex].points.push(pointIndex);
        }
        // Returns array of tag objects for use as a property of a point object
        return meta;
    },

    /**
     * Adds a tag object to an array if it doesn't exist.
     * Returns index of a tag object in a array.
     */
    // addTagObject: function (array, tagKey) {
    //     var tagIndex = this.getObjectIndex(array, 'key', tagKey);
    //     if (tagIndex === -1) {
    //         array.push({ key: tagKey, values: [] });
    //         return array.length - 1;
    //     }
    //     return tagIndex;
    // },
    addTagObject: function (array, tagKey) {
        return this.addTwoValueObject(array, 'key', tagKey, 'values', new Array());
    },

    /**
     * Adds a value object to an array if it doesn't exist.
     * Returns index of a value object in a array.
     */
    addValueObject: function (array, tagVal) {
        return this.addTwoValueObject(array, 'value', tagVal, 'points', new Array());
    },
    // addValueObject: function (array, tagVal) {
    //     var valueIndex = this.getObjectIndex(array, 'value', tagVal);
    //     if (valueIndex === -1) {
    //         array.push({ value: tagVal, points: [] });
    //         return array.length - 1;
    //     }
    //     return valueIndex;
    // },

    addTwoValueObject: function (array, firstKey, firstValue, secondKey, secondValue) {
        var valueIndex = this.getObjectIndex(array, firstKey, firstValue);
        if (valueIndex === -1) {
            array.push({ [firstKey]: firstValue, [secondKey]: secondValue });
            return array.length - 1;
        }
        return valueIndex;
    },

    getObjectIndex: function (array, propertyName, value) {
        for (var i = 0; i < array.length; i++) {
            var object = array[i];
            if (array[i][propertyName] === value) {
                return i;
            }
        }
        return -1;
    },

    getTag: function (key) {
        var index = this.getObjectIndex(parsedTags, 'key', key)
        if (index === -1) {
            return undefined;
        }
        return parsedTags[index];
    },

    getTotalPoints: function () {
        return total;
    },

    getUrl: function (index) {
        return parsedPoints[index].url;
    },

    getPosition: function (index) {
        return parsedPoints[index];
    },

    getColor: function (index) {
        return parsedPoints[index].color;
    },

    getTags: function () {
        return parsedTags;
    }

};

<commit_msg>refactored Data.js to remove duplicate code and added some documentation<commit_after>var THREE = require("three");

var parsedPoints = [],
    parsedTags = [],
    maxEuc = 0,
    minEuc = Number.MAX_VALUE,
    maxZ = 0,
    hueOffset = 20,
    total = 0;

var Data = module.exports = {
    pointSize: 2,
    cloudSize2D: 1.5,

    loadData: function (data) {
        parsedPoints = [];
        parsedTags = [];
        total = data.length;
        var hues = [];

        var i;
        for (i = 0; i < data.length; i++) {
            var dataPoint = new THREE.Vector3(data[i][1], data[i][2], data[i][3]);
            dataPoint.url = "audio/" + data[i][4];
            dataPoint.meta = this.parseTags(data[i][5], i);
            parsedPoints.push(dataPoint);

            var x = Math.pow(parsedPoints[i].x, 2);
            var y = Math.pow(parsedPoints[i].y, 2);
            var z = Math.pow(parsedPoints[i].z, 2);
            var hue = Math.sqrt(x + y + z);
            hues.push(hue);

            maxZ = Math.max(maxZ, z);
            maxEuc = Math.max(maxEuc, hue);
            minEuc = Math.min(minEuc, hue);
        }
        for (i = 0; i < data.length; i++) {
            var color = new THREE.Color();
            var lightness = parsedPoints[i].z / (2 * maxZ);
            // should we continue (maxEuc-minEuc+hueOffset):lla? Seemes to make it worse..
            color.setHSL((hues[i] - minEuc + hueOffset) / (maxEuc - minEuc), 1, lightness + 0.5);
            parsedPoints[i].color = color;

        }
    },

    /**
     * Parses tag JSON into tag objects.
     * @param {array} tags
     * @param {number} pointIndex
     * @returns {array} meta
     */
    parseTags: function (tags, pointIndex) {
        var meta = [],
            tagKey,
            tagVal,
            tagIndex,
            values,
            valueIndex;

        for (var i = 0; i < tags.length; i++) {
            tagKey = tags[i].key;
            tagVal = tags[i].val;

            // Parse tag for a point object
            tagIndex = this.addTwoPropertyObject(meta, 'key', tagKey, 'values', new Array());
            meta[tagIndex].values.push(tagVal);

            // Parse tag for parsedTag array
            tagIndex = this.addTwoPropertyObject(parsedTags, 'key', tagKey, 'values', new Array());
            values = parsedTags[tagIndex].values;
            valueIndex = this.addTwoPropertyObject(values, 'value', tagVal, 'points', new Array());
            values[valueIndex].points.push(pointIndex);
        }
        // Returns array of tag objects for use as a property of a point object
        return meta;
    },

    /**
     * Adds an object with two properties to an array if it doesnt exist and returns index of that object.
     * @param {array} array - array to wich object will be added
     * @param {string} firstKey - name of the first property
     * @param {any} firstValue - value of the first property
     * @param {string} secondKey - name of the second property
     * @param {any} secondValue - value of the second property
     * @returns {number} Index of the object
     */
    addTwoPropertyObject: function (array, firstKey, firstValue, secondKey, secondValue) {
        var valueIndex = this.getObjectIndex(array, firstKey, firstValue);
        if (valueIndex === -1) {
            array.push({ [firstKey]: firstValue, [secondKey]: secondValue });
            return array.length - 1;
        }
        return valueIndex;
    },

    /**
     * Returns index of an object with desired value of a property
     * @param {array} array - array that will be searched
     * @param {string} propertyName - name of the attribute that will be compared
     * @param {any} value - wanted value of the attribute
     * @returns {number} Index of an object
     */
    getObjectIndex: function (array, propertyName, value) {
        for (var i = 0; i < array.length; i++) {
            var object = array[i];
            if (array[i][propertyName] === value) {
                return i;
            }
        }
        return -1;
    },

    getTag: function (key) {
        var index = this.getObjectIndex(parsedTags, 'key', key)
        if (index === -1) {
            return undefined;
        }
        return parsedTags[index];
    },

    getTotalPoints: function () {
        return total;
    },

    getUrl: function (index) {
        return parsedPoints[index].url;
    },

    getPosition: function (index) {
        return parsedPoints[index];
    },

    getColor: function (index) {
        return parsedPoints[index].color;
    },

    getTags: function () {
        return parsedTags;
    }

};

<|endoftext|>


<commit_before>var React = require('react');
var Header = require('./Header.js');

var Home = React.createClass({
  openIntroduction: function () {
    window.open('http://www.christianalfoni.com/articles/2015_09_22_Introducing-Cerebral');
  },
  render: function () {
    return (

Cerebral

State

              Applications are stateful. The application needs to know what page to display,
              if a dropdown is opened or you are currently getting some data from the server. With
              Cerebral you define all the state of your application in one state tree. Think of it like
              a client database, but it holds all the data your applications needs, even if it is just for
              the client.

UI

              The UI is produced using the application state. It is passed to a render function or exposed
              to a template. When you want your UI to change, your application state has to change. Your application
              listens to UI events and uses Cerebral to execute the state update. After an update is complete
              the UI will render the new state of the application.

Express your application flow with signals

            Packages

 React

 Angular

 Baobab

 Tcomb

            Debugger

            Introductions

               ReactiveConf2015 Talk - 30:00

               Introduction article

                 Introduction - 30:37

                 Get started - 31:15

                 Routing - 15:20

    );
  }
});

module.exports = Home;
<commit_msg>Added snippet on Cerebral and Redux<commit_after>var React = require('react');
var Header = require('./Header.js');

var Home = React.createClass({
  render: function () {
    return (

Cerebral

State

              Applications are stateful. The application needs to know what page to display,
              if a dropdown is opened or you are currently getting some data from the server. With
              Cerebral you define all the state of your application in one state tree. Think of it like
              a client database, but it holds all the data your applications needs, even if it is just for
              the client.

UI

              The UI is produced using the application state. It is passed to a render function or exposed
              to a template. When you want your UI to change, your application state has to change. Your application
              listens to UI events and uses Cerebral to execute the state update. After an update is complete
              the UI will render the new state of the application.

Express your application flow with signals

            Packages

 React

 Angular

 Baobab

 Tcomb

            Debugger

            Introductions

               window.open('https://gist.github.com/christianalfoni/e8dc5bfa79e7289a6258')}> Redux and Cerebral

               ReactiveConf2015 Talk - 30:00

               window.open('http://www.christianalfoni.com/articles/2015_09_22_Introducing-Cerebral')}> Introduction article

                 Introduction - 30:37

                 Get started - 31:15

                 Routing - 15:20

    );
  }
});

module.exports = Home;
<|endoftext|>