<commit_before>/*
 * ComunioPlusPlus DE
 *
 * Copyright (c) 2012, Florian Schäfer <florian.schaefer@gmail.com>
 * All rights reserved
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 */

function normalizeName(name) {
    return name.match(/(?:\w\.\s)?(\w*)/)[1];
}

function get(urls, done, fail) {
    var done = done || $.noop,
        fail = fail || $.noop,
        responses = {},
        requests = urls.map(function (url, i) {
            return $.get(url, function (response) { responses[i] = response });
        });

    function makeArray(obj) {
        var result = [];
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                result[prop] = obj[prop];
            }
        }
        return result;
    }

    $.when.apply(null, requests)
    .done(function () {
        done(makeArray(responses));
    })
    .fail(function () {
        fail();
    });
}

function addInjury($targetTr, data, name, club, done) {
    var done = done || $.noop,
        tr = $(data).find('td:contains("' + name + '"):first').closest('tr'),
        $tr = $(tr),
        injured = !!( $tr.find('td img[title="' + club + '"]').length ),
        injury = $tr.find('>td:eq(4)').text(),
        until = $tr.find('>td:eq(6)').text(),

        $td = $('', { 'align': 'center', 'css': { 'width': '24px' } }).appendTo($targetTr);

    if (injured) {
        $('', {
            'src': chrome.extension.getURL('/images/redcross.gif'),
            'title': injury + ' - ' + until,
            'css': {
                'heigth': '16px',
                'width': '16px'
            }
        })
        .appendTo($td);
    }

    done();
}

function addStats($targetTr, name, club) {
    get([
        'http://stats.comunio.de/search.php?name=' + name
    ], function (data) {
        var $tr = $(data[0]).find('table tr td a:contains("' + name + '")').closest('tr').find('img[title="' + club + '"]').closest('tr'),
            $img = $tr.find('td img.trend').clone(),
            $td = $('', { 'align': 'center', 'css': { 'width': '24px' } }).appendTo($targetTr);
        $img.css({
            'background-image': "url(" + chrome.extension.getURL('/images/trends.png') + ")"
        })
        .appendTo($td);
    });
}

function forEachPlayer(callback) {
    $([
        '.tablebox tr'          /* player table in lineup.html */
      , '.tablecontent03 tr'    /* player table in exchangemarket.phtml */
    ].join(','))
    .each(function () {
        var $tr = $(this),
            name = $(this).find('td:eq(0)').text().replace(/[\*\s]/g, ""),    /* player name */
            club = $(this).find('span.clubimg').attr("title");    /* club name */
			console.log("name"+name);
			console.log("club"+club);
        callback($tr, name, club);
    });
}

function formatNumber(number) {
    var number = number + '',
        x = number.split('.'),
        x1 = x[0],
        x2 = x.length > 1 ? '.' + x[1] : '',
            regexp = /(\d+)(\d{3})/;
    while (regexp.test(x1)) {
        x1 = x1.replace(regexp, '$1' + '.' + '$2');
    }
    return x1 + x2;
}

function normalizeNumber(number) {
    return parseInt(number.split('.').join(''));
}

function calculateBids() {
    var bids = 0;
    $('.tablecontent03 input[onkeyup]').each(function () {
        var value = $(this).val();

        if (value) {
            bids += normalizeNumber(value);
        }
    });
    return bids;
}

function getBudget() {
    return normalizeNumber($('#userbudget').text().match(/[?\.\d]+/)[0]);
}

function doCalculation() {
    var $calculation = $('#calculation');
    if ($calculation.length === 0) {
        $calculation = $('#title').clone().attr('id', 'calculation').addClass('contenttext').empty().insertAfter('#title');
    }

    var budget = getBudget(),
        bids = calculateBids(),
        newBalance = budget - bids;

    $calculation.html([
        '
' + formatNumber(budget) + '
'
      , '
-' + formatNumber(bids) + '
'
      , '
=' + formatNumber(newBalance) + '
'
    ].join(''));
}

var currentLocation = document.location.href;

if ( currentLocation.match("http://www.comunio.de/lineup.phtml")
  || currentLocation.match("http://www.comunio.de/exchangemarket.phtml") ) {

    get([
        'http://www.transfermarkt.de/1-bundesliga/verletztespieler/wettbewerb/L1'
    ], function (injuryData) {
        forEachPlayer(function ($tr, name, club) {
            addInjury($tr, injuryData[0], normalizeName(name), club, function () {
                addStats($tr, name, club);
            });
        });
    });

}

if (currentLocation.match("http://www.comunio.de/exchangemarket.phtml")) {

    doCalculation();

    $('.tablecontent03 input[onkeyup]')
    .on('keyup, click, blur', function () {
        doCalculation();
    });

}

<commit_msg>args. missed logs<commit_after>/*
 * ComunioPlusPlus DE
 *
 * Copyright (c) 2012, Florian Schäfer <florian.schaefer@gmail.com>
 * All rights reserved
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 */

function normalizeName(name) {
    return name.match(/(?:\w\.\s)?(\w*)/)[1];
}

function get(urls, done, fail) {
    var done = done || $.noop,
        fail = fail || $.noop,
        responses = {},
        requests = urls.map(function (url, i) {
            return $.get(url, function (response) { responses[i] = response });
        });

    function makeArray(obj) {
        var result = [];
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                result[prop] = obj[prop];
            }
        }
        return result;
    }

    $.when.apply(null, requests)
    .done(function () {
        done(makeArray(responses));
    })
    .fail(function () {
        fail();
    });
}

function addInjury($targetTr, data, name, club, done) {
    var done = done || $.noop,
        tr = $(data).find('td:contains("' + name + '"):first').closest('tr'),
        $tr = $(tr),
        injured = !!( $tr.find('td img[title="' + club + '"]').length ),
        injury = $tr.find('>td:eq(4)').text(),
        until = $tr.find('>td:eq(6)').text(),

        $td = $('', { 'align': 'center', 'css': { 'width': '24px' } }).appendTo($targetTr);

    if (injured) {
        $('', {
            'src': chrome.extension.getURL('/images/redcross.gif'),
            'title': injury + ' - ' + until,
            'css': {
                'heigth': '16px',
                'width': '16px'
            }
        })
        .appendTo($td);
    }

    done();
}

function addStats($targetTr, name, club) {
    get([
        'http://stats.comunio.de/search.php?name=' + name
    ], function (data) {
        var $tr = $(data[0]).find('table tr td a:contains("' + name + '")').closest('tr').find('img[title="' + club + '"]').closest('tr'),
            $img = $tr.find('td img.trend').clone(),
            $td = $('', { 'align': 'center', 'css': { 'width': '24px' } }).appendTo($targetTr);
        $img.css({
            'background-image': "url(" + chrome.extension.getURL('/images/trends.png') + ")"
        })
        .appendTo($td);
    });
}

function forEachPlayer(callback) {
    $([
        '.tablebox tr'          /* player table in lineup.html */
      , '.tablecontent03 tr'    /* player table in exchangemarket.phtml */
    ].join(','))
    .each(function () {
        var $tr = $(this),
            name = $(this).find('td:eq(0)').text().replace(/[\*\s]/g, ""),    /* player name */
            club = $(this).find('span.clubimg').attr("title");    /* club name */
        callback($tr, name, club);
    });
}

function formatNumber(number) {
    var number = number + '',
        x = number.split('.'),
        x1 = x[0],
        x2 = x.length > 1 ? '.' + x[1] : '',
            regexp = /(\d+)(\d{3})/;
    while (regexp.test(x1)) {
        x1 = x1.replace(regexp, '$1' + '.' + '$2');
    }
    return x1 + x2;
}

function normalizeNumber(number) {
    return parseInt(number.split('.').join(''));
}

function calculateBids() {
    var bids = 0;
    $('.tablecontent03 input[onkeyup]').each(function () {
        var value = $(this).val();

        if (value) {
            bids += normalizeNumber(value);
        }
    });
    return bids;
}

function getBudget() {
    return normalizeNumber($('#userbudget').text().match(/[?\.\d]+/)[0]);
}

function doCalculation() {
    var $calculation = $('#calculation');
    if ($calculation.length === 0) {
        $calculation = $('#title').clone().attr('id', 'calculation').addClass('contenttext').empty().insertAfter('#title');
    }

    var budget = getBudget(),
        bids = calculateBids(),
        newBalance = budget - bids;

    $calculation.html([
        '
' + formatNumber(budget) + '
'
      , '
-' + formatNumber(bids) + '
'
      , '
=' + formatNumber(newBalance) + '
'
    ].join(''));
}

var currentLocation = document.location.href;

if ( currentLocation.match("http://www.comunio.de/lineup.phtml")
  || currentLocation.match("http://www.comunio.de/exchangemarket.phtml") ) {

    get([
        'http://www.transfermarkt.de/1-bundesliga/verletztespieler/wettbewerb/L1'
    ], function (injuryData) {
        forEachPlayer(function ($tr, name, club) {
            addInjury($tr, injuryData[0], normalizeName(name), club, function () {
                addStats($tr, name, club);
            });
        });
    });

}

if (currentLocation.match("http://www.comunio.de/exchangemarket.phtml")) {

    doCalculation();

    $('.tablecontent03 input[onkeyup]')
    .on('keyup, click, blur', function () {
        doCalculation();
    });

}

<|endoftext|>


<commit_before>/*jslint browser: true*/
/*global Tangram, gui */

map = (function () {
    'use strict';

    var map_start_location = [40.70531887544228, -74.00976419448853, 15]; // NYC

    /*** URL parsing ***/

    // leaflet-style URL hash pattern:
    // #[zoom],[lat],[lng]
    var url_hash = window.location.hash.slice(1, window.location.hash.length).split('/');

    if (url_hash.length == 3) {
        map_start_location = [url_hash[1],url_hash[2], url_hash[0]];
        // convert from strings
        map_start_location = map_start_location.map(Number);
    }

    /*** Map ***/

    var map = L.map('map',
        {"keyboardZoomOffset" : .05}
    );

    var layer = Tangram.leafletLayer({
        scene: 'scene.yaml',
        attribution: 'Tangram | © OSM contributors | Mapzen'
    });

    var rasterLayer = L.tileLayer('http://d.tile.stamen.com/terrain-background/{z}/{x}/{y}.jpg');

    window.layer = layer;
    var scene = layer.scene;
    window.scene = scene;

    // setView expects format ([lat, long], zoom)
    map.setView(map_start_location.slice(0, 3), map_start_location[2]);

    var hash = new L.Hash(map);

    /***** Render loop *****/

    window.addEventListener('load', function () {
        // Scene initialized
        layer.on('init', function() {
        });
        rasterLayer.addTo(map);
        layer.addTo(map);

    });

    return map;

}());
<commit_msg>added noaa seal level rise layers from 0 to 6 ft and added up and down functions to switch sea level rise<commit_after>/*jslint browser: true*/
/*global Tangram, gui */

