<commit_before>/*jslint devel: true es5: true */
/*global $: true, joynr: true, provisioning: true, RadioProvider: true, RadioStation: true, GeoPosition: true, domain: true, getBuildSignatureString: true, TrafficServiceBroadcastFilter : true, GeocastBroadcastFilter: true */

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

function logHtml(line, msg) {
    $("div#divLog").prepend(
            msg + " " + line + "
").show();
}
function log(line, msg) {
    logHtml(line, msg);
    msg = new Date().toISOString() + ": [provider.js: " + line + "] " + msg;
    console.log(msg);
}

function showCurrentStationInHtml(radioStation) {
    $("input#txtCurrentStationName").val(radioStation.name);
    $("input#txtCurrentStationTrafficService").val(radioStation.trafficService);
    $("input#txtCurrentStationCountry").val(radioStation.country);
}

function showFavoriteStationsInHtml(radioStations) {
    var i;
    var stationsString = "";
    for (i = 0; i < radioStations.length; i++) {
        if (i !== 0) {
            stationsString += "\n";
        }
        stationsString += "Name: " + radioStations[i].name;
        stationsString += " | TrafficService: " + radioStations[i].trafficService;
        stationsString += " | Country: " + radioStations[i].country;
    }
    $("textarea#txtFavoriteStations").val(stationsString);
}

var joynr;
var currentStationValue = "default value for current station";
var providerQos = null;

var RadioProviderImpl =
        function RadioProviderImpl() {
            var self = this;

            var stationsList = [
                new RadioStation({
                    name : "ABC Trible J",
                    trafficService : true,
                    country : "AUSTRALIA"
                }),
                new RadioStation({
                    name : "Radio Popolare",
                    trafficService : false,
                    country : "ITALY"
                }),
                new RadioStation({
                    name : "JAZZ.FM91",
                    trafficService : false,
                    country : "CANADA"
                }),
                new RadioStation({
                    name : "Bayern 3",
                    trafficService : true,
                    country : "GERMANY"
                })
            ];

            var countryGeoPositionMap = {
                // Melbourne
                "AUSTRALIA": new GeoPosition({ latitude: -37.8141070, longitude: 144.9632800 }),
                // Bolzano
                "ITALY": new GeoPosition({ latitude: 46.4982950, longitude: 11.3547580 }),
                // Edmonton
                "CANADA": new GeoPosition({ latitude: 53.5443890, longitude: -113.4909270 }),
                // Munich
                "GERMANY": new GeoPosition({ latitude: 48.1351250, longitude: 11.5819810 })
            };

            var currentStationIndex = 0;

            Object.defineProperty(this, "currentStationIndex", {
                get : function() {
                    return currentStationIndex;
                }
            });

            Object.defineProperty(this, "stationsList", {
                get : function() {
                    return stationsList;
                }
            });

            this.currentStation = {
                get : function() {
                    log("radioProvider.currentStation.get", "called");
                    return stationsList[currentStationIndex];
                }
            };

            this.addFavoriteStation =
                    function(opArgs) {
                        log("radioProvider.addFavoriteStation", "called with args: "
                            + JSON.stringify(opArgs));

                        if (opArgs === undefined) {
                            log(
                                    "radioProvider.addFavoriteStation",
                                    "operation arguments is undefined!");
                            return;
                        }
                        if (opArgs.newFavoriteStation === undefined) {
                            log(
                                    "radioProvider.addFavoriteStation",
                                    "operation argument \"newFavoriteStation\" is undefined!");
                            return;
                        }

                        stationsList.push(opArgs.newFavoriteStation);

                        showFavoriteStationsInHtml(stationsList);
                        return true;
                    };

            this.shuffleStations =
                    function() {
                        log("radioProvider.shuffleStations", "called");

                        var oldStationIndex = currentStationIndex;

                        currentStationIndex++;
                        currentStationIndex = currentStationIndex % stationsList.length;
                        self.currentStation.valueChanged(stationsList[currentStationIndex]);
                        log("radioProvider.shuffleStations", JSON
                                .stringify(stationsList[oldStationIndex])
                            + " -> "
                            + JSON.stringify(stationsList[currentStationIndex]));

                        showCurrentStationInHtml(stationsList[currentStationIndex]);
                    };

            // Provide broadcast specific implementation stubs. Properties will
            // be added when joynr.providerBuilder.build() gets called.

            this.weakSignal = {};

            this.newStationDiscovered = {};

            // the button onclick handler
            this.fireWeakSignal = function() {
                log("radioProvider.fireWeakSignal", "called");

                var outputParameters;
                outputParameters = self.weakSignal.createBroadcastOutputParameters();
                outputParameters.setWeakSignalStation(stationsList[currentStationIndex]);
                self.weakSignal.fire(outputParameters);
            };

            // the button onclick handler
            this.fireNewStationDiscovered = function() {
                log("radioProvider.fireNewStationDiscovered", "called");
                var outputParameters;
                var geoPosition;

                geoPosition = countryGeoPositionMap[stationsList[currentStationIndex].country];
                outputParameters = self.newStationDiscovered.createBroadcastOutputParameters();
                outputParameters.setGeoPosition(geoPosition);
                outputParameters.setDiscoveredStation(stationsList[currentStationIndex]);
                self.newStationDiscovered.fire(outputParameters);
            };

            this.getLocationOfCurrentStation =
                function() {
                    log("radioProvider.getLocationOfCurrentStation", "called");
                    return stationsList[currentStationIndex].country;
                };

            // fill current station into fields
            showCurrentStationInHtml(stationsList[currentStationIndex]);

            // fill favorite stations into textarea
            showFavoriteStationsInHtml(stationsList);
        };

$(function() { // DOM ready
    var radioProvider = null;
    var radioProviderImpl = new RadioProviderImpl();
    // output build signatures to log
    joynr.load(provisioning, function(error, loadedJoynr) {
        if (error) {
            log("main", "error initializing joynr: " + error);
            throw error;
        }

        joynr = loadedJoynr;
        log("main", joynr.buildSignature());
        log("main", getBuildSignatureString());
        // create joynr provider
        radioProvider = joynr.providerBuilder.build(RadioProvider, radioProviderImpl);

        // fill domain into field
        $("input#txtDomain").val(domain);

        // register joynr provider
        $("input#btnRegisterProvider").click(
                function() {
                    domain = $("input#txtDomain").val();

                    // test, whether some filter functions can be added here
                    radioProvider.newStationDiscovered.addBroadcastFilter(new GeocastBroadcastFilter());
                    radioProvider.newStationDiscovered.addBroadcastFilter(new TrafficServiceBroadcastFilter());

                    joynr.capabilities.registerCapability(
                            "RadioProvider.authToken",
                            domain,
                            radioProvider,
                            new joynr.types.ProviderQos({
                                customParameters : [],
                                providerVersion : 1,
                                priority : Date.now(),
                                scope : joynr.types.ProviderScope.GLOBAL,
                                onChangeSubscriptions : true
                            })).then(
                            function() {
                                log(
                                        "joynr.capabilities.registerCapability.done",
                                        "successfully registered Radio provider on domain \""
                                            + domain
                                            + "\".");
                            }).catch(
                            function(error) {
                                log(
                                        "joynr.capabilities.registerCapability.fail",
                                        "error registering Radio provider. Reason: "
                                            + error.toString());
                            });
                    $("input#btnRegisterProvider").attr("disabled", true);
                    $("input#btnUnregisterProvider").attr("disabled", false);
                    $("input#btnShuffleStations").attr("disabled", false);
                    $("input#btnAddFavouriteStation").attr("disabled", false);
                    $("input#btnFireWeakSignal").attr("disabled", false);
                    $("input#btnFireNewStationDiscovered").attr("disabled", false);
                });

        // unregister joynr provider
        $("input#btnUnregisterProvider").click(
                function() {
                    joynr.capabilities.unregisterCapability(
                            "RadioProvider.authToken",
                            domain,
                            radioProvider).then(
                            function() {
                                log(
                                        "joynr.capabilities.unregisterCapability.done",
                                        "successfully unregistered Radio provider on domain \""
                                            + domain
                                            + "\".");
                            }).catch(
                            function(error) {
                                log(
                                        "joynr.capabilities.unregisterCapability.fail",
                                        "error unregistering Radio provider. Reason: " + error);
                            });
                    $("input#btnRegisterProvider").attr("disabled", false);
                    $("input#btnUnregisterProvider").attr("disabled", true);
                    $("input#btnShuffleStations").attr("disabled", true);
                    $("input#btnAddFavouriteStation").attr("disabled", true);
                    $("input#btnFireWeakSignal").attr("disabled", true);
                    $("input#btnFireNewStationDiscovered").attr("disabled", true);
                });

        $("input#btnShuffleStations").click(function() {
            radioProviderImpl.shuffleStations();
        });
        $("input#btnFireWeakSignal").click(function() {
            radioProviderImpl.fireWeakSignal();
        });
        $("input#btnFireNewStationDiscovered").click(function() {
            radioProviderImpl.fireNewStationDiscovered();
        });
        $("input#btnAddFavoriteStation").click(function() {
            radioProviderImpl.addFavoriteStation({
                newFavoriteStation : new RadioStation({
                    name : $("input#txtNewFavoriteStationName").val(),
                    trafficService : $("select#slctTrafficService").val() === "true",
                    country : $("select#slctCountry").val()
                })
            });
        });
    });
});
<commit_msg>[JS] Radio: Provide async method implementation example<commit_after>/*jslint devel: true es5: true */
/*global $: true, joynr: true, provisioning: true, RadioProvider: true, RadioStation: true, GeoPosition: true, domain: true, getBuildSignatureString: true, TrafficServiceBroadcastFilter : true, GeocastBroadcastFilter: true, Promise: true */

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

function logHtml(line, msg) {
    $("div#divLog").prepend(
            msg + " " + line + "
").show();
}
function log(line, msg) {
    logHtml(line, msg);
    msg = new Date().toISOString() + ": [provider.js: " + line + "] " + msg;
    console.log(msg);
}

function showCurrentStationInHtml(radioStation) {
    $("input#txtCurrentStationName").val(radioStation.name);
    $("input#txtCurrentStationTrafficService").val(radioStation.trafficService);
    $("input#txtCurrentStationCountry").val(radioStation.country);
}

function showFavoriteStationsInHtml(radioStations) {
    var i;
    var stationsString = "";
    for (i = 0; i < radioStations.length; i++) {
        if (i !== 0) {
            stationsString += "\n";
        }
        stationsString += "Name: " + radioStations[i].name;
        stationsString += " | TrafficService: " + radioStations[i].trafficService;
        stationsString += " | Country: " + radioStations[i].country;
    }
    $("textarea#txtFavoriteStations").val(stationsString);
}

var joynr;
var currentStationValue = "default value for current station";
var providerQos = null;

var RadioProviderImpl =
        function RadioProviderImpl() {
            var self = this;

            var stationsList = [
                new RadioStation({
                    name : "ABC Trible J",
                    trafficService : true,
                    country : "AUSTRALIA"
                }),
                new RadioStation({
                    name : "Radio Popolare",
                    trafficService : false,
                    country : "ITALY"
                }),
                new RadioStation({
                    name : "JAZZ.FM91",
                    trafficService : false,
                    country : "CANADA"
                }),
                new RadioStation({
                    name : "Bayern 3",
                    trafficService : true,
                    country : "GERMANY"
                })
            ];

            var countryGeoPositionMap = {
                // Melbourne
                "AUSTRALIA": new GeoPosition({ latitude: -37.8141070, longitude: 144.9632800 }),
                // Bolzano
                "ITALY": new GeoPosition({ latitude: 46.4982950, longitude: 11.3547580 }),
                // Edmonton
                "CANADA": new GeoPosition({ latitude: 53.5443890, longitude: -113.4909270 }),
                // Munich
                "GERMANY": new GeoPosition({ latitude: 48.1351250, longitude: 11.5819810 })
            };

            var currentStationIndex = 0;

            Object.defineProperty(this, "currentStationIndex", {
                get : function() {
                    return currentStationIndex;
                }
            });

            Object.defineProperty(this, "stationsList", {
                get : function() {
                    return stationsList;
                }
            });

            this.currentStation = {
                get : function() {
                    log("radioProvider.currentStation.get", "called");
                    return stationsList[currentStationIndex];
                }
            };

            this.addFavoriteStation =
                    function(opArgs) {
                        /*
                         * synchronous method implementation can be used if no
                         * further asynchronous activity is required to obtain
                         * the result.
                         * In case of error a ProviderRuntimeException or an
                         * error enum value suitable for the method can be
                         * thrown. The latter will be automatically wrapped
                         * inside an ApplicationException.
                         */
                        log("radioProvider.addFavoriteStation", "called with args: "
                            + JSON.stringify(opArgs));

                        if (opArgs === undefined) {
                            log(
                                    "radioProvider.addFavoriteStation",
                                    "operation arguments is undefined!");
                            return;
                        }
                        if (opArgs.newFavoriteStation === undefined) {
                            log(
                                    "radioProvider.addFavoriteStation",
                                    "operation argument \"newFavoriteStation\" is undefined!");
                            return;
                        }

                        stationsList.push(opArgs.newFavoriteStation);

                        showFavoriteStationsInHtml(stationsList);
                        return true;
                    };

            this.shuffleStations =
                    function() {
                        /*
                         * asynchronous method implementation is required if
                         * the result depends on other asynchronous activity.
                         *
                         * When activity is finished, then based on the positive
                         * or negative outcome, resolve() or reject() has to be
                         * called. For resolve() any output parameters have to be
                         * provided. For reject a ProviderRuntimeException or an
                         * error enum value suitable for the method must be
                         * provided. The latter will be automatically wrapped
                         * inside an ApplicationException.
                         */
                        log("radioProvider.shuffleStations", "called");
                        return new Promise(function(resolve, reject) {
                            var oldStationIndex = currentStationIndex;
                            currentStationIndex++;
                            currentStationIndex = currentStationIndex % stationsList.length;
                            self.currentStation.valueChanged(stationsList[currentStationIndex]);
                            log("radioProvider.shuffleStations", JSON
                                    .stringify(stationsList[oldStationIndex])
                                + " -> "
                                + JSON.stringify(stationsList[currentStationIndex]));

                            showCurrentStationInHtml(stationsList[currentStationIndex]);
                            resolve();
                        });
                    };

            // Provide broadcast specific implementation stubs. Properties will
            // be added when joynr.providerBuilder.build() gets called.

            this.weakSignal = {};

            this.newStationDiscovered = {};

            // the button onclick handler
            this.fireWeakSignal = function() {
                log("radioProvider.fireWeakSignal", "called");

                var outputParameters;
                outputParameters = self.weakSignal.createBroadcastOutputParameters();
                outputParameters.setWeakSignalStation(stationsList[currentStationIndex]);
                self.weakSignal.fire(outputParameters);
            };

            // the button onclick handler
            this.fireNewStationDiscovered = function() {
                log("radioProvider.fireNewStationDiscovered", "called");
                var outputParameters;
                var geoPosition;

                geoPosition = countryGeoPositionMap[stationsList[currentStationIndex].country];
                outputParameters = self.newStationDiscovered.createBroadcastOutputParameters();
                outputParameters.setGeoPosition(geoPosition);
                outputParameters.setDiscoveredStation(stationsList[currentStationIndex]);
                self.newStationDiscovered.fire(outputParameters);
            };

            this.getLocationOfCurrentStation =
                function() {
                    log("radioProvider.getLocationOfCurrentStation", "called");
                    return stationsList[currentStationIndex].country;
                };

            // fill current station into fields
            showCurrentStationInHtml(stationsList[currentStationIndex]);

            // fill favorite stations into textarea
            showFavoriteStationsInHtml(stationsList);
        };

$(function() { // DOM ready
    var radioProvider = null;
    var radioProviderImpl = new RadioProviderImpl();
    // output build signatures to log
    joynr.load(provisioning, function(error, loadedJoynr) {
        if (error) {
            log("main", "error initializing joynr: " + error);
            throw error;
        }

        joynr = loadedJoynr;
        log("main", joynr.buildSignature());
        log("main", getBuildSignatureString());
        // create joynr provider
        radioProvider = joynr.providerBuilder.build(RadioProvider, radioProviderImpl);

        // fill domain into field
        $("input#txtDomain").val(domain);

        // register joynr provider
        $("input#btnRegisterProvider").click(
                function() {
                    domain = $("input#txtDomain").val();

                    // test, whether some filter functions can be added here
                    radioProvider.newStationDiscovered.addBroadcastFilter(new GeocastBroadcastFilter());
                    radioProvider.newStationDiscovered.addBroadcastFilter(new TrafficServiceBroadcastFilter());

                    joynr.capabilities.registerCapability(
                            "RadioProvider.authToken",
                            domain,
                            radioProvider,
                            new joynr.types.ProviderQos({
                                customParameters : [],
                                providerVersion : 1,
                                priority : Date.now(),
                                scope : joynr.types.ProviderScope.GLOBAL,
                                onChangeSubscriptions : true
                            })).then(
                            function() {
                                log(
                                        "joynr.capabilities.registerCapability.done",
                                        "successfully registered Radio provider on domain \""
                                            + domain
                                            + "\".");
                            }).catch(
                            function(error) {
                                log(
                                        "joynr.capabilities.registerCapability.fail",
                                        "error registering Radio provider. Reason: "
                                            + error.toString());
                            });
                    $("input#btnRegisterProvider").attr("disabled", true);
                    $("input#btnUnregisterProvider").attr("disabled", false);
                    $("input#btnShuffleStations").attr("disabled", false);
                    $("input#btnAddFavouriteStation").attr("disabled", false);
                    $("input#btnFireWeakSignal").attr("disabled", false);
                    $("input#btnFireNewStationDiscovered").attr("disabled", false);
                });

        // unregister joynr provider
        $("input#btnUnregisterProvider").click(
                function() {
                    joynr.capabilities.unregisterCapability(
                            "RadioProvider.authToken",
                            domain,
                            radioProvider).then(
                            function() {
                                log(
                                        "joynr.capabilities.unregisterCapability.done",
                                        "successfully unregistered Radio provider on domain \""
                                            + domain
                                            + "\".");
                            }).catch(
                            function(error) {
                                log(
                                        "joynr.capabilities.unregisterCapability.fail",
                                        "error unregistering Radio provider. Reason: " + error);
                            });
                    $("input#btnRegisterProvider").attr("disabled", false);
                    $("input#btnUnregisterProvider").attr("disabled", true);
                    $("input#btnShuffleStations").attr("disabled", true);
                    $("input#btnAddFavouriteStation").attr("disabled", true);
                    $("input#btnFireWeakSignal").attr("disabled", true);
                    $("input#btnFireNewStationDiscovered").attr("disabled", true);
                });

        $("input#btnShuffleStations").click(function() {
            radioProviderImpl.shuffleStations();
        });
        $("input#btnFireWeakSignal").click(function() {
            radioProviderImpl.fireWeakSignal();
        });
        $("input#btnFireNewStationDiscovered").click(function() {
            radioProviderImpl.fireNewStationDiscovered();
        });
        $("input#btnAddFavoriteStation").click(function() {
            radioProviderImpl.addFavoriteStation({
                newFavoriteStation : new RadioStation({
                    name : $("input#txtNewFavoriteStationName").val(),
                    trafficService : $("select#slctTrafficService").val() === "true",
                    country : $("select#slctCountry").val()
                })
            });
        });
    });
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name       Shutterstock show places table
// @namespace  http://gubnota.github.io/fenki_js_helpers/plugin/monkey/
// @version    0.1.4
// @downloadURL http://gubnota.github.io/fenki_js_helpers/plugin/monkey/shutter_places/main.user.js
// @description Shows Shutterstock buyers places map spots one-by-one table
// @include        http*://submit.shutterstock.com/home.mhtml*
// @copyright  2015+, Vladislav Muravyev
// @author Vladislav Muravyev
// @grant          none
// @icon http://gubnota.github.io/fenki_js_helpers/plugin/monkey/shutter_places/ss.png
// @run-at document-end
// ==/UserScript==
(function(window, undefined){
var w = window;
    // Пример: подключение jquery.min.js
    // (function(a,b){function ci(a) ... a.jQuery=a.$=d})(w);

    // [3] не запускаем скрипт во фреймах
    // без этого условия скрипт будет запускаться несколько раз на странице с фреймами
    if (w.self != w.top) {
        return;
    }
    // [4] дополнительная проверка наряду с @include
    if (/:\/\/submit\.shutterstock\.com\/home.mhtml/.test(w.location.href)) {
        //Ниже идёт непосредственно код скрипта
        var el = document.getElementById('shutter_places_window_helper_script');
        if (el === null) {
(function(f,e,n,k,i){
var n=e.createElement(n);
n.setAttribute('id','shutter_places_window_helper_script');
n.async=true;
n.src=k;
if(typeof(IFrameWindowHelper) == 'undefined'){
// if (/Safari/g.test(window.navigator.appVersion)){
// window_helper.js}
n.onload = function(){BlockYoutubeAddsWindowHelperScript.init()};
}
(e[i]('head')[0] || e[i]('body')[0]).appendChild(n);
})(window,document,'script',(6 == document.location.protocol.length ? 'https:' : 'http:') + '//gubnota.github.io/fenki_js_helpers/plugin/monkey/shutter_places/window_helper.js','getElementsByTagName');

        }//undefined el
    }//url pattern
})(window);
<commit_msg>youtube ads blocker<commit_after>// ==UserScript==
// @name       Shutterstock show places table
// @namespace  http://gubnota.github.io/fenki_js_helpers/plugin/monkey/
// @version    0.1.4
// @downloadURL http://gubnota.github.io/fenki_js_helpers/plugin/monkey/shutter_places/main.user.js
// @description Shows Shutterstock buyers places map spots one-by-one table
// @include        http*://submit.shutterstock.com/home.mhtml*
// @copyright  2015+, Vladislav Muravyev
// @author Vladislav Muravyev
// @grant          none
// @icon http://gubnota.github.io/fenki_js_helpers/plugin/monkey/shutter_places/ss.png
// @run-at document-end
// ==/UserScript==
(function(window, undefined){
var w = window;
    // Пример: подключение jquery.min.js
    // (function(a,b){function ci(a) ... a.jQuery=a.$=d})(w);

    // [3] не запускаем скрипт во фреймах
    // без этого условия скрипт будет запускаться несколько раз на странице с фреймами
    if (w.self != w.top) {
        return;
    }
    // [4] дополнительная проверка наряду с @include
    if (/:\/\/submit\.shutterstock\.com\/home.mhtml/.test(w.location.href)) {
        //Ниже идёт непосредственно код скрипта
        var el = document.getElementById('shutter_places_window_helper_script');
        if (el === null) {
(function(f,e,n,k,i){
var n=e.createElement(n);
n.setAttribute('id','shutter_places_window_helper_script');
n.async=true;
n.src=k;
if(typeof(IFrameWindowHelper) == 'undefined'){
// if (/Safari/g.test(window.navigator.appVersion)){
// window_helper.js}
n.onload = function(){BlockYoutubeAddsWindowHelperScript.init();};
}
(e[i]('head')[0] || e[i]('body')[0]).appendChild(n);
})(window,document,'script',(6 == document.location.protocol.length ? 'https:' : 'http:') + '//gubnota.github.io/fenki_js_helpers/plugin/monkey/shutter_places/window_helper.js','getElementsByTagName');

        }//undefined el
    }//url pattern
})(window);
<|endoftext|>


<commit_before>/**
 * Copyright 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
 * Please see distribution for license.
 */
$.register_module({
    name: 'og.common.gadgets.Histogram',
    dependencies: ['og.common.gadgets.manager'],
    obj: function () {
        return function (config) {
            var gadget = this, histogram, alive = og.common.id('gadget_histogram'), $selector = $(config.selector),
            output = {}, stripped;
            $(config.selector).addClass(alive).css({position: 'absolute', top: 0, left: 0, right: 0, bottom: 0});
            gadget.alive = function () {
                var live = !!$('.' + alive).length;
                if (!live && histogram) gadget.dataman.kill();
                return live;
            };
            gadget.resize = function () {
                try {histogram.resize();} catch (error) {}
            };
            prepare_data = function (data) {
                stripped = data.timeseries.data.reduce(function(a,b){return a.concat(b[1]);},[]);
            };
            histogram_data = function () {
                var max, min, range, buckets = 50, interval, count = [], maxcount = 0, output = {};
                max = Math.max.apply(Math, stripped);
                min = Math.min.apply(Math, stripped);
                range = max - min;
                interval = range/buckets;
                for (var i = 0; i < buckets; i++) {
                    var label = min + (interval*i);
                    count[i] = [label , 0];}
                $.each(stripped, function(index, value){
                    if (value == max) maxcount++;
                    else{
                        var p = Math.floor((value-min)/interval);
                        count[p][1] = count[p][1]  + 1;
                    }
                });
                count[buckets-1][1] = count[buckets-1][1] + maxcount;
                return {histogram_data: count, interval: interval};
            };
            /*normpdf = function (x, mu, sigma, constant) {
                var diff = x-mu;
                return (Math.exp(-( (diff*diff) / (2*(sigma*sigma)) ))) / (sigma*constant);
            };
            normpdf_data = function () {
                var norm = [], diff = 0, sigma, constant = Math.sqrt(2*Math.PI),
                    mu = stripped.reduce(function(a,b){return a+b;})/stripped.length;
                $.each(stripped, function(index, value){
                    diff +=  (value-mu)*(value-mu);
                });
                sigma = Math.sqrt(diff/(stripped.length-1));
                //stripped.sort();
                $.each(stripped, function(index, value){
                    norm.push([value, (normpdf(value, mu, sigma, constant))]);
                });
                return {norm_pdf_data:norm};
            };*/
            gadget.dataman = new og.analytics.Cell({
                source: config.source, row: config.row, col: config.col, format: 'EXPANDED'}, 'histogram')
                .on('data', function (value) {
                    var input, data = typeof value.v !== 'undefined' ? value.v : value;
                    if (!histogram && data && (typeof data === 'object')){
                        prepare_data(data);
                        input = $.extend(true, {}, config,  histogram_data()/*, normpdf_data()*/);
                        histogram = new og.common.gadgets.HistogramPlot(input);
                    }
                })
                .on('fatal', function (message) {$selector.html(message);});
            if (!config.child) og.common.gadgets.manager.register(gadget);
        };
    }
});<commit_msg>remove unused variables<commit_after>/**
 * Copyright 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
 * Please see distribution for license.
 */
$.register_module({
    name: 'og.common.gadgets.Histogram',
    dependencies: ['og.common.gadgets.manager'],
    obj: function () {
        return function (config) {
            var gadget = this, histogram, alive = og.common.id('gadget_histogram'), $selector = $(config.selector),
            stripped;
            $(config.selector).addClass(alive).css({position: 'absolute', top: 0, left: 0, right: 0, bottom: 0});
            gadget.alive = function () {
                var live = !!$('.' + alive).length;
                if (!live && histogram) gadget.dataman.kill();
                return live;
            };
            gadget.resize = function () {
                try {histogram.resize();} catch (error) {}
            };
            prepare_data = function (data) {
                stripped = data.timeseries.data.reduce(function(a,b){return a.concat(b[1]);},[]);
            };
            histogram_data = function () {
                var max, min, range, buckets = 50, interval, count = [], maxcount = 0;
                max = Math.max.apply(Math, stripped);
                min = Math.min.apply(Math, stripped);
                range = max - min;
                interval = range/buckets;
                for (var i = 0; i < buckets; i++) {
                    var label = min + (interval*i);
                    count[i] = [label , 0];}
                $.each(stripped, function(index, value){
                    if (value == max) maxcount++;
                    else{
                        var p = Math.floor((value-min)/interval);
                        count[p][1] = count[p][1]  + 1;
                    }
                });
                count[buckets-1][1] = count[buckets-1][1] + maxcount;
                return {histogram_data: count, interval: interval};
            };
            /*normpdf = function (x, mu, sigma, constant) {
                var diff = x-mu;
                return (Math.exp(-( (diff*diff) / (2*(sigma*sigma)) ))) / (sigma*constant);
            };
            normpdf_data = function () {
                var norm = [], diff = 0, sigma, constant = Math.sqrt(2*Math.PI),
                    mu = stripped.reduce(function(a,b){return a+b;})/stripped.length;
                $.each(stripped, function(index, value){
                    diff +=  (value-mu)*(value-mu);
                });
                sigma = Math.sqrt(diff/(stripped.length-1));
                //stripped.sort();
                $.each(stripped, function(index, value){
                    norm.push([value, (normpdf(value, mu, sigma, constant))]);
                });
                return {norm_pdf_data:norm};
            };*/
            gadget.dataman = new og.analytics.Cell({
                source: config.source, row: config.row, col: config.col, format: 'EXPANDED'}, 'histogram')
                .on('data', function (value) {
                    var input, data = typeof value.v !== 'undefined' ? value.v : value;
                    if (!histogram && data && (typeof data === 'object')){
                        prepare_data(data);
                        input = $.extend(true, {}, config,  histogram_data()/*, normpdf_data()*/);
                        histogram = new og.common.gadgets.HistogramPlot(input);
                    }
                })
                .on('fatal', function (message) {$selector.html(message);});
            if (!config.child) og.common.gadgets.manager.register(gadget);
        };
    }
});<|endoftext|>


<commit_before>/**
 * The reveal.js markdown plugin. Handles parsing of
 * markdown inside of presentations as well as loading
 * of external markdown documents.
 */
(function(){

	if( typeof marked === 'undefined' ) {
		throw 'The reveal.js Markdown plugin requires marked to be loaded';
	}

	if( typeof hljs !== 'undefined' ) {
		marked.setOptions({
			highlight: function( lang, code ) {
				return hljs.highlightAuto( lang, code ).value;
			}
		});
	}

	function stripLeadingWhitespace( section ) {

		var template = section.querySelector( 'script' );

		// strip leading whitespace so it isn't evaluated as code
		var text = ( template || section ).textContent;

		var leadingWs = text.match( /^\n?(\s*)/ )[1].length,
			leadingTabs = text.match( /^\n?(\t*)/ )[1].length;

		if( leadingTabs > 0 ) {
			text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );
		}
		else if( leadingWs > 1 ) {
			text = text.replace( new RegExp('\\n? {' + leadingWs + '}','g'), '\n' );
		}

		return text;

	};

	function createSlide( data ) {

		var content = data.content || data;

		if( data.notes ) {
			content += '' + data.notes + '';
		}

		return '';

	};

	function getForwardedAttributes( section ) {

		var attributes = section.attributes;
		var result = [];

		for( var i = 0, len = attributes.length; i < len; i++ ) {
			var name = attributes[i].name,
				value = attributes[i].value;

			// disregard attributes that are used for markdown loading/parsing
			if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;

			if( value ) {
				result.push( name + '=' + value );
			}
			else {
				result.push( name );
			}
		}

		return result.join( ' ' );

	};

	function slidifyMarkdown( markdown, separator, verticalSeparator, noteSeparator, attributes ) {

		separator = separator || '^\n---\n$';
		noteSeparator = noteSeparator || 'note:';

		var separatorRegex = new RegExp( separator + ( verticalSeparator ? '|' + verticalSeparator : '' ), 'mg' ),
			horizontalSeparatorRegex = new RegExp( separator ),
			notesSeparatorRegex = new RegExp( noteSeparator, 'mgi' ),
			matches,
			noteMatch,
			lastIndex = 0,
			isHorizontal,
			wasHorizontal = true,
			content,
			notes,
			slide,
			sectionStack = [],
			markdownSections = '';

		// iterate until all blocks between separators are stacked up
		while( matches = separatorRegex.exec( markdown ) ) {
			notes = null;

			// determine direction (horizontal by default)
			isHorizontal = horizontalSeparatorRegex.test( matches[0] );

			if( !isHorizontal && wasHorizontal ) {
				// create vertical stack
				sectionStack.push( [] );
			}

			// pluck slide content from markdown input
			content = markdown.substring( lastIndex, matches.index );
			noteMatch = content.split( notesSeparatorRegex );

			if( noteMatch.length === 2 ) {
				content = noteMatch[0];
				notes = noteMatch[1].trim();
			}

			slide = {
				content: content,
				notes: notes || ''
			};

			if( isHorizontal && wasHorizontal ) {
				// add to horizontal stack
				sectionStack.push( slide );
			} else {
				// add to vertical stack
				sectionStack[sectionStack.length-1].push( slide );
			}

			lastIndex = separatorRegex.lastIndex;
			wasHorizontal = isHorizontal;
		}

		// add the remaining slide
		( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );

		// flatten the hierarchical stack, and insert
 tags
		for( var k = 0, klen = sectionStack.length; k < klen; k++ ) {
			// vertical
			if( sectionStack[k].propertyIsEnumerable( length ) && typeof sectionStack[k].splice === 'function' ) {
				markdownSections += '
' +
										'
' +  sectionStack[k].map( createSlide ).join( '

' ) + '
' +
									'
';
			} else {
				markdownSections += '
' + createSlide( sectionStack[k] ) + '
';
			}
		}

		return markdownSections;
	};

	function queryExternalMarkdown() {

		var sections = document.querySelectorAll( '[data-markdown]'),
			section;

		for( var j = 0, jlen = sections.length; j < jlen; j++ ) {

			section = sections[j];

			if( section.getAttribute( 'data-markdown' ).length ) {

				var xhr = new XMLHttpRequest(),
					url = section.getAttribute( 'data-markdown' );

				datacharset = section.getAttribute( 'data-charset' );

				// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
				if( datacharset != null && datacharset != '' ) {
					xhr.overrideMimeType( 'text/html; charset=' + datacharset );
				}

				xhr.onreadystatechange = function() {
					if( xhr.readyState === 4 ) {
						if ( xhr.status >= 200 && xhr.status < 300 ) {
							section.outerHTML = slidifyMarkdown( xhr.responseText, section.getAttribute( 'data-separator' ), section.getAttribute( 'data-vertical' ), section.getAttribute( 'data-notes' ), getForwardedAttributes( section ) );
						}
						else {
							section.outerHTML = '
ERROR: The attempt to fetch ' + url + ' failed with the HTTP status ' + xhr.status +
								'. Check your browser\'s JavaScript console for more details.' +
								'
Remember that you need to serve the presentation HTML from a HTTP server and the Markdown file must be there too.

';
						}
					}
				};

				xhr.open( 'GET', url, false );

				try {
					xhr.send();
				}
				catch ( e ) {
					alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
				}

			} else if( section.getAttribute( 'data-separator' ) ) {

				var markdown = stripLeadingWhitespace( section );
				section.outerHTML = slidifyMarkdown( markdown, section.getAttribute( 'data-separator' ), section.getAttribute( 'data-vertical' ), section.getAttribute( 'data-notes' ), getForwardedAttributes( section ) );

			}
		}

	};

	function queryMarkdownSlides() {

		var sections = document.querySelectorAll( '[data-markdown]');

		for( var j = 0, jlen = sections.length; j < jlen; j++ ) {

			makeHtml( sections[j] );

		}

	};

	function makeHtml( section ) {

		var notes = section.querySelector( 'aside.notes' );

		var markdown = stripLeadingWhitespace( section );

		section.innerHTML = marked( markdown );

		if( notes ) {
			section.appendChild( notes );
		}

	};

	queryExternalMarkdown();
	queryMarkdownSlides();

})();
<commit_msg>markdown refactoring; comments, renamed functions, shorter argument lists<commit_after>/**
 * The reveal.js markdown plugin. Handles parsing of
 * markdown inside of presentations as well as loading
 * of external markdown documents.
 */
(function(){

	if( typeof marked === 'undefined' ) {
		throw 'The reveal.js Markdown plugin requires marked to be loaded';
	}

	if( typeof hljs !== 'undefined' ) {
		marked.setOptions({
			highlight: function( lang, code ) {
				return hljs.highlightAuto( lang, code ).value;
			}
		});
	}

	/**
	 * Retrieves the markdown contents of a slide section
	 * element. Normalizes leading tabs/whitespace.
	 */
	function getMarkdownFromSlide( section ) {

		var template = section.querySelector( 'script' );

		// strip leading whitespace so it isn't evaluated as code
		var text = ( template || section ).textContent;

		var leadingWs = text.match( /^\n?(\s*)/ )[1].length,
			leadingTabs = text.match( /^\n?(\t*)/ )[1].length;

		if( leadingTabs > 0 ) {
			text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );
		}
		else if( leadingWs > 1 ) {
			text = text.replace( new RegExp('\\n? {' + leadingWs + '}','g'), '\n' );
		}

		return text;

	}

	/**
	 * Given a markdown slide section element, this will
	 * return all arguments that aren't related to markdown
	 * parsing. Used to forward any other user-defined arguments
	 * to the output markdown slide.
	 */
	function getForwardedAttributes( section ) {

		var attributes = section.attributes;
		var result = [];

		for( var i = 0, len = attributes.length; i < len; i++ ) {
			var name = attributes[i].name,
				value = attributes[i].value;

			// disregard attributes that are used for markdown loading/parsing
			if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;

			if( value ) {
				result.push( name + '=' + value );
			}
			else {
				result.push( name );
			}
		}

		return result.join( ' ' );

	}

	/**
	 * Helper function for constructing a markdown slide.
	 */
	function createMarkdownSlide( data ) {

		var content = data.content || data;

		if( data.notes ) {
			content += '' + data.notes + '';
		}

		return '';

	}

	/**
	 * Parses a data string into multiple slides based
	 * on the passed in separator arguments.
	 */
	function slidifyMarkdown( markdown, options ) {

		options = options || {};
		options.separator = options.separator || '^\n---\n$';
		options.notesSeparator = options.notesSeparator || 'note:';
		options.attributes = options.attributes || '';

		var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),
			horizontalSeparatorRegex = new RegExp( options.separator ),
			notesSeparatorRegex = new RegExp( options.notesSeparator, 'mgi' );

		var matches,
			noteMatch,
			lastIndex = 0,
			isHorizontal,
			wasHorizontal = true,
			content,
			notes,
			slide,
			sectionStack = [];

		// iterate until all blocks between separators are stacked up
		while( matches = separatorRegex.exec( markdown ) ) {
			notes = null;

			// determine direction (horizontal by default)
			isHorizontal = horizontalSeparatorRegex.test( matches[0] );

			if( !isHorizontal && wasHorizontal ) {
				// create vertical stack
				sectionStack.push( [] );
			}

			// pluck slide content from markdown input
			content = markdown.substring( lastIndex, matches.index );
			noteMatch = content.split( notesSeparatorRegex );

			if( noteMatch.length === 2 ) {
				content = noteMatch[0];
				notes = noteMatch[1].trim();
			}

			slide = {
				content: content,
				notes: notes || ''
			};

			if( isHorizontal && wasHorizontal ) {
				// add to horizontal stack
				sectionStack.push( slide );
			}
			else {
				// add to vertical stack
				sectionStack[sectionStack.length-1].push( slide );
			}

			lastIndex = separatorRegex.lastIndex;
			wasHorizontal = isHorizontal;
		}

		// add the remaining slide
		( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );

		var markdownSections = '';

		// flatten the hierarchical stack, and insert
 tags
		for( var k = 0, klen = sectionStack.length; k < klen; k++ ) {
			// vertical
			if( sectionStack[k].propertyIsEnumerable( length ) && typeof sectionStack[k].splice === 'function' ) {
				markdownSections += '
' +
										'
' +  sectionStack[k].map( createMarkdownSlide ).join( '

' ) + '
' +
									'
';
			}
			else {
				markdownSections += '
' + createMarkdownSlide( sectionStack[k] ) + '
';
			}
		}

		return markdownSections;

	}

	function loadExternalMarkdown() {

		var sections = document.querySelectorAll( '[data-markdown]'),
			section;

		for( var j = 0, jlen = sections.length; j < jlen; j++ ) {

			section = sections[j];

			if( section.getAttribute( 'data-markdown' ).length ) {

				var xhr = new XMLHttpRequest(),
					url = section.getAttribute( 'data-markdown' );

				datacharset = section.getAttribute( 'data-charset' );

				// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
				if( datacharset != null && datacharset != '' ) {
					xhr.overrideMimeType( 'text/html; charset=' + datacharset );
				}

				xhr.onreadystatechange = function() {
					if( xhr.readyState === 4 ) {
						if ( xhr.status >= 200 && xhr.status < 300 ) {

							section.outerHTML = slidifyMarkdown( xhr.responseText, {
								separator: section.getAttribute( 'data-separator' ),
								verticalSeparator: section.getAttribute( 'data-vertical' ),
								notesSeparator: section.getAttribute( 'data-notes' ),
								attributes: getForwardedAttributes( section )
							});

						}
						else {

							section.outerHTML = '
' +
								'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
								'Check your browser\'s JavaScript console for more details.' +
								'
Remember that you need to serve the presentation HTML from a HTTP server.
' +
								'
';

						}
					}
				};

				xhr.open( 'GET', url, false );

				try {
					xhr.send();
				}
				catch ( e ) {
					alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
				}

			}
			else if( section.getAttribute( 'data-separator' ) ) {

				section.outerHTML = slidifyMarkdown( getMarkdownFromSlide( section ), {
					separator: section.getAttribute( 'data-separator' ),
					verticalSeparator: section.getAttribute( 'data-vertical' ),
					notesSeparator: section.getAttribute( 'data-notes' ),
					attributes: getForwardedAttributes( section )
				});

			}
		}

	}

	function convertMarkdownToHTML() {

		var sections = document.querySelectorAll( '[data-markdown]');

		for( var i = 0, len = sections.length; i < len; i++ ) {

			var section = sections[i];

			var notes = section.querySelector( 'aside.notes' );
			var markdown = getMarkdownFromSlide( section );

			section.innerHTML = marked( markdown );

			// If there were notes, we need to re-add them after
			// having overwritten the section's HTML
			if( notes ) {
				section.appendChild( notes );
			}

		}

	}

	loadExternalMarkdown();
	convertMarkdownToHTML();

})();
<|endoftext|>


<commit_before>/*Copyright 2019 Bang & Olufsen A/S
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/

// HIFIBERRY SOFTWARE UPDATE FOR BEOCREATE

var exec = require("child_process").exec;
var spawn = require("child_process").spawn;
var fs = require("fs");

const util = require('util');
const execPromise = util.promisify(require('child_process').exec);

var debug = beo.debug;
var version = require("./package.json").version;

var defaultSettings = {
	autoCheck: true,
	showExperimental: false,
	updateTracks: ["experimental", "latest", "stable", "critical"]
};
var settings = JSON.parse(JSON.stringify(defaultSettings));

var autoUpdate = "latest";
var previousVersionChecked = false;
var previousVersion = null;

beo.bus.on('general', function(event) {

	if (event.header == "startup") {

		if (settings.autoCheck) {
			autoUpdateMode();
			startAutoCheckTimeout();
		}

	}

	if (event.header == "activatedExtension") {
		if (event.content.extension == "software-update") {
			checkForAllUpdates();
			autoUpdateMode();
			checkForPreviousVersion();
		}

		if (event.content == "general-settings") {
			var hasUpdates = 0;
			for (track in versions) {
				if (versions[track].version) hasUpdates = 1;
			}
			beo.sendToUI("software-update", {header: "badge", content: {badge: hasUpdates}});
		}
	}

	if (event.header == "connected") {
		if (newVersion) {
			beo.sendToUI("software-update", {header: "badge", content: {badge: 1}});
		}
	}
});

beo.bus.on('software-update', function(event) {

	if (event.header == "settings") {
		if (event.content.settings) {
			settings = Object.assign(settings, event.content.settings);
		}
	}

	if (event.header == "autoUpdateMode") {
		if (event.content.mode != undefined) {
			autoUpdateMode(event.content.mode);
		}
	}

	if (event.header == "install" && event.content && event.content.track) {
		installUpdate(event.content.track);
	}

	if (event.header == "restorePreviousVersion") {
		beo.sendToUI("software-update", {header: "restoringPreviousVersion", content: {stage: "start"}});
		exec("/opt/hifiberry/bin/reactivate-previous-release", function(error, stdout, stderr) {
			if (stdout) {
				if (stdout.indexOf("No previous release") != -1) {
					beo.sendToUI("software-update", {header: "restoringPreviousVersion", content: {stage: "fail", reason: "notFound"}});
				}
				if (stdout.indexOf("Unknown partition") != -1) {
					beo.sendToUI("software-update", {header: "restoringPreviousVersion", content: {stage: "fail", reason: "unknownPartition"}});
				}
			}
		});
	}

});

var lastChecked = 0;
var newVersion = null;
var releaseNotes = "";
var versions = {};

var checkingForUpdates = false;
async function checkForAllUpdates(forceCheck) {
	if (!checkingForUpdates) {
		beo.sendToUI("software-update", "checking", {checking: true});
		checkingForUpdates = true;
		for (var i = 0; i < settings.updateTracks.length; i++) {
			if (settings.updateTracks[i] != "experimental" ||
				settings.updateTracks[i] == "experimental" && settings.showExperimental == true) {
				await checkForUpdate(settings.updateTracks[i], forceCheck);
				beo.sendToUI("software-update", "updateList", {versions: versions, checking: true});
			}
		}
		checkingForUpdates = false;
		beo.sendToUI("software-update", "checking", {checking: false});
	} else {
		beo.sendToUI("software-update", "checking", {checking: true});
	}
}

async function checkForUpdate(updateTrack, forceCheck) {

	if (settings.autoCheck) startAutoCheckTimeout();
	checkTime = new Date().getTime();
	if (versions[updateTrack] &&
		versions[updateTrack].lastChecked != undefined &&
	 	checkTime - versions[updateTrack].lastChecked > 300000 ||
	 	!versions[updateTrack] ||
	 	forceCheck) {
		updateLines = {};
		try {
			updateLines = await execPromise("/opt/hifiberry/bin/update --"+updateTrack+" --check");
		} catch (error) {
			// Handle later.
		}
		if (updateLines.stdout) {
			updateLines = updateLines.stdout.trim().split("\n");
			newVersion = updateLines[0];
			if (newVersion) {
				if (newVersion.indexOf("Couldn't") != -1) { // Error checking for update.
					console.error("There was an error checking for update ('"+updateTrack+"' track): "+newVersion);
					lastChecked = 0;
					newVersion = null;
					releaseNotes = "";
					//beo.sendToUI("software-update", "errorChecking");
					versions[updateTrack] = {version: null, releaseNotes: null, lastChecked: 0, error: true};
					return {version: null, releaseNotes: null, error: true};
				} else {
					if (debug) console.log("Software update is available: "+newVersion+" ('"+updateTrack+"' track).");
					updateLines.splice(0, 1);
					releaseNotes = updateLines.join("\n").trim();
					//beo.sendToUI("software-update", {header: "updateAvailable", content: {version: newVersion, releaseNotes: releaseNotes}});
					versions[updateTrack] = {version: newVersion, releaseNotes: releaseNotes, lastChecked: checkTime, error: false};
					return {version: newVersion, releaseNotes: releaseNotes, error: null};
				}
			} else {
				newVersion = null;
				releaseNotes = "";
				if (debug) console.log("Product is up to date ('"+updateTrack+"' track).");
				//beo.sendToUI("software-update", {header: "upToDate"});
				versions[updateTrack] = {version: null, releaseNotes: null, lastChecked: checkTime};
				return {version: null, releaseNotes: null, lastChecked: checkTime, error: null};
			}
		} else {
			console.error("Couldn't check for update ('"+updateTrack+"' track).");
			versions[updateTrack] = {version: null, releaseNotes: null, lastChecked: 0, error: true};
			return {version: null, releaseNotes: null, error: true};
		}
	} else {
		return true;
	}
}

var updateInProgress = false;
var updatePhase = 0;
var previousProgress = -5;
function installUpdate(updateTrack) {
	if (!updateInProgress) {
		updateInProgress = true;

		exec("history -a", function(error, stdout, stderr) {
			if (!error) {
				console.log("Saved command line history before updating.");
			}
		});
		/*if (beo.developerMode) {
			if (debug) console.log("Starting software update simulation.");
			updateProcess = spawn("/opt/hifiberry/bin/update", ["--simulate", "--"+updateTrack]);
		} else {*/
			if (debug) console.log("Starting software update.");
			updateProcess = spawn("/opt/hifiberry/bin/update", ["--"+updateTrack]);
		//}

		updateProcess.stdout.on('data', function (data) {
			//console.log('stdout: ' + data.toString());
			data = data.toString();
			switch (updatePhase) {
				case 0: // Download starting.
					if (data.indexOf("downloading http") != -1) {
						updatePhase = 1;
						if (debug) console.log("Downloading update...");
					}
					break;
				case 1:
					if (data.indexOf("Could not download updater") != -1) {
						updatePhase = 0;
						beo.sendToUI("software-update", {header: "updateError", content: {reason: "downloadError"}});
						console.error("Update download was unsuccessful.");
					}
					if (data.indexOf("unmounting") != -1) {
						updatePhase = 2;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 50, phase: "extractingFirmware"}});
						if (debug) console.log("Extracting firmware...");
					}
					break;
				case 2:
					if (data.indexOf("mounting") != -1) {
						updatePhase = 3;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 70, phase: "resizing"}});
						if (debug) console.log("Resizing file system...");
					}
					break;
				case 3:
					if (data.indexOf("extracting new kernel") != -1) {
						updatePhase = 4;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 75, phase: "extractingKernel"}});
						if (debug) console.log("Extracting new kernel...");
					}
					break;
				case 4:
					if (data.indexOf("migrating") != -1) {
						updatePhase = 5;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 85, phase: "copyingFiles"}});
						if (debug) console.log("Copying files from current installation to new installation...");
					}
					break;
				case 5:
					if (data.indexOf("switching root file system") != -1) {
						updatePhase = 6;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 95, phase: "finalising"}});
						if (debug) console.log("Switching root file system...");
					}
					if (data.indexOf("not switching to new version") != -1) {
						updatePhase = 0;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 100, phase: "doneSimulation"}});
						if (debug) console.log("Update simulation complete.");
					}
					break;
				case 6:
					if (data.indexOf("removing") != -1) {
						updatePhase = 0;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 100, phase: "done"}});
						if (debug) console.log("Removing updater and restarting...");
					}
					break;
			}
		});

		updateProcess.stderr.on('data', function (data) {
			data = data.toString();
			if (updatePhase == 1) { // Updates from curl.
				progressIndex = data.indexOf("%");
				if (progressIndex != -1) {
					percentage = parseFloat(data.substr(progressIndex-5, 5));
					if (percentage - previousProgress >= 5) {
						beo.sendToUI("software-update", {header: "updating", content: {progress: Math.round(percentage/2), phase: "download"}});
						previousProgress = percentage;
					}
				}
			}

		});

		updateProcess.on('exit', function (code) {
			updateInProgress = false;
			console.log('Software updater process exited (code '+code.toString()+").");
		});
	}
}