map = (function () {
    'use strict';

    var map_start_location = [40.70531887544228, -74.00976419448853, 15]; // NYC

    /*** URL parsing ***/

    // leaflet-style URL hash pattern:
    // #[zoom],[lat],[lng]
    var url_hash = window.location.hash.slice(1, window.location.hash.length).split('/');

    if (url_hash.length == 3) {
        map_start_location = [url_hash[1],url_hash[2], url_hash[0]];
        // convert from strings
        map_start_location = map_start_location.map(Number);
    }

    /*** Map ***/

    var map = L.map('map',
        {"keyboardZoomOffset" : .05}
    );

    var layer = Tangram.leafletLayer({
        scene: 'scene.yaml',
        attribution: 'Tangram | © OSM contributors | Mapzen'
    });

    var currentSlr = 4;
    var slrLayers = {
                        slr_0ft: L.tileLayer('https://www.coast.noaa.gov/arcgis/rest/services/dc_slr/slr_0ft/MapServer/tile/{z}/{y}/{x}'),
                        slr_1ft: L.tileLayer('https://www.coast.noaa.gov/arcgis/rest/services/dc_slr/slr_1ft/MapServer/tile/{z}/{y}/{x}'),
                        slr_2ft: L.tileLayer('https://www.coast.noaa.gov/arcgis/rest/services/dc_slr/slr_2ft/MapServer/tile/{z}/{y}/{x}'),
                        slr_3ft: L.tileLayer('https://www.coast.noaa.gov/arcgis/rest/services/dc_slr/slr_3ft/MapServer/tile/{z}/{y}/{x}'),
                        slr_4ft: L.tileLayer('https://www.coast.noaa.gov/arcgis/rest/services/dc_slr/slr_4ft/MapServer/tile/{z}/{y}/{x}'),
                        slr_5ft: L.tileLayer('https://www.coast.noaa.gov/arcgis/rest/services/dc_slr/slr_5ft/MapServer/tile/{z}/{y}/{x}'),
                        slr_6ft: L.tileLayer('https://www.coast.noaa.gov/arcgis/rest/services/dc_slr/slr_6ft/MapServer/tile/{z}/{y}/{x}')
                     };

    window.layer = layer;
    var scene = layer.scene;
    window.scene = scene;

    // setView expects format ([lat, long], zoom)
    map.setView(map_start_location.slice(0, 3), map_start_location[2]);

    var hash = new L.Hash(map);

    /***** Render loop *****/

    window.addEventListener('load', function () {
        // Scene initialized
        layer.on('init', function() {
        });
        slrLayers["slr_"+currentSlr+"ft"].addTo(map);
        layer.addTo(map);

    });

    window.slrUp = function() {
        if(currentSlr + 1 <= 6){
            map.removeLayer(slrLayers["slr_"+currentSlr+"ft"]);
            currentSlr += 1;
            slrLayers["slr_"+currentSlr+"ft"].addTo(map);
        }
    }

    window.slrDown = function() {
        if(currentSlr - 1 >=0 ){
            map.removeLayer(slrLayers["slr_"+currentSlr+"ft"]);
            currentSlr -= 1;
            slrLayers["slr_"+currentSlr+"ft"].addTo(map);
        }

    }

    return map;

}());
<|endoftext|>


<commit_before>const projData = [{"project":"The virus’s tale","url":"https://apps.bostonglobe.com/metro/graphics/2020/05/coronavirus-tale/","org":"The Boston Globe","img":"coronavirus-tale-og.jpg"},
{"project":"Coronavirus deaths in Mass. are likely far higher than what’s been reported. Here’s why","url":"https://www.bostonglobe.com/2020/04/22/nation/official-toll-massachusetts-coronavirus-deaths-likely-undercounted-review-shows/","org":"The Boston Globe","img":"https://bostonglobe-prod.cdn.arcpublishing.com/resizer/6vUM1pxTNJi5-uV8OpZrqlUckYI=/1440x0/arc-anglerfish-arc2-prod-bostonglobe.s3.amazonaws.com/public/TFUVTTODH6XSVP6AVDGY5X5AMU.jpg"},
{"project":"How overwhelmed could New England hospitals become? Explore maps of different scenarios","url":"https://www.bostonglobe.com/2020/03/26/nation/how-overwhelmed-could-new-england-hospitals-become-explore-maps-different-scenarios/","org":"The Boston Globe","img":"https://bostonglobe-prod.cdn.arcpublishing.com/resizer/It_ElsukaYmpbH312-Anx_cHbhg=/1440x0/arc-anglerfish-arc2-prod-bostonglobe.s3.amazonaws.com/public/CBSAPTBWBBHLRHUHNFINSKFRFY.jpg"},
{"project":"Seeing Red: A Spotlight report on traffic and congestion in metro Boston ","url":"https://apps.bostonglobe.com/metro/investigations/spotlight/2019/11/19/seeing-red/","org":"The Boston Globe","img":"https://apps.bostonglobe.com/metro/investigations/spotlight/2019/11/19/seeing-red/assets/images/optimized/ryantrafficrush3spotlight_1280.jpg"},
{"project":"At the edge of a warming world","url":"https://apps.bostonglobe.com/metro/2019/09/26/cape-cod-climate-change/story/","org":"The Boston Globe","img":"https://apps.bostonglobe.com/metro/2019/09/26/cape-cod-climate-change/assets/images/landing_1280.jpg"},
{"project":"The Valedictorians Project: What became of Boston's brightest?","url":"https://apps.bostonglobe.com/magazine/graphics/2019/01/17/valedictorians/","org":"The Boston Globe","img":"landingcover_1200.jpg"},
{"project":"Who’s running for president in 2020?","url":"https://apps.bostonglobe.com/news/politics/graphics/2019/03/president-2020-candidates/","org":"The Boston Globe","img":"og-2020.jpg"},
{"project":"What the Mueller report says about Trump and more","url":"https://apps.bostonglobe.com/news/politics/graphics/2019/03/mueller-report/","org":"The Boston Globe","img":"report_topper.jpg"},
{"project":"Here’s how the Patriots Super Bowl roster evolved around Tom Brady","url":"https://apps.bostonglobe.com/sports/graphics/2019/02/03/superbowl-2019-patriots-roster-brady/","org":"The Boston Globe","img":"og.jpg"},
{"project":"Inside the secret courts of Massachusetts","url":"https://apps.bostonglobe.com/metro/investigations/spotlight/secret-courts-in-massachusetts/","org":"The Boston Globe","img":"secret_og_1200.jpg"},
{"project":"Gladiator: The story of Aaron Hernandez and Football Inc.","url":"https://apps.bostonglobe.com/spotlight/gladiator/","org":"The Boston Globe","img":"gladiator_og.jpg"},
{"project":"Elections 2018","url":"https://www3.bostonglobe.com/elections/2018/?arc404=true","org":"The Boston Globe","img":"https://apps.bostonglobe.com/elections/2018/assets/images/election_us_og.jpg"},
{"project":"What happened when Boston Public Schools tried for equity with an algorithm","url":"https://apps.bostonglobe.com/ideas/graphics/2018/09/equity-machine/","org":"The Boston Globe","img":"https://apps.bostonglobe.com/ideas/graphics/2018/09/equity-machine/assets/og.jpg"},
{"project":"Journalists are not the enemy","url":"https://apps.bostonglobe.com/opinion/graphics/2018/08/freepress/","org":"The Boston Globe","img":"https://apps.bostonglobe.com/opinion/graphics/2018/08/freepress/assets/freepress_og.jpg"},
{"project":"Traffic, crowding, cancellations: This MBTA route shows all the T’s bus problems","url":"https://apps.bostonglobe.com/metro/graphics/2018/08/111-bus/","org":"The Boston Globe","img":"https://apps.bostonglobe.com/metro/graphics/2018/08/111-bus/assets/img/bus_og_1200.jpg"},
{"project":"The case for ending Google’s Internet domination","url":"https://apps.bostonglobe.com/opinion/graphics/2018/06/break-google/","org":"The Boston Globe","img":"break.gif"},
{"project":"Doctors and sex abuse: How well does your state protect patients?","url":"https://doctors.ajc.com/states/","org":"Atlanta Journal-Constitution","img":"http://s3.amazonaws.com/ajc-doctors-images/Doc-stethoscope-intro_social.jpg"},
{"project":"Families, not job seekers, gain the most from legal immigration","url":"https://www.ajc.com/news/gen-politics/families-not-job-seekers-gain-the-most-from-legal-immigration/mMeYq4d9MuXy4d5TwCvOPJ/","org":"Atlanta Journal-Constitution","img":"https://www.ajc.com/rf/image_lowres/Pub/p8/MyAJC/2017/08/07/Images/green-cards_by-categ-460-460.png"},
{"project":"Which are Georgia's winning industries?","url":"https://www.ajc.com/business/which-are-georgia-winning-and-losing-job-industries-since-2000/8eIZ0Q6X1NfMjk3KEYSsZJ/","org":"Atlanta Journal-Constitution","img":"https://s3.amazonaws.com/ajc-staging-sites/georgia-industries/img/trade-all.jpg"},
{"project":"How Georgia’s Sixth Congressional District voted","url":"https://www.ajc.com/news/gen-politics/how-each-neighborhood-voted-the-6th-district-runoff/72ZamHK2RQLoVsvFhcfWjM/","org":"Atlanta Journal-Constitution","img":"map-precinct.jpg"},
{"project":"After Virginia, Georgia has the most Confederate symbols in the country","url":"https://www.ajc.com/news/state--regional-govt--politics/after-virginia-georgia-has-the-most-confederate-symbols-the-country/vZPnQ2kGSd9xgURzS5kJiJ/","org":"Atlanta Journal-Constitution","img":"https://www.ajc.com/rf/image_lowres/Pub/p8/AJC/2017/08/18/Images/newsEngin.19603242_030616_IN_stone1.jpg"},
{"project":"The Ultimate Atlanta School Guide","url":"https://schools.ajc.com/#/schools","org":"Atlanta Journal-Constitution","img":"https://schools.ajc.com/images/school.jpg"}];

const projects = d3.select('#code-projects')
  .selectAll('.portfolio-item')
  .data(projData)
  .join('div')
  .attr('class', 'portfolio-item');

const links = projects
  .append('a')
  .attr('href', d => d.url);

const divs = links
  .append('div')
  .attr('class', 'portfolio-item__img');

divs.append('img')
  .attr('src', d => d.img.indexOf('http') > -1 ? d.img :
    d.project.indexOf('Sixth Congressional') > -1
    ? 'assets/map-precinct.jpg' :
    `${d.url}assets/images/${d.img}`);

divs.append('h3')
  .attr('class', 'portfolio-item__title')
  .text(d => d.project);

divs.append('p')
  .attr('class', 'portfolio-item__org')
  .append('span')
  .text(d => d.org);<commit_msg>Added tsa<commit_after>const projData = [{"project":"The virus’s tale","url":"https://apps.bostonglobe.com/metro/graphics/2020/05/coronavirus-tale/","org":"The Boston Globe","img":"coronavirus-tale-og.jpg"},
{"project":"Coronavirus deaths in Mass. are likely far higher than what’s been reported. Here’s why","url":"https://www.bostonglobe.com/2020/04/22/nation/official-toll-massachusetts-coronavirus-deaths-likely-undercounted-review-shows/","org":"The Boston Globe","img":"https://bostonglobe-prod.cdn.arcpublishing.com/resizer/6vUM1pxTNJi5-uV8OpZrqlUckYI=/1440x0/arc-anglerfish-arc2-prod-bostonglobe.s3.amazonaws.com/public/TFUVTTODH6XSVP6AVDGY5X5AMU.jpg"},
{"project":"How overwhelmed could New England hospitals become? Explore maps of different scenarios","url":"https://www.bostonglobe.com/2020/03/26/nation/how-overwhelmed-could-new-england-hospitals-become-explore-maps-different-scenarios/","org":"The Boston Globe","img":"https://bostonglobe-prod.cdn.arcpublishing.com/resizer/It_ElsukaYmpbH312-Anx_cHbhg=/1440x0/arc-anglerfish-arc2-prod-bostonglobe.s3.amazonaws.com/public/CBSAPTBWBBHLRHUHNFINSKFRFY.jpg"},
{"project":"Seeing Red: A Spotlight report on traffic and congestion in metro Boston ","url":"https://apps.bostonglobe.com/metro/investigations/spotlight/2019/11/19/seeing-red/","org":"The Boston Globe","img":"https://apps.bostonglobe.com/metro/investigations/spotlight/2019/11/19/seeing-red/assets/images/optimized/ryantrafficrush3spotlight_1280.jpg"},
{"project":"At the edge of a warming world","url":"https://apps.bostonglobe.com/metro/2019/09/26/cape-cod-climate-change/story/","org":"The Boston Globe","img":"https://apps.bostonglobe.com/metro/2019/09/26/cape-cod-climate-change/assets/images/landing_1280.jpg"},
{"project":"The Valedictorians Project: What became of Boston's brightest?","url":"https://apps.bostonglobe.com/magazine/graphics/2019/01/17/valedictorians/","org":"The Boston Globe","img":"landingcover_1200.jpg"},
{"project":"Who’s running for president in 2020?","url":"https://apps.bostonglobe.com/news/politics/graphics/2019/03/president-2020-candidates/","org":"The Boston Globe","img":"og-2020.jpg"},
{"project":"What the Mueller report says about Trump and more","url":"https://apps.bostonglobe.com/news/politics/graphics/2019/03/mueller-report/","org":"The Boston Globe","img":"report_topper.jpg"},
{"project":"Here’s how the Patriots Super Bowl roster evolved around Tom Brady","url":"https://apps.bostonglobe.com/sports/graphics/2019/02/03/superbowl-2019-patriots-roster-brady/","org":"The Boston Globe","img":"og.jpg"},
{"project":"Inside the secret courts of Massachusetts","url":"https://apps.bostonglobe.com/metro/investigations/spotlight/secret-courts-in-massachusetts/","org":"The Boston Globe","img":"secret_og_1200.jpg"},
{"project":"Gladiator: The story of Aaron Hernandez and Football Inc.","url":"https://apps.bostonglobe.com/spotlight/gladiator/","org":"The Boston Globe","img":"gladiator_og.jpg"},
{"project":"Elections 2018","url":"https://www3.bostonglobe.com/elections/2018/?arc404=true","org":"The Boston Globe","img":"https://apps.bostonglobe.com/elections/2018/assets/images/election_us_og.jpg"},
{"project":"What happened when Boston Public Schools tried for equity with an algorithm","url":"https://apps.bostonglobe.com/ideas/graphics/2018/09/equity-machine/","org":"The Boston Globe","img":"https://apps.bostonglobe.com/ideas/graphics/2018/09/equity-machine/assets/og.jpg"},
{"project":"Journalists are not the enemy","url":"https://apps.bostonglobe.com/opinion/graphics/2018/08/freepress/","org":"The Boston Globe","img":"https://apps.bostonglobe.com/opinion/graphics/2018/08/freepress/assets/freepress_og.jpg"},
{"project":"TSA is tracking regular travelers like terrorists in secret surveillance","url":"https://apps.bostonglobe.com/news/nation/graphics/2018/07/tsa-quiet-skies/","org":"The Boston Globe","img":"https://apps.bostonglobe.com/news/nation/graphics/2018/07/tsa-quiet-skies/assets/img/airport_delay_1200.jpg"},
{"project":"Traffic, crowding, cancellations: This MBTA route shows all the T’s bus problems","url":"https://apps.bostonglobe.com/metro/graphics/2018/08/111-bus/","org":"The Boston Globe","img":"https://apps.bostonglobe.com/metro/graphics/2018/08/111-bus/assets/img/bus_og_1200.jpg"},
{"project":"The case for ending Google’s Internet domination","url":"https://apps.bostonglobe.com/opinion/graphics/2018/06/break-google/","org":"The Boston Globe","img":"break.gif"},
{"project":"Doctors and sex abuse: How well does your state protect patients?","url":"https://doctors.ajc.com/states/","org":"Atlanta Journal-Constitution","img":"http://s3.amazonaws.com/ajc-doctors-images/Doc-stethoscope-intro_social.jpg"},
{"project":"Families, not job seekers, gain the most from legal immigration","url":"https://www.ajc.com/news/gen-politics/families-not-job-seekers-gain-the-most-from-legal-immigration/mMeYq4d9MuXy4d5TwCvOPJ/","org":"Atlanta Journal-Constitution","img":"https://www.ajc.com/rf/image_lowres/Pub/p8/MyAJC/2017/08/07/Images/green-cards_by-categ-460-460.png"},
{"project":"Which are Georgia's winning industries?","url":"https://www.ajc.com/business/which-are-georgia-winning-and-losing-job-industries-since-2000/8eIZ0Q6X1NfMjk3KEYSsZJ/","org":"Atlanta Journal-Constitution","img":"https://s3.amazonaws.com/ajc-staging-sites/georgia-industries/img/trade-all.jpg"},
{"project":"How Georgia’s Sixth Congressional District voted","url":"https://www.ajc.com/news/gen-politics/how-each-neighborhood-voted-the-6th-district-runoff/72ZamHK2RQLoVsvFhcfWjM/","org":"Atlanta Journal-Constitution","img":"map-precinct.jpg"},
{"project":"After Virginia, Georgia has the most Confederate symbols in the country","url":"https://www.ajc.com/news/state--regional-govt--politics/after-virginia-georgia-has-the-most-confederate-symbols-the-country/vZPnQ2kGSd9xgURzS5kJiJ/","org":"Atlanta Journal-Constitution","img":"https://www.ajc.com/rf/image_lowres/Pub/p8/AJC/2017/08/18/Images/newsEngin.19603242_030616_IN_stone1.jpg"},
{"project":"The Ultimate Atlanta School Guide","url":"https://schools.ajc.com/#/schools","org":"Atlanta Journal-Constitution","img":"https://schools.ajc.com/images/school.jpg"}];

const projects = d3.select('#code-projects')
  .selectAll('.portfolio-item')
  .data(projData)
  .join('div')
  .attr('class', 'portfolio-item');

const links = projects
  .append('a')
  .attr('href', d => d.url);

const divs = links
  .append('div')
  .attr('class', 'portfolio-item__img');

divs.append('img')
  .attr('src', d => d.img.indexOf('http') > -1 ? d.img :
    d.project.indexOf('Sixth Congressional') > -1
    ? 'assets/map-precinct.jpg' :
    `${d.url}assets/images/${d.img}`);

divs.append('h3')
  .attr('class', 'portfolio-item__title')
  .text(d => d.project);

divs.append('p')
  .attr('class', 'portfolio-item__org')
  .append('span')
  .text(d => d.org);<|endoftext|>


<commit_before>
/* Dasca, an idle game by @CylonicRaider (github.com/CylonicRaider/dasca)
 * Game implementation */

'use strict';

/* *** Utilities *** */

function $id(id) {
  return document.getElementById(id);
}
function $idx(id, elem) {
  return elem.querySelector("#" + id);
}
function $sel(sel, elem) {
  return (elem || document).querySelector(sel);
}
function $selAll(sel, elem) {
  return (elem || document).querySelectorAll(sel);
}

/* Create a DOM element */
function $makeNode(tag, className, attrs, children) {
  /* Allow omitting parameters */
  if (Array.isArray(className)) {
    if (! attrs && ! children) {
      children = className;
      attrs = null;
      className = null;
    }
  } else if (typeof className == "object" && className != null) {
    if (! children) {
      children = attrs;
      attrs = className;
      className = null;
    }
  } else if (Array.isArray(attrs) || typeof attrs == "string") {
    if (! children) {
      children = attrs;
      attrs = null;
    }
  }
  /* Create node */
  var ret = document.createElement(tag);
  /* Set classes */
  if (className) ret.className = className;
  /* Set additional attributes */
  if (attrs) {
    for (var name in attrs) {
      if (! attrs.hasOwnProperty(name)) continue;
      ret.setAttribute(name, attrs[name]);
    }
  }
  /* Add children */
  if (children) {
    if (typeof children == "string") children = [children];
    for (var i = 0; i < children.length; i++) {
      var e = children[i];
      if (! e) {
        /* Allow conditional node omission */
      } else if (typeof e == "string") {
        /* Strings become text nodes */
        ret.appendChild(document.createTextNode(e));
      } else if (typeof e != "object") {
        /* Other primitive types are not allowed */
        throw new Error("Bad child encountered during DOM node creation");
      } else if (Array.isArray(e)) {
        /* Arrays are handled recursively */
        ret.appendChild($makeNode.apply(null, e));
      } else {
        /* Everything else is assumed to be a DOM node */
        ret.appendChild(e);
      }
    }
  }
  return ret;
}

/* Show the given UI element, hiding any siblings and showing all its
 * showable parents */
function showNode(node) {
  if (! node) return;
  /* Resolve ID-s */
  if (typeof node == "string") node = $id(node);
  /* Hide siblings */
  var prev = node.previousElementSibling, next = node.nextElementSibling;
  while (prev) {
    if (prev.classList.contains("selectable"))
      prev.classList.remove("selected");
    prev = prev.previousElementSibling;
  }
  while (next) {
    if (next.classList.contains("selectable"))
      next.classList.remove("selected");
    next = next.nextElementSibling;
  }
  /* Show parent */
  showNode(node.parentNode);
  /* Show node */
  if (node.classList && node.classList.contains("selectable"))
      node.classList.add("selected");
}

/* Hide all selectable children of node
 * Approximate opposite of showNode. */
function hideNodes(node) {
  if (! node) return;
  /* Resolve ID-s */
  if (typeof node == "string") node = $id("node");
  /* Hide children */
  for (var ch = node.firstElementChild; ch; ch = ch.nextElementSibling) {
    if (ch.classList.contains("selectable"))
      ch.classList.remove("selected");
  }
}

/* *** Game mechanics *** */

/* A value changing over time
 * A variable has a value, and a set of handlers which are invoked on every
 * update of it that return increments which are applied to the variable
 * cumulatively.
 * Handlers are objects whose "cb" method is called with three parameters:
 * variable: The Variable object.
 * delta   : The time difference since the last update.
 * now     : The current time.
 * The return values are differences whose sum is added to the value of the
 * variable after each update.
 * If the handler has a rate property, the callback is not invoked, and
 * the change is instead calculated as the product of the time passed and
 * the rate. */
function Variable(value) {
  this.value = value;
  this.handlers = [];
  this.lastUpdate = null;
}

Variable.prototype = {
  /* Aggregate the results of the handlers' return values and increment the
   * value by them. */
  update: function(now) {
    if (this.lastUpdate == null) this.lastUpdate = now;
    var delta = now - this.lastUpdate, incr = 0, hnd = this.handlers;
    for (var i = 0; i < hnd.length; i++) {
      if (hdn[i].rate != null) {
        incr += hnd[i].rate * delta;
      } else {
        incr += hnd[i].cb(this, delta, now);
      }
    }
    this.value += incr;
  },

  /* OOP */
  constructor: Variable
};

/* The main game object */
function Game(state) {
  this._env = Object.create(window);
  this._env.game = this;
  if (state == null) {
    this.state = new GameState(this);
  } else {
    this.state = deserialize(state, this._env);
  }
  this.ui = new GameUI(this);
  this.running = true;
}