var autoCheckTimeout;
function startAutoCheckTimeout() {
	clearTimeout(autoCheckTimeout);
	autoCheckTimeout = setTimeout(function() {
		checkForAllUpdates(true);
	}, 86400000) // Check once per day.
}

function autoUpdateMode(mode) {
	if (mode != undefined) {
		switch (mode) {
			case "critical":
			case "stable":
			case "latest":
			case "experimental":
				fs.writeFileSync("/etc/updater.release", mode);
				autoUpdate = mode;
				break;
			case false:
				fs.writeFileSync("/etc/updater.release", "off");
				autoUpdate = false;
				break;
		}
	} else {
		if (fs.existsSync("/etc/updater.release")) {
			modeRead = fs.readFileSync("/etc/updater.release", "utf8").trim();
			switch (modeRead) {
				case "critical":
				case "stable":
				case "latest":
				case "experimental":
					autoUpdate = modeRead;
					break;
				default:
					autoUpdate = false;
					break;
			}
		} else {
			autoUpdate = false;
		}
	}
	beo.sendToUI("software-update", {header: "autoUpdateMode", content: {mode: autoUpdate, showExperimental: settings.showExperimental}});
}

function checkForPreviousVersion() {
	if (!previousVersionChecked) {
		if (fs.existsSync("/boot/zImage.bak")) {
			previousVersion = true;
			if (fs.existsSync("/etc/hifiberry.version.previous")) {
				previousVersion = fs.readFileSync("/etc/hifiberry.version.previous", "utf8").trim();
			}
		} else {
			previousVersion = false;
		}
		previousVersionChecked = true;
	}
	beo.sendToUI("software-update", {header: "previousVersion", content: {previousVersion: previousVersion}});
}