Game.prototype = {
  /* Save the game state into a string */
  save: function() {
    return serialize(this.state);
  },

  /* Mount the game into the given node */
  mount: function(node) {
    this.ui.mount(node);
  },

  /* Start the game */
  start: function() {
    this.addTab("start", "Cockpit", {hidden: true});
    var intro = [[["i", null, "Darkness."], 1],
                 [["i", null, "Silence."], 3],
                 [["i", null, "Confinement."], 5],
                 [["i", null, "Amnesia."], 8]];
    intro.forEach(function(x) {
      this.addTask(x[1], "showMessage", x[0]);
    }, this);
    this.addTask(10, "showTab", "start");
  },

  /* Get the value of a flag */
  getFlag: function(name) {
    return this.state.flags[name];
  },

  /* Set a flag; return whether the value changed */
  setFlag: function(name) {
    var old = this.state.flags[name];
    this.state.flags[name] = true;
    return (! old);
  },

  /* Clear a flag; return whether the value changes */
  clearFlag: function(name) {
    var old = this.state.flags[name];
    this.state.flags[name] = false;
    return (!! old);
  },

  /* Schedule an Action to be run */
  addTaskEx: function(delay, subject, method, args) {
    var task = new Action(subject, method, args, this._env);
    return this.state.scheduler.addTaskIn(task, delay);
  },

  /* Convenience wrapper for addTaskEx()
   * Arguments are passed variadically. */
  addTask: function(delay, method) {
    var m = ("game." + method).match(/^(.+)\.([^.]+)$/);
    var args = Array.prototype.slice.call(arguments, 2);
    return this.addTaskEx(delay, m[1], m[2], args);
  },

  /* Show a log message */
  showMessage: function(msg) {
    this.state.messages.push(msg);
    this.ui._showMessage(msg);
  },

  /* Add a UI tab */
  addTab: function(name, dispname, options) {
    if (! options) options = {};
    options.name = dispname;
    if (this.state.tabOrder.indexOf(name) == -1)
      this.state.tabOrder.push(name);
    this.state.tabs[name] = options;
    this.ui._addTab(name, dispname, options);
  },

  /* Select a UI tab */
  showTab: function(name, hidden, noShow) {
    if (hidden != null) this.state.tabs[name].hidden = hidden;
    this.ui._showTab(name, this.state.tabs[name].hidden, noShow);
  },

  /* Stop running the game */
  exit: function() {
    this.running = false;
  },

  /* OOP */
  constructor: Game
};

/* The (serializable) state of a game
 * The constructor creates a new state; for restoring a saved one, use the
 * deserialization function (in a suitable environment, which is created by
 * the constructor of Game). */
function GameState(game) {
  this.scheduler = Scheduler.makeStrobe(100);
  // {string -> bool}. Can be used to show one-off messages.
  this.flags = {};
  // [string]. Stores log messages.
  this.messages = [];
  // {string -> object}. Name is the codename of a tab; value contains the
  // display name of the tab (as "name") and, optionally, whether its button
  // should not be displayed.
  this.tabs = {};
  // [string] The order in which the tab buttons should be arranged.
  this.tabOrder = [];
  // string. Contains the codename of the current tab, or null for none.
  this.currentTab = null;
  this._game = game;
}

GameState.prototype = {
  /* OOP */
  constructor: GameState,

  /* Deserialization */
  __restore__: function(obj, env) {
    /* Let unset fields fall back to at least something */
    var ret = new GameState(env.game);
    for (var key in obj) {
      if (! /^_/.test(key) && obj.hasOwnProperty(key)) ret[key] = obj[key];
    }
    return ret;
  }
};

/* The DOM-based user interface of the game */
function GameUI(game) {
  this.game = game;
  this.root = null;
  this.parent = null;
  this._tabs = {};
  this._tabButtons = {};
}

GameUI.prototype = {
  /* Produce the DOM tree corresponding to this object */
  render: function() {
    if (this.root == null) {
      this.root = $makeNode("div", {id: "game-content"}, [
        ["div", "row row-all", [
          ["div", "col col-quarter inset", [
            ["div", {id: "messagebar"}]
          ]],
          ["div", "col col-all", [
            ["div", "row row-small row-btn vinset", {id: "tabbar"}],
            ["div", "row row-all pane", {id: "mainpane"}]
          ]]
        ]],
        ["div", "row row-small inset", {id: "bottombar"}, [
          ["button", "btn btn-small dim", {id: "credits-game"}, "Credits"],
          ["div", "col col-all"],
          ["button", "btn btn-small", {id: "exit-game"}, "Exit"]
        ]]
      ]);
      $idx("exit-game", this.root).addEventListener("click", function() {
        this.game.exit();
        showNode("titlescreen");
      }.bind(this));
      $idx("credits-game", this.root).addEventListener("click", function() {
        showNode("creditscreen");
      });
      var state = this.game.state;
      if (state.messages.length) {
        var m = state.messages;
        for (var i = 0; i < m.length; i++)
          this._showMessage(m[i]);
      }
      for (var key in state.tabs) {
        if (! state.tabs.hasOwnProperty(key)) continue;
        this._addTab(key, state.tabs[key].name, state.tabs[key]);
      }
    }
    return this.root;
  },

  /* Embed the game's UI into the given DOM node
   * If not already done, the UI is constructed. */
  mount: function(parent) {
    this.parent = parent;
    parent.appendChild(this.render());
  },

  /* Message showing backend */
  _showMessage: function(text) {
    var msgnode = $makeNode("p", "log-message", [text]);
    var msgbar = $idx("messagebar", this.root);
    msgbar.appendChild(msgnode);
    msgbar.scrollTop = msgbar.scrollHeight;
  },

  /* Add a UI tab */
  _addTab: function(name, dispname, options) {
    if (! options) options = {};
    this._tabButtons[name] = $makeNode("button",
      "btn btn-small fade-in-fast", {id: "tabbtn-" + name}, [dispname]);
    this._tabButtons[name].addEventListener("click", function() {
      this.game.showTab(name);
    }.bind(this));
    var tabbar = $idx("tabbar", this.root);
    if (! options.hidden && ! tabbar.contains(this._tabButtons[name])) {
      $idx("tabbar", this.root).appendChild(this._tabButtons[name]);
      this._sortTabs();
    }
    this._tabs[name] = $makeNode("div", "selectable layer",
      {id: "tab-" + name});
    $idx("mainpane", this.root).appendChild(this._tabs[name]);
  },

  /* Show a UI tab */
  _showTab: function(name, hidden, noShow) {
    var tabbtn = this._tabButtons[name];
    var tabbar = $idx("tabbar", this.root);
    if (! hidden && ! tabbar.contains(tabbtn)) {
      tabbar.appendChild(tabbtn);
      this._sortTabs();
    }
    if (! noShow)
      showNode(this._tabs[name]);
  },

  /* Ensure the UI tab buttons are in the correct order */
  _sortTabs: function() {
    var indices = {}, order = this.game.state.tabOrder;
    for (var i = 0; i < order.length; i++)
      indices[order[i]] = i + 1;
    var last = order.length + 1;
    var tabbar = $idx("tabbar", this.root);
    var nodes = Array.prototype.slice.call(tabbar.children);
    nodes.sort(function(a, b) {
      var ka = indices[a.id.replace(/^tabbtn-/, "")] || last;
      var kb = indices[b.id.replace(/^tabbtn-/, "")] || last;
      return (ka < kb) ? -1 : (ka > kb) ? 1 : 0;
    });
    nodes.forEach(function(el) {
      tabbar.appendChild(el);
    });
  },

  /* Consistency */
  constructor: GameUI
};

/* *** Initialization *** */

function init() {
  var game = null;
  $id("startgame").addEventListener("click", function() {
    game = new Game();
    game.mount($id("mainscreen"));
    game.start();
    showNode("mainscreen");
  });
  $id("credits-title").addEventListener("click", function() {
    showNode("creditscreen");
  });
  $id("back-credits").addEventListener("click", function() {
    if (game && game.running) {
      showNode("mainscreen");
    } else {
      showNode("titlescreen");
    }
  });
  showNode("titlescreen");
}

window.addEventListener("load", init);
<commit_msg>Add static API<commit_after>
/* Dasca, an idle game by @CylonicRaider (github.com/CylonicRaider/dasca)
 * Game implementation */

'use strict';

/* *** Utilities *** */

function $id(id) {
  return document.getElementById(id);
}
function $idx(id, elem) {
  return elem.querySelector("#" + id);
}
function $sel(sel, elem) {
  return (elem || document).querySelector(sel);
}
function $selAll(sel, elem) {
  return (elem || document).querySelectorAll(sel);
}

/* Create a DOM element */
function $makeNode(tag, className, attrs, children) {
  /* Allow omitting parameters */
  if (Array.isArray(className)) {
    if (! attrs && ! children) {
      children = className;
      attrs = null;
      className = null;
    }
  } else if (typeof className == "object" && className != null) {
    if (! children) {
      children = attrs;
      attrs = className;
      className = null;
    }
  } else if (Array.isArray(attrs) || typeof attrs == "string") {
    if (! children) {
      children = attrs;
      attrs = null;
    }
  }
  /* Create node */
  var ret = document.createElement(tag);
  /* Set classes */
  if (className) ret.className = className;
  /* Set additional attributes */
  if (attrs) {
    for (var name in attrs) {
      if (! attrs.hasOwnProperty(name)) continue;
      ret.setAttribute(name, attrs[name]);
    }
  }
  /* Add children */
  if (children) {
    if (typeof children == "string") children = [children];
    for (var i = 0; i < children.length; i++) {
      var e = children[i];
      if (! e) {
        /* Allow conditional node omission */
      } else if (typeof e == "string") {
        /* Strings become text nodes */
        ret.appendChild(document.createTextNode(e));
      } else if (typeof e != "object") {
        /* Other primitive types are not allowed */
        throw new Error("Bad child encountered during DOM node creation");
      } else if (Array.isArray(e)) {
        /* Arrays are handled recursively */
        ret.appendChild($makeNode.apply(null, e));
      } else {
        /* Everything else is assumed to be a DOM node */
        ret.appendChild(e);
      }
    }
  }
  return ret;
}

/* Show the given UI element, hiding any siblings and showing all its
 * showable parents */
function showNode(node) {
  if (! node) return;
  /* Resolve ID-s */
  if (typeof node == "string") node = $id(node);
  /* Hide siblings */
  var prev = node.previousElementSibling, next = node.nextElementSibling;
  while (prev) {
    if (prev.classList.contains("selectable"))
      prev.classList.remove("selected");
    prev = prev.previousElementSibling;
  }
  while (next) {
    if (next.classList.contains("selectable"))
      next.classList.remove("selected");
    next = next.nextElementSibling;
  }
  /* Show parent */
  showNode(node.parentNode);
  /* Show node */
  if (node.classList && node.classList.contains("selectable"))
      node.classList.add("selected");
}

/* Hide all selectable children of node
 * Approximate opposite of showNode. */
function hideNodes(node) {
  if (! node) return;
  /* Resolve ID-s */
  if (typeof node == "string") node = $id("node");
  /* Hide children */
  for (var ch = node.firstElementChild; ch; ch = ch.nextElementSibling) {
    if (ch.classList.contains("selectable"))
      ch.classList.remove("selected");
  }
}

/* *** Game mechanics *** */

/* A value changing over time
 * A variable has a value, and a set of handlers which are invoked on every
 * update of it that return increments which are applied to the variable
 * cumulatively.
 * Handlers are objects whose "cb" method is called with three parameters:
 * variable: The Variable object.
 * delta   : The time difference since the last update.
 * now     : The current time.
 * The return values are differences whose sum is added to the value of the
 * variable after each update.
 * If the handler has a rate property, the callback is not invoked, and
 * the change is instead calculated as the product of the time passed and
 * the rate. */
function Variable(value) {
  this.value = value;
  this.handlers = [];
  this.lastUpdate = null;
}

Variable.prototype = {
  /* Aggregate the results of the handlers' return values and increment the
   * value by them. */
  update: function(now) {
    if (this.lastUpdate == null) this.lastUpdate = now;
    var delta = now - this.lastUpdate, incr = 0, hnd = this.handlers;
    for (var i = 0; i < hnd.length; i++) {
      if (hdn[i].rate != null) {
        incr += hnd[i].rate * delta;
      } else {
        incr += hnd[i].cb(this, delta, now);
      }
    }
    this.value += incr;
  },

  /* OOP */
  constructor: Variable
};

/* The main game object */
function Game(state) {
  this._env = Object.create(window);
  this._env.game = this;
  if (state == null) {
    this.state = new GameState(this);
  } else {
    this.state = deserialize(state, this._env);
  }
  this.ui = new GameUI(this);
  this.running = true;
}

Game.prototype = {
  /* Save the game state into a string */
  save: function() {
    return serialize(this.state);
  },

  /* Mount the game into the given node */
  mount: function(node) {
    this.ui.mount(node);
  },

  /* Start the game */
  start: function() {
    this.addTab("start", "Cockpit", {hidden: true});
    var intro = [[["i", null, "Darkness."], 1],
                 [["i", null, "Silence."], 3],
                 [["i", null, "Confinement."], 5],
                 [["i", null, "Amnesia."], 8]];
    intro.forEach(function(x) {
      this.addTask(x[1], "showMessage", x[0]);
    }, this);
    this.addTask(10, "showTab", "start");
  },

  /* Get the value of a flag */
  getFlag: function(name) {
    return this.state.flags[name];
  },

  /* Set a flag; return whether the value changed */
  setFlag: function(name) {
    var old = this.state.flags[name];
    this.state.flags[name] = true;
    return (! old);
  },

  /* Clear a flag; return whether the value changes */
  clearFlag: function(name) {
    var old = this.state.flags[name];
    this.state.flags[name] = false;
    return (!! old);
  },

  /* Schedule an Action to be run */
  addTaskEx: function(delay, subject, method, args) {
    var task = new Action(subject, method, args, this._env);
    return this.state.scheduler.addTaskIn(task, delay);
  },

  /* Convenience wrapper for addTaskEx()
   * Arguments are passed variadically. */
  addTask: function(delay, method) {
    var m = ("game." + method).match(/^(.+)\.([^.]+)$/);
    var args = Array.prototype.slice.call(arguments, 2);
    return this.addTaskEx(delay, m[1], m[2], args);
  },

  /* Show a log message */
  showMessage: function(msg) {
    this.state.messages.push(msg);
    this.ui._showMessage(msg);
  },

  /* Add a UI tab */
  addTab: function(name, dispname, options) {
    if (! options) options = {};
    options.name = dispname;
    if (this.state.tabOrder.indexOf(name) == -1)
      this.state.tabOrder.push(name);
    this.state.tabs[name] = options;
    this.ui._addTab(name, dispname, options);
  },

  /* Select a UI tab */
  showTab: function(name, hidden, noShow) {
    if (hidden != null) this.state.tabs[name].hidden = hidden;
    this.ui._showTab(name, this.state.tabs[name].hidden, noShow);
  },

  /* Stop running the game */
  exit: function() {
    this.running = false;
  },

  /* OOP */
  constructor: Game
};

/* The (serializable) state of a game
 * The constructor creates a new state; for restoring a saved one, use the
 * deserialization function (in a suitable environment, which is created by
 * the constructor of Game). */
function GameState(game) {
  this.scheduler = Scheduler.makeStrobe(100);
  // {string -> bool}. Can be used to show one-off messages.
  this.flags = {};
  // [string]. Stores log messages.
  this.messages = [];
  // {string -> object}. Name is the codename of a tab; value contains the
  // display name of the tab (as "name") and, optionally, whether its button
  // should not be displayed.
  this.tabs = {};
  // [string] The order in which the tab buttons should be arranged.
  this.tabOrder = [];
  // string. Contains the codename of the current tab, or null for none.
  this.currentTab = null;
  this._game = game;
}

GameState.prototype = {
  /* OOP */
  constructor: GameState,

  /* Deserialization */
  __restore__: function(obj, env) {
    /* Let unset fields fall back to at least something */
    var ret = new GameState(env.game);
    for (var key in obj) {
      if (! /^_/.test(key) && obj.hasOwnProperty(key)) ret[key] = obj[key];
    }
    return ret;
  }
};

/* The DOM-based user interface of the game */
function GameUI(game) {
  this.game = game;
  this.root = null;
  this.parent = null;
  this._tabs = {};
  this._tabButtons = {};
}

GameUI.prototype = {
  /* Produce the DOM tree corresponding to this object */
  render: function() {
    if (this.root == null) {
      this.root = $makeNode("div", {id: "game-content"}, [
        ["div", "row row-all", [
          ["div", "col col-quarter inset", [
            ["div", {id: "messagebar"}]
          ]],
          ["div", "col col-all", [
            ["div", "row row-small row-btn vinset", {id: "tabbar"}],
            ["div", "row row-all pane", {id: "mainpane"}]
          ]]
        ]],
        ["div", "row row-small inset", {id: "bottombar"}, [
          ["button", "btn btn-small dim", {id: "credits-game"}, "Credits"],
          ["div", "col col-all"],
          ["button", "btn btn-small", {id: "exit-game"}, "Exit"]
        ]]
      ]);
      $idx("exit-game", this.root).addEventListener("click", function() {
        this.game.exit();
        showNode("titlescreen");
      }.bind(this));
      $idx("credits-game", this.root).addEventListener("click", function() {
        showNode("creditscreen");
      });
      var state = this.game.state;
      if (state.messages.length) {
        var m = state.messages;
        for (var i = 0; i < m.length; i++)
          this._showMessage(m[i]);
      }
      for (var key in state.tabs) {
        if (! state.tabs.hasOwnProperty(key)) continue;
        this._addTab(key, state.tabs[key].name, state.tabs[key]);
      }
    }
    return this.root;
  },

  /* Embed the game's UI into the given DOM node
   * If not already done, the UI is constructed. */
  mount: function(parent) {
    this.parent = parent;
    parent.appendChild(this.render());
  },

  /* Message showing backend */
  _showMessage: function(text) {
    var msgnode = $makeNode("p", "log-message", [text]);
    var msgbar = $idx("messagebar", this.root);
    msgbar.appendChild(msgnode);
    msgbar.scrollTop = msgbar.scrollHeight;
  },

  /* Add a UI tab */
  _addTab: function(name, dispname, options) {
    if (! options) options = {};
    this._tabButtons[name] = $makeNode("button",
      "btn btn-small fade-in-fast", {id: "tabbtn-" + name}, [dispname]);
    this._tabButtons[name].addEventListener("click", function() {
      this.game.showTab(name);
    }.bind(this));
    var tabbar = $idx("tabbar", this.root);
    if (! options.hidden && ! tabbar.contains(this._tabButtons[name])) {
      $idx("tabbar", this.root).appendChild(this._tabButtons[name]);
      this._sortTabs();
    }
    this._tabs[name] = $makeNode("div", "selectable layer",
      {id: "tab-" + name});
    $idx("mainpane", this.root).appendChild(this._tabs[name]);
  },

  /* Show a UI tab */
  _showTab: function(name, hidden, noShow) {
    var tabbtn = this._tabButtons[name];
    var tabbar = $idx("tabbar", this.root);
    if (! hidden && ! tabbar.contains(tabbtn)) {
      tabbar.appendChild(tabbtn);
      this._sortTabs();
    }
    if (! noShow)
      showNode(this._tabs[name]);
  },

  /* Ensure the UI tab buttons are in the correct order */
  _sortTabs: function() {
    var indices = {}, order = this.game.state.tabOrder;
    for (var i = 0; i < order.length; i++)
      indices[order[i]] = i + 1;
    var last = order.length + 1;
    var tabbar = $idx("tabbar", this.root);
    var nodes = Array.prototype.slice.call(tabbar.children);
    nodes.sort(function(a, b) {
      var ka = indices[a.id.replace(/^tabbtn-/, "")] || last;
      var kb = indices[b.id.replace(/^tabbtn-/, "")] || last;
      return (ka < kb) ? -1 : (ka > kb) ? 1 : 0;
    });
    nodes.forEach(function(el) {
      tabbar.appendChild(el);
    });
  },

  /* Consistency */
  constructor: GameUI
};

/* *** Initialization *** */

var Dasca = {
  game: null
};

function init() {
  var game = null;
  $id("startgame").addEventListener("click", function() {
    game = new Game();
    Dasca.game = game;
    game.mount($id("mainscreen"));
    game.start();
    showNode("mainscreen");
  });
  $id("credits-title").addEventListener("click", function() {
    showNode("creditscreen");
  });
  $id("back-credits").addEventListener("click", function() {
    if (game && game.running) {
      showNode("mainscreen");
    } else {
      showNode("titlescreen");
    }
  });
  showNode("titlescreen");
}

window.addEventListener("load", init);
<|endoftext|>


<commit_before>const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow

const path = require('path')
const url = require('url')
const port = "9191"
const { exec } = require('child_process');
const cmdStr = ".\\R-Portable\\bin\\RScript.exe -e \"shiny::runApp('shinyApp.R', port="+port+")\"";
console.log(cmdStr);
exec(cmdStr, (error, stdout, stderr) => {
  console.log('running Shiny App');
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.log(`stderr: ${stderr}`);
});

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({webPreferences:{nodeIntegration:false},width: 800, height: 600})
console.log(process.cwd())
  mainWindow.loadURL('http://127.0.0.1:'+port)

  // and load the index.html of the app.
/*
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }))
*/
  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  console.log('EVENT::window-all-closed')
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<commit_msg>added loading graphic, and child_process, kill Rscript tasks so that the application cleans up the R Shiny Server and stops it from running<commit_after>const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow

const path = require('path')
const url = require('url')
const port = "9191"
const child = require('child_process');
//const cmdStr = ".\\R-Portable\\bin\\RScript.exe -e \"shiny::runApp('shinyApp.R', port="+port+")\"";
//ShinyProjects\MarketSizing\Market_sizing_Cardinal-master-b823f3aa918475f5d56f01aec9763ed860715158\mrktsiz_no_crosstalk
const killStr = "taskkill /im Rscript.exe /f"
console.log(killStr);
const childProcess = child.spawn("./R-Portable/bin/RScript.exe", ["-e", "shiny::runApp('app.R', port="+port+")"])
childProcess.stdout.on('data', (data) => {
  console.log(`stdout:${data}`)
})
childProcess.stderr.on('data', (data) => {
  console.log(`stdout:${data}`)
})

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow () {
  // Create the browser window.
  //  mainWindow = new BrowserWindow({webPreferences:{nodeIntegration:false},width: 800, height: 600})
  //  console.log(process.cwd())

    let loading = new BrowserWindow({show: false, frame: false})
    //let loading = new BrowserWindow()
    console.log(new Date().toISOString()+'::showing loading');
    loading.loadURL("data:text/html;charset=utf-8;base64,PGh0bWw+DQo8c3R5bGU+DQpib2R5ew0KICBwYWRkaW5nOiAxZW07DQogIGNvbG9yOiAjNzc3Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBzYW5zIiwgc2Fucy1zZXJpZjsNCiAgd2lkdGg6IDgwJTsNCiAgbWFyZ2luOiAwIGF1dG87DQp9DQpoMXsNCiAgbWFyZ2luOiAxZW0gMDsNCiAgYm9yZGVyLWJvdHRvbTogMXB4IGRhc2hlZDsNCiAgcGFkZGluZy1ib3R0b206IDFlbTsNCiAgZm9udC13ZWlnaHQ6IGxpZ2h0ZXI7DQp9DQpwew0KICBmb250LXN0eWxlOiBpdGFsaWM7DQp9DQoubG9hZGVyew0KICBtYXJnaW46IDAgMCAyZW07DQogIGhlaWdodDogMTAwcHg7DQogIHdpZHRoOiAyMCU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgcGFkZGluZzogMWVtOw0KICBtYXJnaW46IDAgYXV0byAxZW07DQogIGRpc3BsYXk6IGlubGluZS1ibG9jazsNCiAgdmVydGljYWwtYWxpZ246IHRvcDsNCn0NCg0KLyoNCiAgU2V0IHRoZSBjb2xvciBvZiB0aGUgaWNvbg0KKi8NCnN2ZyBwYXRoLA0Kc3ZnIHJlY3R7DQogIGZpbGw6ICNGRjY3MDA7DQp9DQo8L3N0eWxlPg0KPGJvZHk+PCEtLSAzICAtLT4NCjxkaXYgY2xhc3M9ImxvYWRlciBsb2FkZXItLXN0eWxlMyIgdGl0bGU9IjIiPg0KICA8c3ZnIHZlcnNpb249IjEuMSIgaWQ9ImxvYWRlci0xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCiAgICAgd2lkdGg9IjgwcHgiIGhlaWdodD0iODBweCIgdmlld0JveD0iMCAwIDUwIDUwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MCA1MDsiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KICA8cGF0aCBmaWxsPSIjMDAwIiBkPSJNNDMuOTM1LDI1LjE0NWMwLTEwLjMxOC04LjM2NC0xOC42ODMtMTguNjgzLTE4LjY4M2MtMTAuMzE4LDAtMTguNjgzLDguMzY1LTE4LjY4MywxOC42ODNoNC4wNjhjMC04LjA3MSw2LjU0My0xNC42MTUsMTQuNjE1LTE0LjYxNWM4LjA3MiwwLDE0LjYxNSw2LjU0MywxNC42MTUsMTQuNjE1SDQzLjkzNXoiPg0KICAgIDxhbmltYXRlVHJhbnNmb3JtIGF0dHJpYnV0ZVR5cGU9InhtbCINCiAgICAgIGF0dHJpYnV0ZU5hbWU9InRyYW5zZm9ybSINCiAgICAgIHR5cGU9InJvdGF0ZSINCiAgICAgIGZyb209IjAgMjUgMjUiDQogICAgICB0bz0iMzYwIDI1IDI1Ig0KICAgICAgZHVyPSIwLjZzIg0KICAgICAgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiLz4NCiAgICA8L3BhdGg+DQogIDwvc3ZnPg0KPC9kaXY+DQo8L2JvZHk+DQo8L2h0bWw+");

    //loading.toggleDevTools()
    loading.once('show', () => {
      console.log(new Date().toISOString()+'::show loading')
      mainWindow = new BrowserWindow({webPreferences:{nodeIntegration:false}, show:false, width: 800, height: 600})

      mainWindow.webContents.once('dom-ready', () => {
        console.log(new Date().toISOString()+'::mainWindow loaded')
        mainWindow.show()
        loading.hide()
        loading.close()
      })
      // long loading html
      mainWindow.loadURL('http://127.0.0.1:'+port)
      /**
      mainWindow.loadURL(url.format({
        pathname: path.join(__dirname, 'index.html'),
        protocol: 'file:',
        slashes: true
      }))
      **/
      mainWindow.webContents.on('did-finish-load', function() {
        console.log(new Date().toISOString()+'::did-finish-load')
      });

      mainWindow.webContents.on('did-start-load', function() {
        console.log(new Date().toISOString()+'::did-start-load')
      });

      mainWindow.webContents.on('did-stop-load', function() {
        console.log(new Date().toISOString()+'::did-stop-load')
      });
      mainWindow.webContents.on('dom-ready', function() {
        console.log(new Date().toISOString()+'::dom-ready')
      });

      // Open the DevTools.
      // mainWindow.webContents.openDevTools()

      // Emitted when the window is closed.
      mainWindow.on('closed', function () {
        console.log(new Date().toISOString()+'::closed')
        cleanUpApplication()
      })
    })
    loading.show()

}

function cleanUpApplication(){
  if(childProcess){
    console.log('window-all-closed   ... going to kill')
    try{
      childProcess.kill();
      console.log('killed.')
      child.execSync(killStr)
    } catch(ex){
      console.log(ex)
    }
  }

  mainWindow = null

}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  console.log('EVENT::window-all-closed')
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }

  cleanUpApplication()

})

app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<|endoftext|>


<commit_before>"use strict";

// you have to require the utils module and call adapter function
var utils =    require(__dirname + '/lib/utils'); // Get common adapter utils
var adapter = utils.adapter('paw');
var http = require('http');
const querystring = require('querystring');

adapter.on('unload', function (callback) {
    try {
        adapter.log.info('cleaned everything up...');
        callback();
    } catch (e) {
        callback();
    }
});

// is called if a subscribed object changes
adapter.on('objectChange', function (id, obj) {
    // Warning, obj can be null if it was deleted
    adapter.log.info('objectChange ' + id + ' ' + JSON.stringify(obj));
});

var sms={};

// is called if a subscribed state changes
adapter.on('stateChange', function (id, state) {
    adapter.log.info('stateChange ' + id + ' ' + JSON.stringify(state));

    var arr_id = id.split('.'); //разбить на массив
    //adapter.log.info(JSON.stringify(arr_id))
    //adapter.log.info(id);
    //adapter.log.info(adapter.namespace+'.'+arr_id[2]+'.tts.response');

    if(id===adapter.namespace+'.'+arr_id[2]+'.tts.response'){

        for (var i = 0; i < adapter.config.devices.length; i++) {
            var name = adapter.config.devices[i].name
            var ip = adapter.config.devices[i].ip
            var port = adapter.config.devices[i].port
            var time_start = adapter.config.devices[i].time_start
            var time_end = adapter.config.devices[i].time_end
            var date = new Date();
            var start = false;
            if(date.getHours()>=time_start&&date.getHours()<=time_end) start=true;  // Проверка времени оповещения
            if (arr_id[2]==name&&start) { //поиск по имени

                if(typeof (bufer[name])!=='object'){
                    bufer[name] = {};
                }
                bufer[name].ip = ip;
                bufer[name].port = port;
                bufer[name].name = name;
                if(typeof (bufer[name].text)!=='object'){
                    bufer[name].text=[]
                }
                if(typeof (bufer[name].start)=='undefined'){
                    bufer[name].start=true
                }

                bufer[name].text.push(state.val);
                if (bufer[name].start==true) {
                    say_bufer(name);
                }

            }
        }
    }else if (id===adapter.namespace+'.all_device.tts_response'||find(subscribe, id)!==-1){
        adapter.log.info(id);
        adapter.log.info(state);
        for (var i = 0; i < adapter.config.devices.length; i++) {
            var name = adapter.config.devices[i].name
            var ip = adapter.config.devices[i].ip
            var port = adapter.config.devices[i].port
            var time_start = adapter.config.devices[i].time_start
            var time_end = adapter.config.devices[i].time_end
            var date = new Date();
            if(date.getHours()>=time_start&&date.getHours()<=time_end) { // Проверка времени оповещения

                if(typeof (bufer[name])!=='object'){
                    bufer[name] = {};
                }
                bufer[name].ip = ip;
                bufer[name].port = port;
                bufer[name].name = name;
                if(typeof (bufer[name].text)!=='object'){
                    bufer[name].text=[]
                }
                if(typeof (bufer[name].start)=='undefined'){
                    bufer[name].start=true
                }

                bufer[name].text.push(state.val);
                if (bufer[name].start==true) {
                    say_bufer(name);
                }

            }

        }

    }
});

var bufer={};

function say_bufer(name){

    if(bufer[name].text.length!==0){
        bufer[name].start = false;

        getdata(name, bufer[name].ip, bufer[name].port, '/set.xhtml',{
            "send":"say",
            "text":bufer[name].text[0]}, function (response, ip) {
            try {
                response = JSON.parse(response);
                response.ip = ip;

            } catch (exception) {
                response = 'JSON.parse(response): error ';
            }
            adapter.log.info(JSON.stringify(response));
        });
        var char = bufer[name].text[0].toString();
        var num= char.length-char.replace(/\d/gm,'').length;
        var str = char.length-num;
        setTimeout(say_bufer, num*700+str*70+1000,name);
        bufer[name].text.shift();
    }else{
        bufer[name].start = true;
    }

}

var res=[];
var x=0;
// Some message was sent to adapter instance over message box. Used by email, pushover, text2speech, ...
adapter.on('message', function (obj) {

    if (typeof obj == 'object' && obj.message) {
        if (obj.command) {
            var text = obj.command.replace(/\s+/g,'') //убрать пробелы
            var arr = text.split(','); //разбить на массив
            if (obj.message) {
                var x=0;
                for (var i = 0; i < adapter.config.devices.length; i++){
                    var name = adapter.config.devices[i].name
                    var ip = adapter.config.devices[i].ip
                    var port = adapter.config.devices[i].port
                    if(name!=''&&ip!=''&&port!='') {
                        if(arr=='all'||find(arr, name)!==-1||find(arr, ip)!==-1){ //поиск по имени и ip
                            getdata(name, ip, port, '/set.xhtml', obj.message, function (response,ip){
                                try {
                                    response = JSON.parse(response);
                                    response.ip = ip;

                                } catch (exception) {
                                    response = 'JSON.parse(response): error ';
                                }
                                res[x]=response;
                                x=x+1;
                                if (obj.callback){
                                    setTimeout(function () {
                                        adapter.sendTo(obj.from, obj.command, res, obj.callback);
                                    },2000);

                                }
                                res=[];
                            });
                        }
                    }
                }

            }
        }
    }
});

adapter.on('ready', function () {
    main();
});

function setdata (setid, response ) {
    for (var key in response) {
        var val = response[key];
        //adapter.log.info("key: " + response[key]);
        adapter.setObject(setid+'.'+key, {
            type: 'state',
            common: {
                name: key,
                type: 'mixed',
                role: 'indicator',
                read: "true",
                write: "false"
            },
            native: {}
        });
        adapter.setState(setid+'.' + key, {val: val, ack: true});
    }
}

function set_id (setid, name, val ) {
    adapter.setObject(setid+'.'+name, {
        type: 'state',
        common: {
            name: name,
            type: 'mixed',
            role: 'tts',
            read: "true",
            write: "true"
        },
        native: {}
    });
    adapter.setState(setid+'.'+name, {val: val, ack: true});

}

function parsedata(name,data,path) {

    try {
        data = JSON.parse(data);
    } catch (exception) {
        adapter.log.info('parse_data_error: '+name);
        data = null;
    }

    if (data) {
        //adapter.log.info('ok: '+name);
        if(path=='/get.xhtml'){
            if(data.sensors) {
                delete data.sensors.info
                setdata (name+'.sensors', data.sensors );
            }
            if(data.wifi) setdata (name+'.wifi', data.wifi );
            if(data.battery) setdata (name+'.battery', data.battery );
            if(data.cpu) setdata (name+'.cpu', data.cpu );
            if(data.audio_volume.info) setdata (name+'.audio_volume.info', data.audio_volume.info );
            if(data.audio_volume) {
                delete data.audio_volume.info
                setdata (name+'.audio_volume', data.audio_volume );
            }
            if(data.memory) setdata (name+'.memory', data.memory );
            if(data.info) setdata (name+'.info', data.info );
            if(typeof data.gps ==='object') setdata (name+'.gps', data.gps );
        }
    }
}