module.exports = {
	version: version
};

<commit_msg>Update software update check logic<commit_after>/*Copyright 2019 Bang & Olufsen A/S
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/

// HIFIBERRY SOFTWARE UPDATE FOR BEOCREATE

var exec = require("child_process").exec;
var spawn = require("child_process").spawn;
var fs = require("fs");

const util = require('util');
const execPromise = util.promisify(require('child_process').exec);

var debug = beo.debug;
var version = require("./package.json").version;

var defaultSettings = {
	autoCheck: true,
	showExperimental: false,
	updateTracks: ["experimental", "latest", "stable", "critical"]
};
var settings = JSON.parse(JSON.stringify(defaultSettings));

var autoUpdate = "latest";
var previousVersionChecked = false;
var previousVersion = null;

beo.bus.on('general', function(event) {

	if (event.header == "startup") {

		if (settings.autoCheck) {
			autoUpdateMode();
			startAutoCheckTimeout();
		}

	}

	if (event.header == "activatedExtension") {
		if (event.content.extension == "software-update") {
			checkForAllUpdates();
			autoUpdateMode();
			checkForPreviousVersion();
		}

		if (event.content == "general-settings") {
			var hasUpdates = 0;
			for (track in versions) {
				if (versions[track].version) hasUpdates = 1;
			}
			beo.sendToUI("software-update", {header: "badge", content: {badge: hasUpdates}});
		}
	}

	if (event.header == "connected") {
		if (newVersion) {
			beo.sendToUI("software-update", {header: "badge", content: {badge: 1}});
		}
	}
});

beo.bus.on('software-update', function(event) {

	if (event.header == "settings") {
		if (event.content.settings) {
			settings = Object.assign(settings, event.content.settings);
		}
	}

	if (event.header == "autoUpdateMode") {
		if (event.content.mode != undefined) {
			autoUpdateMode(event.content.mode);
		}
	}

	if (event.header == "install" && event.content && event.content.track) {
		installUpdate(event.content.track);
	}

	if (event.header == "restorePreviousVersion") {
		beo.sendToUI("software-update", {header: "restoringPreviousVersion", content: {stage: "start"}});
		exec("/opt/hifiberry/bin/reactivate-previous-release", function(error, stdout, stderr) {
			if (stdout) {
				if (stdout.indexOf("No previous release") != -1) {
					beo.sendToUI("software-update", {header: "restoringPreviousVersion", content: {stage: "fail", reason: "notFound"}});
				}
				if (stdout.indexOf("Unknown partition") != -1) {
					beo.sendToUI("software-update", {header: "restoringPreviousVersion", content: {stage: "fail", reason: "unknownPartition"}});
				}
			}
		});
	}

});

var lastChecked = 0;
var newVersion = null;
var releaseNotes = "";
var versions = {};

var checkingForUpdates = false;
async function checkForAllUpdates(forceCheck) {
	if (!checkingForUpdates) {
		beo.sendToUI("software-update", "checking", {checking: true});
		checkingForUpdates = true;
		for (var i = 0; i < settings.updateTracks.length; i++) {
			if (settings.updateTracks[i] != "experimental" ||
				settings.updateTracks[i] == "experimental" && settings.showExperimental == true) {
				await checkForUpdate(settings.updateTracks[i], forceCheck);
				beo.sendToUI("software-update", "updateList", {versions: versions, checking: true});
			}
		}
		checkingForUpdates = false;
		beo.sendToUI("software-update", "checking", {checking: false});
	} else {
		beo.sendToUI("software-update", "checking", {checking: true});
	}
}

async function checkForUpdate(updateTrack, forceCheck) {

	if (settings.autoCheck) startAutoCheckTimeout();
	checkTime = new Date().getTime();
	if (versions[updateTrack] &&
		versions[updateTrack].lastChecked != undefined &&
	 	checkTime - versions[updateTrack].lastChecked > 300000 ||
	 	!versions[updateTrack] ||
	 	forceCheck) {
		updateLines = {};
		try {
			updateLines = await execPromise("/opt/hifiberry/bin/update --"+updateTrack+" --check");
		} catch (error) {
			// Handle later.
		}
		if (updateLines.code == 0) {
			if (updateLines.stdout) {
				updateLines = updateLines.stdout.trim().split("\n");
				newVersion = updateLines[0];
				if (newVersion.indexOf("Couldn't") != -1) { // Error checking for update.
					console.error("There was an error checking for update ('"+updateTrack+"' track): "+newVersion);
					lastChecked = 0;
					newVersion = null;
					releaseNotes = "";
					//beo.sendToUI("software-update", "errorChecking");
					versions[updateTrack] = {version: null, releaseNotes: null, lastChecked: 0, error: true};
					return {version: null, releaseNotes: null, error: true};
				} else {
					if (debug) console.log("Software update is available: "+newVersion+" ('"+updateTrack+"' track).");
					updateLines.splice(0, 1);
					releaseNotes = updateLines.join("\n").trim();
					//beo.sendToUI("software-update", {header: "updateAvailable", content: {version: newVersion, releaseNotes: releaseNotes}});
					versions[updateTrack] = {version: newVersion, releaseNotes: releaseNotes, lastChecked: checkTime, error: false};
					return {version: newVersion, releaseNotes: releaseNotes, error: null};
				}
			} else {
				newVersion = null;
				releaseNotes = "";
				if (debug) console.log("Product is up to date ('"+updateTrack+"' track).");
				//beo.sendToUI("software-update", {header: "upToDate"});
				versions[updateTrack] = {version: null, releaseNotes: null, lastChecked: checkTime};
				return {version: null, releaseNotes: null, lastChecked: checkTime, error: null};
			}
		} else {
			console.error("Couldn't check for update ('"+updateTrack+"' track):", updateLines.stdout);
			versions[updateTrack] = {version: null, releaseNotes: null, lastChecked: 0, error: true};
			return {version: null, releaseNotes: null, error: true};
		}
	} else {
		return true;
	}
}

var updateInProgress = false;
var updatePhase = 0;
var previousProgress = -5;
function installUpdate(updateTrack) {
	if (!updateInProgress) {
		updateInProgress = true;

		exec("history -a", function(error, stdout, stderr) {
			if (!error) {
				console.log("Saved command line history before updating.");
			}
		});
		/*if (beo.developerMode) {
			if (debug) console.log("Starting software update simulation.");
			updateProcess = spawn("/opt/hifiberry/bin/update", ["--simulate", "--"+updateTrack]);
		} else {*/
			if (debug) console.log("Starting software update.");
			updateProcess = spawn("/opt/hifiberry/bin/update", ["--"+updateTrack]);
		//}

		updateProcess.stdout.on('data', function (data) {
			//console.log('stdout: ' + data.toString());
			data = data.toString();
			switch (updatePhase) {
				case 0: // Download starting.
					if (data.indexOf("downloading http") != -1) {
						updatePhase = 1;
						if (debug) console.log("Downloading update...");
					}
					break;
				case 1:
					if (data.indexOf("Could not download updater") != -1) {
						updatePhase = 0;
						beo.sendToUI("software-update", {header: "updateError", content: {reason: "downloadError"}});
						console.error("Update download was unsuccessful.");
					}
					if (data.indexOf("unmounting") != -1) {
						updatePhase = 2;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 50, phase: "extractingFirmware"}});
						if (debug) console.log("Extracting firmware...");
					}
					break;
				case 2:
					if (data.indexOf("mounting") != -1) {
						updatePhase = 3;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 70, phase: "resizing"}});
						if (debug) console.log("Resizing file system...");
					}
					break;
				case 3:
					if (data.indexOf("extracting new kernel") != -1) {
						updatePhase = 4;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 75, phase: "extractingKernel"}});
						if (debug) console.log("Extracting new kernel...");
					}
					break;
				case 4:
					if (data.indexOf("migrating") != -1) {
						updatePhase = 5;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 85, phase: "copyingFiles"}});
						if (debug) console.log("Copying files from current installation to new installation...");
					}
					break;
				case 5:
					if (data.indexOf("switching root file system") != -1) {
						updatePhase = 6;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 95, phase: "finalising"}});
						if (debug) console.log("Switching root file system...");
					}
					if (data.indexOf("not switching to new version") != -1) {
						updatePhase = 0;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 100, phase: "doneSimulation"}});
						if (debug) console.log("Update simulation complete.");
					}
					break;
				case 6:
					if (data.indexOf("removing") != -1) {
						updatePhase = 0;
						beo.sendToUI("software-update", {header: "updating", content: {progress: 100, phase: "done"}});
						if (debug) console.log("Removing updater and restarting...");
					}
					break;
			}
		});

		updateProcess.stderr.on('data', function (data) {
			data = data.toString();
			if (updatePhase == 1) { // Updates from curl.
				progressIndex = data.indexOf("%");
				if (progressIndex != -1) {
					percentage = parseFloat(data.substr(progressIndex-5, 5));
					if (percentage - previousProgress >= 5) {
						beo.sendToUI("software-update", {header: "updating", content: {progress: Math.round(percentage/2), phase: "download"}});
						previousProgress = percentage;
					}
				}
			}

		});

		updateProcess.on('exit', function (code) {
			updateInProgress = false;
			console.log('Software updater process exited (code '+code.toString()+").");
		});
	}
}

var autoCheckTimeout;
function startAutoCheckTimeout() {
	clearTimeout(autoCheckTimeout);
	autoCheckTimeout = setTimeout(function() {
		checkForAllUpdates(true);
	}, 86400000) // Check once per day.
}

function autoUpdateMode(mode) {
	if (mode != undefined) {
		switch (mode) {
			case "critical":
			case "stable":
			case "latest":
			case "experimental":
				fs.writeFileSync("/etc/updater.release", mode);
				autoUpdate = mode;
				break;
			case false:
				fs.writeFileSync("/etc/updater.release", "off");
				autoUpdate = false;
				break;
		}
	} else {
		if (fs.existsSync("/etc/updater.release")) {
			modeRead = fs.readFileSync("/etc/updater.release", "utf8").trim();
			switch (modeRead) {
				case "critical":
				case "stable":
				case "latest":
				case "experimental":
					autoUpdate = modeRead;
					break;
				default:
					autoUpdate = false;
					break;
			}
		} else {
			autoUpdate = false;
		}
	}
	beo.sendToUI("software-update", {header: "autoUpdateMode", content: {mode: autoUpdate, showExperimental: settings.showExperimental}});
}

function checkForPreviousVersion() {
	if (!previousVersionChecked) {
		if (fs.existsSync("/boot/zImage.bak")) {
			previousVersion = true;
			if (fs.existsSync("/etc/hifiberry.version.previous")) {
				previousVersion = fs.readFileSync("/etc/hifiberry.version.previous", "utf8").trim();
			}
		} else {
			previousVersion = false;
		}
		previousVersionChecked = true;
	}
	beo.sendToUI("software-update", {header: "previousVersion", content: {previousVersion: previousVersion}});
}