function getdata(name,ip,port,path,setdata,callback) {

    var setdata = querystring.stringify(setdata);

    var options = {
        host: ip,
        port: port,
        path: path,
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(setdata)
        }
    };

    var req = http.request(options, function(res) {
        res.setEncoding('utf8');
        var buffer = "";
        res.on( "data", function( data ) {
            buffer = buffer + data;
        });
        res.on( "end", function( data ) {
            if(buffer!=''){
                if(path=='/get.xhtml'){
                    parsedata (name,buffer,path);
                }else{
                    callback(buffer,ip);
                }

            }
        });
    });

    req.on('error', (e) => {
        adapter.log.warn(`problem with request: ${e.message}`);
        if(path=='/get.xhtml'){
            find(ignorelist, ip)
            if(find(ignorelist, ip)===-1){
                adapter.log.warn('ignorelist:'+ip);
                ignorelist[ignorelist.length] = ip;
            }
        }else{
            callback(e,ip);
        }

    });

    req.write(setdata);
    req.end();
}

var ignorelist = [];
var subscribe = [];

function find(array, value) {

    for (var i = 0; i < array.length; i++) {
        if (array[i] == value) return i;
    }
    return -1;
}

function time_reset_ignore(){

    //adapter.log.warn('ignorelist reset:');
    ignorelist = [];
    for (var i = 0; i < adapter.config.devices.length; i++) {
        var ip = adapter.config.devices[i].ip
        var ign = adapter.config.devices[i].ign
        if(ign===true||ign==="true"){
            if(find(ignorelist, ip)===-1){
                adapter.log.warn('ignorelist:'+ip);
                ignorelist[ignorelist.length] = ip;
            }
        }
    }
}

function init(){
    for (var i = 0; i < adapter.config.devices.length; i++) {
        var name = adapter.config.devices[i].name
        var ip = adapter.config.devices[i].ip
        var port = adapter.config.devices[i].port

        if(name!=''){
            set_id (name+'.tts','response','text'  );
            adapter.subscribeStates(name+'.tts.response');
        }
    }
    set_id ('all_device','tts_response','text'  );
    adapter.subscribeStates('all_device.tts_response');
}

function time_paw() {
    //adapter.log.info('start: ');

    for (var i = 0; i < adapter.config.devices.length; i++) {
        var name = adapter.config.devices[i].name
        var ip = adapter.config.devices[i].ip
        var port = adapter.config.devices[i].port

        if(name!=''&&ip!=''&&port!=''){

            if(find(ignorelist, ip)===-1){
                getdata(name,ip,port,'/get.xhtml','')
            }
        }
    }
}

function main() {

    if (!adapter.config.devices.length || !adapter.config.interval) {
        adapter.log.warn('No one device configured');
        return;

    }

    //adapter.config.interval = Number(adapter.config.interval);

    if (adapter.config.interval < 5000) adapter.config.interval = 5000;

    setInterval(time_paw, Number(adapter.config.interval));
    setInterval(time_reset_ignore, 600000);

    adapter.log.info('config devices: ' + JSON.stringify(adapter.config.devices));
    adapter.log.info('config: ' + adapter.config.interval);
    adapter.log.info('length: ' + adapter.config.devices.length);

    time_reset_ignore();
    init();
    if(adapter.config.text2command){
        adapter.objects.getObjectView('system', 'instance',
            {startkey: 'system.adapter.text2command.', endkey: 'system.adapter.text2command.\u9999'},
            function (err, doc) {
                if (doc && doc.rows) {
                    for (var i = 0; i < doc.rows.length; i++) {
                        var id = doc.rows[i].id;
                        var obj = doc.rows[i].value;
                        var arr_sub = id.split('.');
                        adapter.log.info('subscribe: ' + arr_sub[2] + '.' + arr_sub[3] + '.response');
                        adapter.subscribeForeignStates(arr_sub[2] + '.' + arr_sub[3] + '.response');
                        subscribe[subscribe.length] = arr_sub[2] + '.' + arr_sub[3] + '.response';
                    }
                    if (!doc.rows.length) adapter.log.info('No objects found.');
                } else {
                    adapter.log.info('No objects found: ' + err);
                }
            });
    }

    if(adapter.config.apiai){

        adapter.objects.getObjectView('system', 'instance',
            {startkey: 'system.adapter.apiai.', endkey: 'system.adapter.apiai.\u9999'},
            function (err, doc) {
                if (doc && doc.rows) {
                    for (var i = 0; i < doc.rows.length; i++) {
                        var id  = doc.rows[i].id;
                        var obj = doc.rows[i].value;
                        var arr_sub = id.split('.');
                        adapter.log.info('subscribe: '+arr_sub[2]+'.'+arr_sub[3]+'.respons.speech');
                        adapter.subscribeForeignStates(arr_sub[2]+'.'+arr_sub[3]+'.respons.speech');
                        subscribe[subscribe.length] = arr_sub[2]+'.'+arr_sub[3]+'.respons.speech';
                    }
                    if (!doc.rows.length) adapter.log.info('No objects found.');
                } else {
                    adapter.log.info('No objects found: ' + err);
                }
            });
    }

    if(adapter.config.hilink){

        adapter.objects.getObjectView('system', 'instance',
            {startkey: 'system.adapter.hilink.', endkey: 'system.adapter.hilink.\u9999'},
            function (err, doc) {
                if (doc && doc.rows) {
                    for (var i = 0; i < doc.rows.length; i++) {
                        var id  = doc.rows[i].id;
                        var obj = doc.rows[i].value;
                        var arr_sub = id.split('.');
                        adapter.log.info('subscribe: '+arr_sub[2]+'.'+arr_sub[3]+'.last_sms.Content');
                        adapter.subscribeForeignStates(arr_sub[2]+'.'+arr_sub[3]+'.last_sms.Content');
                        subscribe[subscribe.length] = arr_sub[2]+'.'+arr_sub[3]+'.last_sms.Content';
                    }
                    if (!doc.rows.length) adapter.log.info('No objects found.');
                } else {
                    adapter.log.info('No objects found: ' + err);
                }
            });
    }

}

<commit_msg>add bufer<commit_after>"use strict";

// you have to require the utils module and call adapter function
var utils =    require(__dirname + '/lib/utils'); // Get common adapter utils
var adapter = utils.adapter('paw');
var http = require('http');
const querystring = require('querystring');

adapter.on('unload', function (callback) {
    try {
        adapter.log.info('cleaned everything up...');
        callback();
    } catch (e) {
        callback();
    }
});

// is called if a subscribed object changes
adapter.on('objectChange', function (id, obj) {
    // Warning, obj can be null if it was deleted
    adapter.log.info('objectChange ' + id + ' ' + JSON.stringify(obj));
});

var sms={};

// is called if a subscribed state changes
adapter.on('stateChange', function (id, state) {
    adapter.log.info('stateChange ' + id + ' ' + JSON.stringify(state));

    var arr_id = id.split('.'); //разбить на массив
    //adapter.log.info(JSON.stringify(arr_id))
    //adapter.log.info(id);
    //adapter.log.info(adapter.namespace+'.'+arr_id[2]+'.tts.response');

    if(id===adapter.namespace+'.'+arr_id[2]+'.tts.response'){

        for (var i = 0; i < adapter.config.devices.length; i++) {
            var name = adapter.config.devices[i].name
            var ip = adapter.config.devices[i].ip
            var port = adapter.config.devices[i].port
            var time_start = adapter.config.devices[i].time_start
            var time_end = adapter.config.devices[i].time_end
            var date = new Date();
            var start = false;
            if(date.getHours()>=time_start&&date.getHours()<=time_end) start=true;  // Проверка времени оповещения
            if (arr_id[2]==name&&start) { //поиск по имени

                if(typeof (bufer[name])!=='object'){
                    bufer[name] = {};
                }
                bufer[name].ip = ip;
                bufer[name].port = port;
                bufer[name].name = name;
                if(typeof (bufer[name].text)!=='object'){
                    bufer[name].text=[]
                }
                if(typeof (bufer[name].start)=='undefined'){
                    bufer[name].start=true
                }

                bufer[name].text.push(state.val);
                if (bufer[name].start==true) {
                    say_bufer(name);
                }

            }
        }
    }else if (id===adapter.namespace+'.all_device.tts_response'||find(subscribe, id)!==-1){
        adapter.log.info(id);
        adapter.log.info(state);
        for (var i = 0; i < adapter.config.devices.length; i++) {
            var name = adapter.config.devices[i].name
            var ip = adapter.config.devices[i].ip
            var port = adapter.config.devices[i].port
            var time_start = adapter.config.devices[i].time_start
            var time_end = adapter.config.devices[i].time_end
            var date = new Date();
            if(date.getHours()>=time_start&&date.getHours()<=time_end) { // Проверка времени оповещения

                if(typeof (bufer[name])!=='object'){
                    bufer[name] = {};
                }
                bufer[name].ip = ip;
                bufer[name].port = port;
                bufer[name].name = name;
                if(typeof (bufer[name].text)!=='object'){
                    bufer[name].text=[]
                }
                if(typeof (bufer[name].start)=='undefined'){
                    bufer[name].start=true
                }

                bufer[name].text.push(state.val);
                if (bufer[name].start==true) {
                    say_bufer(name);
                }

            }

        }

    }
});

var bufer={};

function say_bufer(name){

    if(bufer[name].text.length!==0){
        bufer[name].start = false;

        getdata(name, bufer[name].ip, bufer[name].port, '/set.xhtml',{
            "send":"say",
            "text":bufer[name].text[0]}, function (response, ip) {
            try {
                response = JSON.parse(response);
                response.ip = ip;

            } catch (exception) {
                response = 'JSON.parse(response): error ';
            }
            adapter.log.info(JSON.stringify(response));
        });
        var char = bufer[name].text[0].toString();
        var num= char.length-char.replace(/\d/gm,'').length;
        var str = char.length-num;
        setTimeout(say_bufer, num*700+str*70+1000,name);
        bufer[name].text.shift();
    }else{
        bufer[name].start = true;
    }

}

var res=[];
var x=0;
// Some message was sent to adapter instance over message box. Used by email, pushover, text2speech, ...
adapter.on('message', function (obj) {

    if (typeof obj == 'object' && obj.message) {
        if (obj.command) {
            var text = obj.command.replace(/\s+/g,'') //убрать пробелы
            var arr = text.split(','); //разбить на массив
            if (obj.message) {
                var x=0;
                for (var i = 0; i < adapter.config.devices.length; i++){
                    var name = adapter.config.devices[i].name
                    var ip = adapter.config.devices[i].ip
                    var port = adapter.config.devices[i].port
                    if(name!=''&&ip!=''&&port!='') {
                        if(arr=='all'||find(arr, name)!==-1||find(arr, ip)!==-1){ //поиск по имени и ip
                            getdata(name, ip, port, '/set.xhtml', obj.message, function (response,ip){
                                try {
                                    response = JSON.parse(response);
                                    response.ip = ip;

                                } catch (exception) {
                                    response = 'JSON.parse(response): error ';
                                }
                                res[x]=response;
                                x=x+1;
                                if (obj.callback){
                                    setTimeout(function () {
                                        adapter.sendTo(obj.from, obj.command, res, obj.callback);
                                    },2000);

                                }
                                res=[];
                            });
                        }
                    }
                }

            }
        }
    }
});