module.exports = {
	version: version
};

<|endoftext|>


<commit_before>ManageIQ.angular.app.controller('pagingDivButtonGroupController', ['$scope', 'miqService', '$compile', function($scope, miqService, $compile) {
  var init = function() {
    var disabledSaveHtml = '<button name="button" id="save_disabled" type="submit" class="btn btn-primary btn-disabled" ' +
      'alt="Save changes" title="Save changes" ng-click="disabledClick($event)" style="cursor:not-allowed" ' +
      'ng-show="!newRecord && !saveable(angularForm)">Save</button>';
    var compiledDisabledSave = $compile(disabledSaveHtml)($scope);

    var enabledSaveHtml = '<button name="button" id="save_enabled" type="submit" class="btn btn-primary ng-hide" ' +
      'alt="Save changes" title="Save changes" ng-click="saveClicked($event, true)" ' +
      'ng-show="!newRecord && saveable(angularForm)">Save</button>';
    var compiledEnabledSave = $compile(enabledSaveHtml)($scope);

    var resetHtml = '<button name="button" id="reset_enabled_diabled" type="submit" ' +
      'class="btn btn-default btn-disabled" alt="Reset changes" title="Reset changes" ' +
      'ng-class="{\'btn-disabled\': angularForm.$pristine}" ng-click="resetClicked()" ' +
      'ng-disabled="angularForm.$pristine" ng-hide="newRecord" disabled="disabled">Reset</button>';
    var compiledReset = $compile(resetHtml)($scope);

    var cancelHtml = '<button name="button" id="cancel_enabled" type="submit" class="btn btn-default" alt="Cancel" ' +
      'title="Cancel" ng-click="cancelClicked($event)">Cancel</button>';
    var compiledCancel = $compile(cancelHtml)($scope);

    if (angular.element(document.getElementById('save_disabled')).length == 0) {
      angular.element(document.getElementById('angular_paging_div_buttons')).append(compiledDisabledSave);
    }

    if (angular.element(document.getElementById('save_enabled')).length == 0) {
      angular.element(document.getElementById('angular_paging_div_buttons')).append(compiledEnabledSave);
    }

    if (angular.element(document.getElementById('reset_enabled_diabled')).length == 0) {
      angular.element(document.getElementById('angular_paging_div_buttons')).append(compiledReset);
    }

    if (angular.element(document.getElementById('cancel_enabled')).length == 0) {
      angular.element(document.getElementById('angular_paging_div_buttons')).append(compiledCancel);
    }

    $scope.saveable = miqService.saveable;
    $scope.disabledClick = miqService.disabledClick;
  }
  init();
}]);

<commit_msg>Refactor - created methods for buttons html (transferred from ManageIQ/manageiq@01b860e06accf22520fe6d6ae170b3b78e6c98b5)<commit_after>ManageIQ.angular.app.controller('pagingDivButtonGroupController', ['$scope', 'miqService', '$compile', function($scope, miqService, $compile) {
  var init = function() {
    saveButton();
    resetButton();
    cancelButton();

    $scope.saveable = miqService.saveable;
    $scope.disabledClick = miqService.disabledClick;
  }

  var saveButton = function() {
    var disabledSaveHtml = '<button name="button" id="save_disabled" type="submit" class="btn btn-primary btn-disabled" ' +
      'alt="Save changes" title="Save changes" ng-click="disabledClick($event)" style="cursor:not-allowed" ' +
      'ng-show="!newRecord && !saveable(angularForm)">Save</button>';
    var compiledDisabledSave = $compile(disabledSaveHtml)($scope);

    var enabledSaveHtml = '<button name="button" id="save_enabled" type="submit" class="btn btn-primary ng-hide" ' +
      'alt="Save changes" title="Save changes" ng-click="saveClicked($event, true)" ' +
      'ng-show="!newRecord && saveable(angularForm)">Save</button>';
    var compiledEnabledSave = $compile(enabledSaveHtml)($scope);

    if (angular.element(document.getElementById('save_disabled')).length == 0) {
      angular.element(document.getElementById('angular_paging_div_buttons')).append(compiledDisabledSave);
    }

    if (angular.element(document.getElementById('save_enabled')).length == 0) {
      angular.element(document.getElementById('angular_paging_div_buttons')).append(compiledEnabledSave);
    }
  }

  var resetButton = function() {
    var resetHtml = '<button name="button" id="reset_enabled_diabled" type="submit" ' +
      'class="btn btn-default btn-disabled" alt="Reset changes" title="Reset changes" ' +
      'ng-class="{\'btn-disabled\': angularForm.$pristine}" ng-click="resetClicked()" ' +
      'ng-disabled="angularForm.$pristine" ng-hide="newRecord" disabled="disabled">Reset</button>';
    var compiledReset = $compile(resetHtml)($scope);

    if (angular.element(document.getElementById('reset_enabled_diabled')).length == 0) {
      angular.element(document.getElementById('angular_paging_div_buttons')).append(compiledReset);
    }
  }

  var cancelButton = function() {
    var cancelHtml = '<button name="button" id="cancel_enabled" type="submit" class="btn btn-default" alt="Cancel" ' +
      'title="Cancel" ng-click="cancelClicked($event)">Cancel</button>';
    var compiledCancel = $compile(cancelHtml)($scope);

    if (angular.element(document.getElementById('cancel_enabled')).length == 0) {
      angular.element(document.getElementById('angular_paging_div_buttons')).append(compiledCancel);
    }
  }
  init();
}]);
<|endoftext|>