adapter.on('ready', function () {
    main();
});

function setdata (setid, response ) {
    for (var key in response) {
        var val = response[key];
        //adapter.log.info("key: " + response[key]);
        adapter.setObject(setid+'.'+key, {
            type: 'state',
            common: {
                name: key,
                type: 'mixed',
                role: 'indicator',
                read: "true",
                write: "false"
            },
            native: {}
        });
        adapter.setState(setid+'.' + key, {val: val, ack: true});
    }
}

function set_id (setid, name, val ) {
    adapter.setObject(setid+'.'+name, {
        type: 'state',
        common: {
            name: name,
            type: 'mixed',
            role: 'tts',
            read: "true",
            write: "true"
        },
        native: {}
    });
    adapter.setState(setid+'.'+name, {val: val, ack: true});

}

function parsedata(name,data,path) {

    try {
        data = JSON.parse(data);
    } catch (exception) {
        adapter.log.info('parse_data_error: '+name);
        data = null;
    }

    if (data) {
        //adapter.log.info('ok: '+name);
        if(path=='/get.xhtml'){
            if(data.sensors) {
                delete data.sensors.info
                setdata (name+'.sensors', data.sensors );
            }
            if(data.wifi) setdata (name+'.wifi', data.wifi );
            if(data.battery) setdata (name+'.battery', data.battery );
            if(data.cpu) setdata (name+'.cpu', data.cpu );
            if(data.audio_volume.info) setdata (name+'.audio_volume.info', data.audio_volume.info );
            if(data.audio_volume) {
                delete data.audio_volume.info
                setdata (name+'.audio_volume', data.audio_volume );
            }
            if(data.memory) setdata (name+'.memory', data.memory );
            if(data.info) setdata (name+'.info', data.info );
            if(typeof data.gps ==='object') setdata (name+'.gps', data.gps );
        }
    }
}

function getdata(name,ip,port,path,setdata,callback) {

    var setdata = querystring.stringify(setdata);

    var options = {
        host: ip,
        port: port,
        path: path,
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(setdata)
        }
    };

    var req = http.request(options, function(res) {
        res.setEncoding('utf8');
        var buffer = "";
        res.on( "data", function( data ) {
            buffer = buffer + data;
        });
        res.on( "end", function( data ) {
            if(buffer!=''){
                if(path=='/get.xhtml'){
                    parsedata (name,buffer,path);
                }else{
                    callback(buffer,ip);
                }

            }
        });
    });

    req.on('error', (e) => {
        adapter.log.warn(`problem with request: ${e.message}`);
        if(path=='/get.xhtml'){
            find(ignorelist, ip)
            if(find(ignorelist, ip)===-1){
                adapter.log.warn('ignorelist:'+ip);
                ignorelist[ignorelist.length] = ip;
            }
        }else{
            callback(e,ip);
        }

    });

    req.write(setdata);
    req.end();
}

var ignorelist = [];
var subscribe = [];

function find(array, value) {

    for (var i = 0; i < array.length; i++) {
        if (array[i] == value) return i;
    }
    return -1;
}

function time_reset_ignore(){

    //adapter.log.warn('ignorelist reset:');
    ignorelist = [];
    for (var i = 0; i < adapter.config.devices.length; i++) {
        var ip = adapter.config.devices[i].ip
        var ign = adapter.config.devices[i].ign
        if(ign===true||ign==="true"){
            if(find(ignorelist, ip)===-1){
                adapter.log.warn('ignorelist:'+ip);
                ignorelist[ignorelist.length] = ip;
            }
        }
    }
}

function init(){
    for (var i = 0; i < adapter.config.devices.length; i++) {
        var name = adapter.config.devices[i].name
        var ip = adapter.config.devices[i].ip
        var port = adapter.config.devices[i].port

        if(name!=''){
            set_id (name+'.tts','response','text'  );
            adapter.subscribeStates(name+'.tts.response');
        }
    }
    set_id ('all_device','request',''  );
    adapter.subscribeStates('all_device.request');
    set_id ('all_device','tts_response','text'  );
    adapter.subscribeStates('all_device.tts_response');
}

function time_paw() {
    //adapter.log.info('start: ');

    for (var i = 0; i < adapter.config.devices.length; i++) {
        var name = adapter.config.devices[i].name
        var ip = adapter.config.devices[i].ip
        var port = adapter.config.devices[i].port

        if(name!=''&&ip!=''&&port!=''){

            if(find(ignorelist, ip)===-1){
                getdata(name,ip,port,'/get.xhtml','')
            }
        }
    }
}

function main() {

    if (!adapter.config.devices.length || !adapter.config.interval) {
        adapter.log.warn('No one device configured');
        return;

    }

    //adapter.config.interval = Number(adapter.config.interval);

    if (adapter.config.interval < 5000) adapter.config.interval = 5000;

    setInterval(time_paw, Number(adapter.config.interval));
    setInterval(time_reset_ignore, 600000);

    adapter.log.info('config devices: ' + JSON.stringify(adapter.config.devices));
    adapter.log.info('config: ' + adapter.config.interval);
    adapter.log.info('length: ' + adapter.config.devices.length);

    time_reset_ignore();
    init();
    if(adapter.config.text2command){
        adapter.objects.getObjectView('system', 'instance',
            {startkey: 'system.adapter.text2command.', endkey: 'system.adapter.text2command.\u9999'},
            function (err, doc) {
                if (doc && doc.rows) {
                    for (var i = 0; i < doc.rows.length; i++) {
                        var id = doc.rows[i].id;
                        var obj = doc.rows[i].value;
                        var arr_sub = id.split('.');
                        adapter.log.info('subscribe: ' + arr_sub[2] + '.' + arr_sub[3] + '.response');
                        adapter.subscribeForeignStates(arr_sub[2] + '.' + arr_sub[3] + '.response');
                        subscribe[subscribe.length] = arr_sub[2] + '.' + arr_sub[3] + '.response';
                    }
                    if (!doc.rows.length) adapter.log.info('No objects found.');
                } else {
                    adapter.log.info('No objects found: ' + err);
                }
            });
    }

    if(adapter.config.apiai){

        adapter.objects.getObjectView('system', 'instance',
            {startkey: 'system.adapter.apiai.', endkey: 'system.adapter.apiai.\u9999'},
            function (err, doc) {
                if (doc && doc.rows) {
                    for (var i = 0; i < doc.rows.length; i++) {
                        var id  = doc.rows[i].id;
                        var obj = doc.rows[i].value;
                        var arr_sub = id.split('.');
                        adapter.log.info('subscribe: '+arr_sub[2]+'.'+arr_sub[3]+'.respons.speech');
                        adapter.subscribeForeignStates(arr_sub[2]+'.'+arr_sub[3]+'.respons.speech');
                        subscribe[subscribe.length] = arr_sub[2]+'.'+arr_sub[3]+'.respons.speech';
                    }
                    if (!doc.rows.length) adapter.log.info('No objects found.');
                } else {
                    adapter.log.info('No objects found: ' + err);
                }
            });
    }

    if(adapter.config.hilink){

        adapter.objects.getObjectView('system', 'instance',
            {startkey: 'system.adapter.hilink.', endkey: 'system.adapter.hilink.\u9999'},
            function (err, doc) {
                if (doc && doc.rows) {
                    for (var i = 0; i < doc.rows.length; i++) {
                        var id  = doc.rows[i].id;
                        var obj = doc.rows[i].value;
                        var arr_sub = id.split('.');
                        adapter.log.info('subscribe: '+arr_sub[2]+'.'+arr_sub[3]+'.last_sms.Content');
                        adapter.subscribeForeignStates(arr_sub[2]+'.'+arr_sub[3]+'.last_sms.Content');
                        subscribe[subscribe.length] = arr_sub[2]+'.'+arr_sub[3]+'.last_sms.Content';
                    }
                    if (!doc.rows.length) adapter.log.info('No objects found.');
                } else {
                    adapter.log.info('No objects found: ' + err);
                }
            });
    }

}

<|endoftext|>


<commit_before>define(["github/adioo/bind-list/v0.1.7/list"], function(List) {

    var self;

    function init(config) {

        config.crud = {
            create: "create",
            read:   "read",
            update: "update",
            delete: "remove"
        }

        if (config.sample) {
            // this reads the sample configuration if this is a sample list
            buildSampleConfig(config);
        }

        self = List(this, config);
        self.lang = "de";
    }

    function buildSampleConfig(config) {

        // (mandatory) what to render fot each data item
        config.template = {
            type: "selector", // "url", "html"
            value: ".template",
            binds: [
                {
                    target: ".delete",
                    on: [{
                        name: "click",
                        handler: "removeItem"
                    }]
                },
                {
                    on: [{
                        name: "click",
                        handler: "selectItem"
                    }]
                },
                {
                    attr: [
                        {
                            name: "id",
                            value: {
                                source: "id",
                                filter: [
                                    { name: "prefix", args: "itm", library: "/aasdsda" }
                                ]
                            }
                        }
                    ],
                },
                {
                    target: ".itemText",
                    html: {
                        source: "text"
                    }
                }
            ]
        };

        // (mandatory) where to display rendered items
        config.container = ".content";

        // (mandatory) the CRUD operations are module operations
        config.crud = {
            create: "create",
            read:   "read",
            update: "update",
            delete: "remove"
        };

        // (optional) options
        config.options = {
            selection: "multiple", // "single", "multiple"
            autofetch: true,
            classes: {
                selected: "selected"
            }
        };

        // (optional) selectors as handles for core list operations
        config.controls = {
            add: ".create",
            delete: ".delete"
        };

        // (optional) additional functionality through bind objects
        config.binds = [
            {
                target: "create",
                on: [{
                    name: "click",
                    emit: "newItem"
                }]
            }
        ];
    }

    return init;
});

<commit_msg>Removed the hardcoded de language for the list<commit_after>define(["github/adioo/bind-list/v0.1.7/list"], function(List) {

    var self;

    function init(config) {

        config.crud = {
            create: "create",
            read:   "read",
            update: "update",
            delete: "remove"
        }

        if (config.sample) {
            // this reads the sample configuration if this is a sample list
            buildSampleConfig(config);
        }

        self = List(this, config);
    }

    function buildSampleConfig(config) {

        // (mandatory) what to render fot each data item
        config.template = {
            type: "selector", // "url", "html"
            value: ".template",
            binds: [
                {
                    target: ".delete",
                    on: [{
                        name: "click",
                        handler: "removeItem"
                    }]
                },
                {
                    on: [{
                        name: "click",
                        handler: "selectItem"
                    }]
                },
                {
                    attr: [
                        {
                            name: "id",
                            value: {
                                source: "id",
                                filter: [
                                    { name: "prefix", args: "itm", library: "/aasdsda" }
                                ]
                            }
                        }
                    ],
                },
                {
                    target: ".itemText",
                    html: {
                        source: "text"
                    }
                }
            ]
        };

        // (mandatory) where to display rendered items
        config.container = ".content";

        // (mandatory) the CRUD operations are module operations
        config.crud = {
            create: "create",
            read:   "read",
            update: "update",
            delete: "remove"
        };

        // (optional) options
        config.options = {
            selection: "multiple", // "single", "multiple"
            autofetch: true,
            classes: {
                selected: "selected"
            }
        };

        // (optional) selectors as handles for core list operations
        config.controls = {
            add: ".create",
            delete: ".delete"
        };

        // (optional) additional functionality through bind objects
        config.binds = [
            {
                target: "create",
                on: [{
                    name: "click",
                    emit: "newItem"
                }]
            }
        ];
    }

    return init;
});

<|endoftext|>