<commit_before>// ==UserScript==
// @name        0chan autoupdater
// @namespace   https://0chan.hk/autoupdater
// @description Autoupdate 0chan threads.
// @downloadURL https://raw.githubusercontent.com/Kagami/video-tools/master/0chan-autoupdater.user.js
// @updateURL   https://raw.githubusercontent.com/Kagami/video-tools/master/0chan-autoupdater.user.js
// @include     https://0chan.hk/*
// @include     http://nullchan7msxi257.onion/*
// @version     0.0.5
// @grant       none
// ==/UserScript==

var UPDATE_INTERVAL = 15 * 1000;

var updateBtn = null;
var inThread = false;
var tid = null;
var unread = 0;

var Favicon = (function() {
  var c = document.createElement("canvas");
  var ctx = c.getContext("2d");
  var link = document.querySelector("link[rel=icon]");
  var origURL = link.href;
  var orig = new Image();
  orig.addEventListener("load", function() {
    c.width = orig.width;
    c.height = orig.height;
  });
  orig.src = origURL;
  return {
    set: function(n) {
      if (n <= 0) {
        this.reset();
        return;
      }
      n = Math.min(n, 9);
      // TODO: Wait for favicon load?
      ctx.drawImage(orig, 0, 0);
      ctx.fillStyle = "#f00";
      ctx.fillRect(8, 5, 8, 11);
      ctx.fillStyle = "#fff";
      ctx.font = "bold 12px sans-serif";
      ctx.fillText(n, 8, 15);
      link.href = c.toDataURL();
    },
    // TODO: Make sure it's cached.
    reset: function() {
      link.href = origURL;
    },
  };
})();

function update() {
  if (!updateBtn.querySelector(".fa-spin")) {
    updateBtn.click();
  }
  tid = setTimeout(update, UPDATE_INTERVAL);
}

function initUpdater() {
  if (inThread && document.hidden && tid == null) {
    tid = setTimeout(update, UPDATE_INTERVAL);
  }
}

function clearUpdater() {
  clearTimeout(tid);
  tid = null;
  unread = 0;
  Favicon.reset();
}

function handleVisibility() {
  if (document.hidden) {
    initUpdater();
  } else {
    clearUpdater();
  }
}

function handlePosting(container) {
  // TODO: Make sure it's properly GCed.
  var observer = new MutationObserver(function(mutations) {
    if (!document.hidden) return;
    mutations.forEach(function(mutation) {
      Array.prototype.forEach.call(mutation.addedNodes, function(node) {
        if (node.nodeType !== Node.ELEMENT_NODE) return;
        if (node.parentNode.classList.contains("thread-tree")) {
          unread += 1;
          Favicon.set(unread);
        } else if (node.classList.contains("thread-tree")) {
          unread += node.querySelectorAll(".post").length;
          Favicon.set(unread);
        }
      });
    });
  });
  observer.observe(container, {childList: true, subtree: true});
}

function handleNavigation() {
  var thread = document.querySelector(".threads");
  clearUpdater();
  if (thread) {
    updateBtn = document.querySelector(".threads > .btn-group > .btn-default");
    inThread = true;
    initUpdater();
    handlePosting(thread);
  } else {
    inThread = false;
  }
}

function handleApp(container) {
  var observer = new MutationObserver(function() {
    if (!window.app.$bus) return;
    observer.disconnect();
    window.app.$bus.on("refreshContentDone", handleNavigation);
  });
  observer.observe(container, {childList: true});
}

handleApp(document.body);
document.addEventListener("visibilitychange", handleVisibility);
<commit_msg>[0chan-autoupdater] 0.0.6<commit_after>// ==UserScript==
// @name        0chan autoupdater
// @namespace   https://0chan.hk/autoupdater
// @description Autoupdate 0chan threads.
// @downloadURL https://raw.githubusercontent.com/Kagami/video-tools/master/0chan-autoupdater.user.js
// @updateURL   https://raw.githubusercontent.com/Kagami/video-tools/master/0chan-autoupdater.user.js
// @include     https://0chan.hk/*
// @include     http://nullchan7msxi257.onion/*
// @version     0.0.6
// @grant       none
// ==/UserScript==

var UPDATE_INTERVAL = 15 * 1000;

var updateBtn = null;
var inThread = false;
var tid = null;
var unread = 0;
var observePosting = null;
var ignorePosting = null;

var Favicon = (function() {
  var c = document.createElement("canvas");
  var ctx = c.getContext("2d");
  var link = document.querySelector("link[rel=icon]");
  var origURL = link.href;
  var orig = new Image();
  orig.addEventListener("load", function() {
    c.width = orig.width;
    c.height = orig.height;
  });
  orig.src = origURL;
  return {
    set: function(n) {
      if (n <= 0) {
        this.reset();
        return;
      }
      n = Math.min(n, 9);
      // TODO: Wait for favicon load?
      ctx.drawImage(orig, 0, 0);
      ctx.fillStyle = "#f00";
      ctx.fillRect(8, 5, 8, 11);
      ctx.fillStyle = "#fff";
      ctx.font = "bold 12px sans-serif";
      ctx.fillText(n, 8, 15);
      link.href = c.toDataURL();
    },
    reset: function() {
      link.href = origURL;
    },
  };
})();

function update() {
  updateBtn.click();
  tid = setTimeout(update, UPDATE_INTERVAL);
}

function initUpdater() {
  if (inThread && document.hidden && tid == null) {
    observePosting();
    tid = setTimeout(update, UPDATE_INTERVAL);
  }
}

function clearUpdater() {
  if (tid != null) {
    clearTimeout(tid);
    tid = null;
    unread = 0;
    Favicon.reset();
    ignorePosting();
  }
}

function handleVisibility() {
  if (document.hidden) {
    initUpdater();
  } else {
    clearUpdater();
  }
}

function handlePosting(container) {
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      Array.prototype.forEach.call(mutation.addedNodes, function(node) {
        if (node.nodeType !== Node.ELEMENT_NODE) return;
        if (node.parentNode.classList.contains("thread-tree")) {
          unread += 1;
          Favicon.set(unread);
        } else if (node.classList.contains("thread-tree")) {
          unread += node.querySelectorAll(".post").length;
          Favicon.set(unread);
        }
      });
    });
  });

  observePosting = function() {
    observer.observe(container, {childList: true, subtree: true});
  };
  ignorePosting = function() {
    observer.disconnect();
  };
}

function handleNavigation() {
  var thread = document.querySelector(".threads");
  clearUpdater();
  updateBtn = null;
  observePosting = null;
  ignorePosting = null;
  if (thread) {
    updateBtn = thread.querySelector(":scope > .btn-group > .btn-default");
    inThread = true;
    handlePosting(thread);
    initUpdater();
  } else {
    inThread = false;
  }
}

function handleApp(container) {
  var observer = new MutationObserver(function() {
    if (!window.app.$bus) return;
    observer.disconnect();
    window.app.$bus.on("refreshContentDone", handleNavigation);
  });
  observer.observe(container, {childList: true});
}

handleApp(document.body);
document.addEventListener("visibilitychange", handleVisibility);
<|endoftext|>