<commit_before>var baseGraph;
var pathGraphs = [];

var minNumberOfNodes = 1;
var maxNumberOfNodes = 15;
var pathGraphsEnableEdgeHovering = false;
var baseGraphEnableEdgeHovering = true;
var maxIntegerInputLength = 3;
var maxFloatInputLength = 4;

$(document).ready(function () {
    window.blockUIImage = {
        message: $("#loading-gif"),
        baseZ: 2000,
        css: { "background-color": "transparent", "border": "none" },
        overlayCSS: { backgroundColor: 'white', opacity: 0.4 }
    };

    $('.float').keypress(function (event) {
        if (((event.which != 46 || $(this).val().indexOf('.') != -1) && (event.which < 48 || event.which > 57)) || $(this).val().length >= maxFloatInputLength) {
            event.preventDefault();
        }
    });

    $('.integer').keypress(function (event) {
        if ((event.which < 48 || event.which > 57) || $(this).val().length >= maxIntegerInputLength) {
            event.preventDefault();
        }
    });

    $("#computePathBtn").attr("disabled", true);

    $("#drawGraphBtn").click(function () {
        var n = $("#nValue").val();

        if (n >= minNumberOfNodes && n <= maxNumberOfNodes) {
            if (baseGraph) {
                baseGraph.graph.clear();
                baseGraph.refresh();
            }

            baseGraph = new sigma({
                graph: drawBaseGraph(parseInt(n)),
                renderer: {
                    container: document.getElementById('base-graph-container'),
                    type: 'canvas'
                },
                settings: {
                    enableEdgeHovering: baseGraphEnableEdgeHovering,
                    edgeHoverSizeRatio: 3,
                    edgeLabelSize: 'proportional',
                    defaultEdgeLabelSize: 13,
                    defaultEdgeLabelColor: 'green',
                    zoomingRatio: 1.0
                }
            });
            sigma.plugins.dragNodes(baseGraph, baseGraph.renderers[0]);

            $("#computePathBtn").attr("disabled", false);
        }
        else {
            showModalWarning("Należy podać poprawną liczbę wierzchołków grafu z przedziału (" + minNumberOfNodes + ";" + maxNumberOfNodes + ")");
        }
    });

    $("#computePathBtn").click(function () {
        var popSize = $("#pValue").val();
        var mutationRate = $("#mValue").val();
        var crossoverRate = $("#kValue").val();
        var iterationsNumber = $("#iValue").val();

        if (popSize.length > 0 && mutationRate.length > 0 && crossoverRate.length > 0 && iterationsNumber.length > 0) {
            // $.blockUI(window.blockUIImage);

            var model =
                {
                    PopSize: parseInt(popSize),
                    MutationRate: parseFloat(mutationRate),
                    CrossoverRate: parseFloat(crossoverRate),
                    IterationsNumber: parseInt(iterationsNumber),
                    IdList: prepareMatrixOfNodesIds(),
                    WeightMatrix: prepareMatrixOfEdges()
                };

            $.ajax({
                type: "POST",
                contentType: "application/json",
                dataType: 'json',
                url: '/api/tspresolve',
                data: JSON.stringify(model),
                success: function (result) {

                    google.charts.load('current', { 'packages': ['corechart'] });
                    google.charts.setOnLoadCallback(drawChart);

                    function drawChart() {
                        drawLineChart(result.Iterations);
                        drawScatterChart(result.Iterations);
                    };

                    for (var i = 0; i < pathGraphs.length; i++) {
                        pathGraphs[i].graph.clear();
                        pathGraphs[i].refresh();
                    }
                    pathGraphs = [];

                    drawPathGraph("first-path-graph-container", result.Iterations[Math.floor(Math.random() * result.Iterations.length)].Resolves[0].OrderedIdList);
                    drawPathGraph("second-path-graph-container", result.Iterations[Math.floor(Math.random() * result.Iterations.length)].Resolves[0].OrderedIdList);
                    drawPathGraph("third-path-graph-container", result.Iterations[Math.floor(Math.random() * result.Iterations.length)].Resolves[0].OrderedIdList);
                    drawPathGraph("fourth-path-graph-container", result.Iterations[Math.floor(Math.random() * result.Iterations.length)].Resolves[0].OrderedIdList);

                    //     $.unblockUI();
                },
                error: function () {
                    //   $.unblockUI();
                }
            });
        }
        else {
            showModalWarning("Należy podać wszystkie wymagane parametry");
        }
    });

    $('#clearBtn').click(function () {
        location.reload(true);
    });
});

function drawPathGraph(container, nodes) {
    var g = {
        nodes: [],
        edges: []
    };

    var baseNodes = baseGraph.graph.nodes();

    for (var i = 0; i < baseNodes.length; i++) {
        g.nodes.push({
            id: container + "_" + i,
            x: baseNodes[i].x,
            y: baseNodes[i].y,
            label: baseNodes[i].label,
            size: 1,
            color: '#617db4'
        });
    }

    for (var i = 0; i < nodes.length - 1; i++) {
        g.edges.push({
            id: container + "_" + "e" + "_" + i,
            source: container + "_" + nodes[i],
            target: container + "_" + nodes[i + 1],
            size: 1,
            color: '#ccc',
            type: ['line']
        });
    }

    pathGraphs.push(new sigma({
        graph: g,
        renderer: {
            container: document.getElementById(container),
            type: 'canvas'
        },
        settings: {
            enableEdgeHovering: pathGraphsEnableEdgeHovering,
            edgeHoverSizeRatio: 3,
            edgeLabelSize: 'proportional',
            defaultEdgeLabelSize: 13,
            defaultEdgeLabelColor: 'green',
            zoomingRatio: 1.0,
            minNodeSize: 1,
            maxNodeSize: 6
        }
    }));
};

function drawBaseGraph(n) {
    var g = {
        nodes: [],
        edges: []
    };

    for (var i = 0; i < n; i++) {
        g.nodes.push({
            id: 'n' + i,
            index: i,
            label: 'N' + (i + 1),
            x: Math.random(),
            y: Math.random(),
            size: 1,
            color: '#617db4'
        });
    }

    for (i = n - 1; i >= 0; i--) {
        for (var j = 0; j < i; j++) {
            if (i != j) {
                var distance = Math.ceil(computeDistanceBetweenNodes(g.nodes[i].x, g.nodes[i].y, g.nodes[j].x, g.nodes[j].y) * 5);
                g.edges.push({
                    id: 'e' + "_" + i + "_" + j,
                    source: 'n' + i,
                    target: 'n' + j,
                    size: 1,
                    color: '#ccc',
                    label: "" + distance,
                    type: ['line']
                });
            }
        }
    }
    return g;
};

function drawLineChart(iterations) {
    var rawData = [];
    rawData.push(['X', 'F1', 'F2']);
    for (var i = 0; i < iterations.length; i++) {
        var item = [iterations[i].Id, iterations[i].Resolves[iterations[i].BestFirstFuncResolveIndex].FirstCost, iterations[i].Resolves[iterations[i].BestSecondFuncResolveIndex].SecondCost];
        rawData.push(item);
    }

    var data = google.visualization.arrayToDataTable(rawData);

    //var data = google.visualization.arrayToDataTable([
    //     ['X', 'F1', 'F2'],
    //     ['1', 87, 51],
    //     ['2', 75, 50],
    //     ['3', 68, 45],
    //     ['4', 65, 40],
    //     ['5', 56, 35],
    //     ['6', 45, 32],
    //     ['7', 40, 28],
    //     ['8', 34, 22],
    //     ['9', 30, 20],
    //     ['10', 14, 18]
    //]);

    var options = {
        title: 'Wartośći funkcji wzlędem iteracji',
        legend: { position: 'right' },
        hAxis: { title: 'Iteracje' },
        vAxis: { title: 'Wartości funkcji' },
        height: 400
    };

    var chart = new google.visualization.LineChart(document.getElementById('chart_div_line'));
    chart.draw(data, options);
};

function drawScatterChart(iterations) {
    var data = google.visualization.arrayToDataTable([
              ['X', 'Y'],
              [1, 12],
              [2, 5.5],
              [3, 14],
              [4, 5],
              [5, 3.5],
              [6, 7],
              [7, 8],
              [8, 5.6],
              [9, 13.5],
              [10, 17]
    ]);

    var options = {
        title: 'Przykładowy diagram',
        hAxis: { title: 'X', minValue: 0 },
        vAxis: { title: 'Y', minValue: 0 },
        legend: 'none',
        height: 400
    };

    var chart = new google.visualization.ScatterChart(document.getElementById('chart_div_scatter'));
    chart.draw(data, options);
};

function computeDistanceBetweenNodes(x1, y1, x2, y2) {
    return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
};

function prepareMatrixOfEdges() {
    var xNodes = baseGraph.graph.nodes();
    var xEdges = baseGraph.graph.edges();

    var matrixOfEdges = [];

    for (var i = 0; i < xNodes.length; i++) {
        var tempArray = [];
        for (var j = 0; j < xNodes.length; j++) {
            if (xNodes[i].id == xNodes[j].id) {
                tempArray[j] = 0;
            }
            else {
                for (var k = 0; k < xEdges.length; k++) {
                    if (xEdges[k].source == xNodes[i].id && xEdges[k].target == xNodes[j].id
                        || xEdges[k].target == xNodes[i].id && xEdges[k].source == xNodes[j].id) {
                        tempArray[j] = xEdges[k].label;
                    }
                }
            }
        }
        matrixOfEdges.push(tempArray);
    }
    return matrixOfEdges;
};

function prepareMatrixOfNodesIds() {
    var xNodes = baseGraph.graph.nodes();
    var indexList = [];
    for (var i = 0; i < xNodes.length; i++) {
        indexList.push(parseInt(xNodes[i].index));
    }
    return indexList;
};

function showModalWarning(message) {
    $("#warningMessage").text(message);
    $('#warningModal').modal('show');
};<commit_msg>Added sample population chart<commit_after>var baseGraph;
var pathGraphs = [];

var minNumberOfNodes = 1;
var maxNumberOfNodes = 15;
var pathGraphsEnableEdgeHovering = false;
var baseGraphEnableEdgeHovering = true;
var maxIntegerInputLength = 3;
var maxFloatInputLength = 4;

$(document).ready(function () {
    window.blockUIImage = {
        message: $("#loading-gif"),
        baseZ: 2000,
        css: { "background-color": "transparent", "border": "none" },
        overlayCSS: { backgroundColor: 'white', opacity: 0.4 }
    };

    $('.float').keypress(function (event) {
        if (((event.which != 46 || $(this).val().indexOf('.') != -1) && (event.which < 48 || event.which > 57)) || $(this).val().length >= maxFloatInputLength) {
            event.preventDefault();
        }
    });

    $('.integer').keypress(function (event) {
        if ((event.which < 48 || event.which > 57) || $(this).val().length >= maxIntegerInputLength) {
            event.preventDefault();
        }
    });

    $("#computePathBtn").attr("disabled", true);

    $("#drawGraphBtn").click(function () {
        var n = $("#nValue").val();

        if (n >= minNumberOfNodes && n <= maxNumberOfNodes) {
            if (baseGraph) {
                baseGraph.graph.clear();
                baseGraph.refresh();
            }

            baseGraph = new sigma({
                graph: drawBaseGraph(parseInt(n)),
                renderer: {
                    container: document.getElementById('base-graph-container'),
                    type: 'canvas'
                },
                settings: {
                    enableEdgeHovering: baseGraphEnableEdgeHovering,
                    edgeHoverSizeRatio: 3,
                    edgeLabelSize: 'proportional',
                    defaultEdgeLabelSize: 13,
                    defaultEdgeLabelColor: 'green',
                    zoomingRatio: 1.0
                }
            });
            sigma.plugins.dragNodes(baseGraph, baseGraph.renderers[0]);

            $("#computePathBtn").attr("disabled", false);
        }
        else {
            showModalWarning("Należy podać poprawną liczbę wierzchołków grafu z przedziału (" + minNumberOfNodes + ";" + maxNumberOfNodes + ")");
        }
    });

    $("#computePathBtn").click(function () {
        var popSize = $("#pValue").val();
        var mutationRate = $("#mValue").val();
        var crossoverRate = $("#kValue").val();
        var iterationsNumber = $("#iValue").val();

        if (popSize.length > 0 && mutationRate.length > 0 && crossoverRate.length > 0 && iterationsNumber.length > 0) {
           // $.blockUI(window.blockUIImage);

            var model =
                {
                    PopSize: parseInt(popSize),
                    MutationRate: parseFloat(mutationRate),
                    CrossoverRate: parseFloat(crossoverRate),
                    IterationsNumber: parseInt(iterationsNumber),
                    IdList: prepareMatrixOfNodesIds(),
                    WeightMatrix: prepareMatrixOfEdges()
                };

            $.ajax({
                type: "POST",
                contentType: "application/json",
                dataType: 'json',
                url: '/api/tspresolve',
                data: JSON.stringify(model),
                success: function (result) {

                    google.charts.load('current', { 'packages': ['corechart'] });
                    google.charts.setOnLoadCallback(drawChart);

                    function drawChart() {
                        drawLineChart(result.Iterations);
                        drawScatterChart(result.Iterations);
                    };

                    for (var i = 0; i < pathGraphs.length; i++) {
                        pathGraphs[i].graph.clear();
                        pathGraphs[i].refresh();
                    }
                    pathGraphs = [];

                    drawPathGraph("first-path-graph-container", result.Iterations[Math.floor(Math.random() * result.Iterations.length)].Resolves[0].OrderedIdList);
                    drawPathGraph("second-path-graph-container", result.Iterations[Math.floor(Math.random() * result.Iterations.length)].Resolves[0].OrderedIdList);
                    drawPathGraph("third-path-graph-container", result.Iterations[Math.floor(Math.random() * result.Iterations.length)].Resolves[0].OrderedIdList);
                    drawPathGraph("fourth-path-graph-container", result.Iterations[Math.floor(Math.random() * result.Iterations.length)].Resolves[0].OrderedIdList);

                  //  $.unblockUI();
                },
                error: function () {
                  // $.unblockUI();
                }
            });
        }
        else {
            showModalWarning("Należy podać wszystkie wymagane parametry");
        }
    });

    $('#clearBtn').click(function () {
        location.reload(true);
    });
});

function drawPathGraph(container, nodes) {
    var g = {
        nodes: [],
        edges: []
    };

    var baseNodes = baseGraph.graph.nodes();

    for (var i = 0; i < baseNodes.length; i++) {
        g.nodes.push({
            id: container + "_" + i,
            x: baseNodes[i].x,
            y: baseNodes[i].y,
            label: baseNodes[i].label,
            size: 1,
            color: '#617db4'
        });
    }

    for (var i = 0; i < nodes.length - 1; i++) {
        g.edges.push({
            id: container + "_" + "e" + "_" + i,
            source: container + "_" + nodes[i],
            target: container + "_" + nodes[i + 1],
            size: 1,
            color: '#ccc',
            type: ['line']
        });
    }

    pathGraphs.push(new sigma({
        graph: g,
        renderer: {
            container: document.getElementById(container),
            type: 'canvas'
        },
        settings: {
            enableEdgeHovering: pathGraphsEnableEdgeHovering,
            edgeHoverSizeRatio: 3,
            edgeLabelSize: 'proportional',
            defaultEdgeLabelSize: 13,
            defaultEdgeLabelColor: 'green',
            zoomingRatio: 1.0,
            minNodeSize: 1,
            maxNodeSize: 6
        }
    }));
};

function drawBaseGraph(n) {
    var g = {
        nodes: [],
        edges: []
    };

    for (var i = 0; i < n; i++) {
        g.nodes.push({
            id: 'n' + i,
            index: i,
            label: 'N' + (i + 1),
            x: Math.random(),
            y: Math.random(),
            size: 1,
            color: '#617db4'
        });
    }

    for (i = n - 1; i >= 0; i--) {
        for (var j = 0; j < i; j++) {
            if (i != j) {
                var distance = Math.ceil(computeDistanceBetweenNodes(g.nodes[i].x, g.nodes[i].y, g.nodes[j].x, g.nodes[j].y) * 5);
                g.edges.push({
                    id: 'e' + "_" + i + "_" + j,
                    source: 'n' + i,
                    target: 'n' + j,
                    size: 1,
                    color: '#ccc',
                    label: "" + distance,
                    type: ['line']
                });
            }
        }
    }
    return g;
};

function drawLineChart(iterations) {
    var rawData = [];
    rawData.push(['X', 'F1', 'F2']);
    for (var i = 0; i < iterations.length; i++) {
        var item = [iterations[i].Id, iterations[i].Resolves[iterations[i].BestFirstFuncResolveIndex].FirstCost, iterations[i].Resolves[iterations[i].BestSecondFuncResolveIndex].SecondCost];
        rawData.push(item);
    }

    var data = google.visualization.arrayToDataTable(rawData);

    //var data = google.visualization.arrayToDataTable([
    //     ['X', 'F1', 'F2'],
    //     ['1', 87, 51],
    //     ['2', 75, 50],
    //     ['3', 68, 45],
    //     ['4', 65, 40],
    //     ['5', 56, 35],
    //     ['6', 45, 32],
    //     ['7', 40, 28],
    //     ['8', 34, 22],
    //     ['9', 30, 20],
    //     ['10', 14, 18]
    //]);

    var options = {
        title: 'Wartośći funkcji wzlędem iteracji',
        legend: { position: 'right' },
        hAxis: { title: 'Iteracje' },
        vAxis: { title: 'Wartości funkcji' },
        height: 400
    };

    var chart = new google.visualization.LineChart(document.getElementById('chart_div_line'));
    chart.draw(data, options);
};

function drawScatterChart(iterations) {
    //var data = google.visualization.arrayToDataTable([
    //          ['X', 'Y'],
    //          [1, 12],
    //          [2, 5.5],
    //          [3, 14],
    //          [4, 5],
    //          [5, 3.5],
    //          [6, 7],
    //          [7, 8],
    //          [8, 5.6],
    //          [9, 13.5],
    //          [10, 17]
    //]);

    var rawData = [];
    rawData.push(['X', 'Y']);

    var items = iterations[iterations.length - 1].Resolves;
    for (var i = 0; i < items.length; i++) {
        var item = [i + 1, items[i].FirstCost];
        rawData.push(item);
    }

    var data = google.visualization.arrayToDataTable(rawData);

    var options = {
        title: 'Wartośći osobników populacji',
        hAxis: { title: 'Osobniki', minValue: 0 },
        vAxis: { title: 'Wartości', minValue: 0 },
        legend: 'none',
        height: 400
    };

    var chart = new google.visualization.ScatterChart(document.getElementById('chart_div_scatter'));
    chart.draw(data, options);
};

function computeDistanceBetweenNodes(x1, y1, x2, y2) {
    return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
};

function prepareMatrixOfEdges() {
    var xNodes = baseGraph.graph.nodes();
    var xEdges = baseGraph.graph.edges();

    var matrixOfEdges = [];

    for (var i = 0; i < xNodes.length; i++) {
        var tempArray = [];
        for (var j = 0; j < xNodes.length; j++) {
            if (xNodes[i].id == xNodes[j].id) {
                tempArray[j] = 0;
            }
            else {
                for (var k = 0; k < xEdges.length; k++) {
                    if (xEdges[k].source == xNodes[i].id && xEdges[k].target == xNodes[j].id
                        || xEdges[k].target == xNodes[i].id && xEdges[k].source == xNodes[j].id) {
                        tempArray[j] = xEdges[k].label;
                    }
                }
            }
        }
        matrixOfEdges.push(tempArray);
    }
    return matrixOfEdges;
};

function prepareMatrixOfNodesIds() {
    var xNodes = baseGraph.graph.nodes();
    var indexList = [];
    for (var i = 0; i < xNodes.length; i++) {
        indexList.push(parseInt(xNodes[i].index));
    }
    return indexList;
};

function showModalWarning(message) {
    $("#warningMessage").text(message);
    $('#warningModal').modal('show');
};<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// 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.
var cacheName = 'rotavo-v20190718a';

// Cache a very basic selection of resources
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(cacheName).then((cache) => {
      return cache.addAll([
        '/',
        '/css/main.css',
        '/favicon.ico',
        '/img/icons/android-chrome-192x192.png',
        '/img/icons/android-chrome-512x512.png',
        '/img/icons/favicon-16x16.png',
        '/img/icons/favicon-32x32.png',
        '/index.html',
        '/js/main.js',
      ]);
    }).then(() => {
      return self.skipWaiting();
    })
  );
});

// Clean out old caches
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((existingCacheNames) => {
      return Promise.all(
        existingCacheNames.map((existingCacheName) => {
          if (existingCacheName !== cacheName) {
            return caches.delete(existingCacheName);
          }
        })
      );
    }).then(() => {
      return self.clients.claim();
    })
  );
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request);
    })
  );
});
<commit_msg>Cache version bump<commit_after>// Copyright 2018 Google LLC
//
// 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.
var cacheName = 'rotavo-v20190828a';

// Cache a very basic selection of resources
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(cacheName).then((cache) => {
      return cache.addAll([
        '/',
        '/css/main.css',
        '/favicon.ico',
        '/img/icons/android-chrome-192x192.png',
        '/img/icons/android-chrome-512x512.png',
        '/img/icons/favicon-16x16.png',
        '/img/icons/favicon-32x32.png',
        '/index.html',
        '/js/main.js',
      ]);
    }).then(() => {
      return self.skipWaiting();
    })
  );
});

// Clean out old caches
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((existingCacheNames) => {
      return Promise.all(
        existingCacheNames.map((existingCacheName) => {
          if (existingCacheName !== cacheName) {
            return caches.delete(existingCacheName);
          }
        })
      );
    }).then(() => {
      return self.clients.claim();
    })
  );
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request);
    })
  );
});
<|endoftext|>


<commit_before>importScripts('js/util.js')

/*
 Copyright 2016 Google Inc. All Rights Reserved.
 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.
*/

// Names of the two caches used in this version of the service worker. Change to
// v2, etc. when you update any of the local resources, which will in turn
// trigger the install event again.

const DB_NAME = 'cykb';
const DB_VER = 1;
const NAME_LIST_ALL = 'lists';
const NAME_CAT = 'SchrodingerCat';

const default_config = {
  show: false,
  openFirst: false,
  first: 20,
  openSecond: false,
  second: 5,
  openThird: false,
  third: 0
}
/**
 *
 */
const getDB = function () {
  return new Promise((resolve, reject) => {

    let openRequest = indexedDB.open(DB_NAME, DB_VER)
    openRequest.onupgradeneeded = onupgradeneeded;
    openRequest.onerror = (e) => {
      reject(e)
    }
    openRequest.onsuccess = (e) => {
      resolve(openRequest.result);
    }
  })

}

function onupgradeneeded(e) {
  let oldVer = e.oldVersion;
  let newVer = e.newVersion;
  let db = e.target.result;

  if (!db.objectStoreNames.contains(NAME_CAT)) {
    let cat = db.createObjectStore(NAME_CAT);
    cat.put(default_config, 'config');
  }
  if (!db.objectStoreNames.contains(NAME_LIST_ALL)) {
    db.createObjectStore(NAME_LIST_ALL, {keyPath: "id"});
  }

}

// Following config is cache urls

const PRECACHE = 'precache-201703311411';
const RUNTIME = 'runtime';

// A list of local resources we always want to be cached.
const PRECACHE_URLS = ['./', './css/index.css', './js/util.js', './js/index.min.js'];
const RESOURCES_URLS = [
  './lib/vue/vue.min.js',
  './icon/list.svg',
  './icon/list-active.svg',
  './icon/day.svg',
  './icon/day-active.svg',
  './icon/setting.svg',
  './icon/setting-active.svg',
  './cykb192.png'
];

const ICON_PATH = './cykb192.png';

// The install handler takes care of precaching the resources we always need.
self.addEventListener('install', function (event) {
  event.waitUntil(caches.open(PRECACHE).then(cache => cache.addAll(PRECACHE_URLS)).then(() => caches.open(RUNTIME)).then(cache => cache.addAll(RESOURCES_URLS)).then(self.skipWaiting()));
});

registration.addEventListener('updatefound', e => {
  showNotification('已更新到1.0.2! ', {body: '* 解决"获得今日课程"事务出错 而导致不能继续的问题\n* 修改样式，突出课表顺序\n\n有任何建议和意见可以发送邮件给我。'})
  // console.log('updatefound', e); console.log('a:', registration.active);
  // console.log('w:', registration.wating); console.log('i:',
  // registration.installing);
})
// The activate handler takes care of cleaning up old caches.
self.addEventListener('activate', event => {

  const currentCaches = [PRECACHE, RUNTIME];
  event.waitUntil(caches.keys().then(cacheNames => {
    return cacheNames.filter(cacheName => !currentCaches.includes(cacheName));
  }).then(cachesToDelete => {
    return Promise.all(cachesToDelete.map(cacheToDelete => {
      return caches.delete(cacheToDelete);
    }));
  }).then(() => self.clients.claim()));
});

// The fetch handler serves responses for same-origin resources from a cache. If
// no response is found, it populates the runtime cache with the response from
// the network before returning it to the page.
self.addEventListener('fetch', event => {

  // Skip cross-origin requests, like those for Google Analytics.
  if (event.request.url.startsWith(self.location.origin)) {
    if (navigator.onLine) {
      if (event.request.headers.get('X-SW-tag') == "get-list") {
        event.respondWith(getListFromServer(event.request))
      } else {

        //Online strategy
        event.respondWith(caches.open(PRECACHE).then(cache => cache.match(event.request.clone())).then(cachedResponse => {
          if (cachedResponse) {
            //Request precached resources, return the response directly.

            return cachedResponse
          } else {
            //onLine resources first , always get a new one.

            return caches
              .open(RUNTIME)
              .then(cache => {
                return fetch(event.request).then(response => {
                  // Put a copy of the response in the runtime cache.
                  return cache
                    .put(event.request, response.clone())
                    .then(() => {
                      return response
                    })
                })
              })
          }
        }))
      }
    } else {
      //Off-line , Cache first, error second
      event.respondWith(caches.match(event.request).then(cachedResponse => {
        if (cachedResponse) {
          return cachedResponse
        } else {
          return Response.error()
        }
      }))
    }
  }
});

self.addEventListener('notificationclick', function (event) {

  switch (event.action) {
    case "dismiss":
      {}
    default:
      {
        event
          .notification
          .close()
        break;
      }
  }

  // This looks to see if the current is already open and focuses if it is
  // event.waitUntil(clients.matchAll({   type: "window"
  // }).then(function(clientList) {   for (var i = 0; i < clientList.length; i++)
  // {     var client = clientList[i];     if (client.url == '/' && 'focus' in
  // client)       return client.focus();   }   if (clients.openWindow)     return
  // clients.openWindow('/'); }));
});

self.addEventListener('message', function (e) {
  switch (e.data.action) {
    case 'activate-publicChanel':
      {
        // self.publicChanel = e.ports[0];
        break;
      }
    case "getconfig":
      {
        getDB().then(db => {
          db.transaction([NAME_CAT], 'readonly')
            .objectStore(NAME_CAT)
            .get('config')
            .onsuccess = function (event) {
            let result = event.target.result;
            if (result) {
              e
                .ports[0]
                .postMessage(event.target.result)
            } else {
              showNotification('没有配置文件~将初始化配置!');
              db.transaction([NAME_CAT], 'readwrite')
                .objectStore(NAME_CAT)
                .add(default_config, 'config')
                .onsuccess = success_e => {
                e
                  .ports[0]
                  .postMessage(default_config)
              };
            }
          }
        });

        break;
      }
    case "test":
      {
        showNotification("推送测试!")
        break;
      }
    case "saveconfig":
      {
        getDB().then(db => {
          db.transaction([NAME_CAT], 'readwrite')
            .objectStore(NAME_CAT)
            .put(e.data.data, 'config')
            .onsuccess = function (event) {
            e
              .ports[0]
              .postMessage({code: 0})

          }
        });

        break;
      }
  }
})

/**
 *   ShowNotifications；
 *   current service worker can not response at a specific time ,only to set a timer;
 */
setInterval(function () {
  getDB().then(db => {
    let theCat = db.transaction([NAME_CAT], 'readwrite').objectStore(NAME_CAT);

    let currentTime = new Date();
    let currentY = currentTime.getFullYear()
    let currentMo = currentTime.getMonth();
    let currentD = currentTime.getDate();
    let currentH = currentTime.getHours();
    let currentMin = currentTime.getMinutes();

    let user = {};
    let config = {};
    let today_list = [];
    let timeOption = doTimeCount(currentTime);
    let timeOptionID = timeOption.id;

    theCat
      .get(NAME_CAT)
      .onsuccess = (e) => {
      user = e.target.result;

      if (!user) {
        showNotification("欧漏！后台用户列表丢失~要注销重新登录！>_<")
        return false;
      }
      let today_transition = theCat.get('today');

      today_transition.onerror = (e) => {

        today_list = user
          .list
          .filter(function (el) {
            if (el.weekend.indexOf(timeOption.weekendPast) != -1 && el.day == timeOption.todayDay) {
              return true;
            } else {
              return false;
            }
          });
        if (today_list.length == 0)
          return;
        today_list.sort(sortClassList);
        theCat.put({
          id: timeOptionID,
          list: today_list
        }, 'today').onsuccess = (e) => {
          afterGetToday(today_list);
        };
      }
      today_transition.onsuccess = (e) => {
        let today = e.target.result;

        if (today && today.id == timeOptionID) {
          today_list = today.list;
        } else {
          today_list = user
            .list
            .filter(function (el) {
              if (el.weekend.indexOf(timeOption.weekendPast) != -1 && el.day == timeOption.todayDay) {
                return true;
              } else {
                return false;
              }
            });
          if (today_list.length == 0)
            return;
          today_list.sort(sortClassList);
          theCat.put({
            id: timeOptionID,
            list: today_list
          }, 'today');
        }
        afterGetToday(today_list);
      };

      function afterGetToday(today_list) {

        //保证today_list
        if (today_list.length == 0)
          return;

        //今天没有课，就什么也不做；如果有课，继续执行
        theCat
          .get('config')
          .onsuccess = (e) => {
          config = e.target.result;
          //showNotification()
          if (config.show == false) {
            return;
          }

          // if closed ,return
          if (!(config.openFirst || config.openSecond || config.openThird)) {
            return;
          }

          //预先准备索引，用于判断将要提醒的课程是否真正上课
          let indexList = [];
          today_list.forEach(function (el) {
            indexList.push(getRowIndex(el))
          });
          indexList.sort();
          let isHavingLastClass = function (currentIndex, hour, minute) {
            var i = indexList.indexOf(currentIndex);
            var lastIndex = indexList[i - 1];

            if (lastIndex) {
              return isHavingClass(lastIndex, hour, minute, false);
            } else {
              return false;
            }
          };

          //循环遍历每一节课
          today_list.forEach(function (el) {
            var index = getRowIndex(el);
            var classtime = TIME_GAP[index];
            var firstTime,
              secondTime,
              thirdTime;

            //显示第一次提醒
            if (config.openFirst && Number(config.first) > 0) {
              if (!el.firstN) {
                firstTime = new Date(currentY, currentMo, currentD, currentH, (currentMin + Number(config.first)))
                let h = firstTime.getHours();
                let m = firstTime.getMinutes();
                if (classtime.fromH == h && classtime.fromM == m && !isHavingLastClass(index, h, m)) {

                  showNotification("【" + el.where + "】将在" + config.first + "分钟后上课！", {
                    body: el.className + '\n' + el.who,
                    tag: "first"
                  });
                  el.firstN = true;
                }
              }
            }

            //显示第二次提醒
            if (config.openSecond && Number(config.second) > 0) {
              if (!el.secondN) {
                secondTime = new Date(currentY, currentMo, currentD, currentH, (currentMin + Number(config.second)))
                let h = secondTime.getHours();
                let m = secondTime.getMinutes();
                if (classtime.fromH == h && classtime.fromM == m && !isHavingLastClass(index, h, m)) {
                  showNotification("【" + el.where + "】将在" + config.second + "分钟后上课！", {
                    body: el.className + '\n' + el.who,
                    tag: "second"
                  });
                  el.secondN = true;
                }
              }
            }

            //显示第三次提醒
            if (config.openThird && Number(config.third) > 0) {
              if (!el.thirdN) {
                thirdTime = new Date(currentY, currentMo, currentD, currentH, (currentMin + Number(config.third)))
                let h = thirdTime.getHours();
                let m = thirdTime.getMinutes();
                if (classtime.fromH == h && classtime.fromM == m && !isHavingLastClass(index, h, m)) {
                  showNotification("【" + el.where + "】将在" + config.third + "分钟后上课！", {
                    body: el.className + '\n' + el.who,
                    tag: "third"
                  });
                  el.thirdN = true;
                }
              }
            }

            if (!el.rightNow) {
              if (classtime.fromH == currentH && classtime.fromM == currentMin) {
                showNotification('【' + el.where + '】正在上课！', {
                  body: el.className + '\n' + el.who,
                  tag: "rightNow"
                });
                el.rightNow = true;
              }
            }

          });

          theCat.put({
            id: timeOptionID,
            list: today_list
          }, 'today').onsuccess = e => {};

        }
      }

    }
  })

}, 58000)

function getListFromServer(request) {
  return fetch(request.url).then(resp => {
    return new Promise((resolve, reject) => {
      resp
        .clone()
        .json()
        .then(json => {

          if (json.code != 0) {
            showNotification('更新课表失败!')
            resolve(resp);
          } else {

            getDB().then(db => {
              let transaction = db.transaction([
                NAME_CAT, NAME_LIST_ALL
              ], "readwrite");
              let cat = transaction.objectStore(NAME_CAT);
              let listStore = transaction.objectStore(NAME_LIST_ALL);
              let afterStore = () => {
                if (request.headers.get('X-ROOTUSER') == "ROOT") {
                  cat
                    .put(json, NAME_CAT)
                    .onsuccess = () => {
                    showNotification('更新课表成功!', {
                      body: "学号为" + json.id
                    })
                    resolve(resp);
                  }
                } else {
                  resolve(resp)
                }
              };

              listStore
                .put(json)
                .onsuccess = afterStore;

            })
          }
        });
    })

  })
}

function showNotification(title, option) {
  let default_option = Object.assign({
    tag: "public",
    icon: "./cykb192.png",
    vibrate: [
      500,
      700,
      700,
      700,
      1000,
      700,
      1000
    ],
    actions: [
      {
        action: "dismiss",
        title: "我我我知道了(●￣(ｴ)￣●)"
      }
    ]
  }, option);

  if (Notification.permission == 'granted') {
    return registration.active && registration.showNotification(title, default_option)
  } else {
    console.warn("No permission")
    return Promise.reject("No permission")
  }

}

function uninstall() {
  indexedDB.deleteDatabase(DB_NAME);
  caches
    .keys()
    .then(cacheNames => {
      return Promise.all(cachesToDelete.map(cacheToDelete => {
        return caches.delete(cacheToDelete);
      }));
    })
    .then(() => {
      registration
        .unregister()
        .then(e => {
          postMessage(JSON.stringify({tag: 'alert', msg: '已卸载该应用'}))
        })
    });
}
<commit_msg>every 20 second execute the function<commit_after>importScripts('js/util.js')

/*
 Copyright 2016 Google Inc. All Rights Reserved.
 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.
*/

// Names of the two caches used in this version of the service worker. Change to
// v2, etc. when you update any of the local resources, which will in turn
// trigger the install event again.

const DB_NAME = 'cykb';
const DB_VER = 1;
const NAME_LIST_ALL = 'lists';
const NAME_CAT = 'SchrodingerCat';

const default_config = {
  show: false,
  openFirst: false,
  first: 20,
  openSecond: false,
  second: 5,
  openThird: false,
  third: 0
}
/**
 *
 */
const getDB = function () {
  return new Promise((resolve, reject) => {

    let openRequest = indexedDB.open(DB_NAME, DB_VER)
    openRequest.onupgradeneeded = onupgradeneeded;
    openRequest.onerror = (e) => {
      reject(e)
    }
    openRequest.onsuccess = (e) => {
      resolve(openRequest.result);
    }
  })

}

function onupgradeneeded(e) {
  let oldVer = e.oldVersion;
  let newVer = e.newVersion;
  let db = e.target.result;

  if (!db.objectStoreNames.contains(NAME_CAT)) {
    let cat = db.createObjectStore(NAME_CAT);
    cat.put(default_config, 'config');
  }
  if (!db.objectStoreNames.contains(NAME_LIST_ALL)) {
    db.createObjectStore(NAME_LIST_ALL, {keyPath: "id"});
  }

}

// Following config is cache urls

const PRECACHE = 'precache-201703311411';
const RUNTIME = 'runtime';

// A list of local resources we always want to be cached.
const PRECACHE_URLS = ['./', './css/index.css', './js/util.js', './js/index.min.js'];
const RESOURCES_URLS = [
  './lib/vue/vue.min.js',
  './icon/list.svg',
  './icon/list-active.svg',
  './icon/day.svg',
  './icon/day-active.svg',
  './icon/setting.svg',
  './icon/setting-active.svg',
  './cykb192.png'
];

const ICON_PATH = './cykb192.png';

// The install handler takes care of precaching the resources we always need.
self.addEventListener('install', function (event) {
  event.waitUntil(caches.open(PRECACHE).then(cache => cache.addAll(PRECACHE_URLS)).then(() => caches.open(RUNTIME)).then(cache => cache.addAll(RESOURCES_URLS)).then(self.skipWaiting()));
});

registration.addEventListener('updatefound', e => {
  showNotification('已更新到1.0.2! ', {body: '* 解决"获得今日课程"事务出错 而导致不能继续的问题\n* 修改样式，突出课表顺序\n\n有任何建议和意见可以发送邮件给我。'})
  // console.log('updatefound', e); console.log('a:', registration.active);
  // console.log('w:', registration.wating); console.log('i:',
  // registration.installing);
})
// The activate handler takes care of cleaning up old caches.
self.addEventListener('activate', event => {

  const currentCaches = [PRECACHE, RUNTIME];
  event.waitUntil(caches.keys().then(cacheNames => {
    return cacheNames.filter(cacheName => !currentCaches.includes(cacheName));
  }).then(cachesToDelete => {
    return Promise.all(cachesToDelete.map(cacheToDelete => {
      return caches.delete(cacheToDelete);
    }));
  }).then(() => self.clients.claim()));
});

// The fetch handler serves responses for same-origin resources from a cache. If
// no response is found, it populates the runtime cache with the response from
// the network before returning it to the page.
self.addEventListener('fetch', event => {

  // Skip cross-origin requests, like those for Google Analytics.
  if (event.request.url.startsWith(self.location.origin)) {
    if (navigator.onLine) {
      if (event.request.headers.get('X-SW-tag') == "get-list") {
        event.respondWith(getListFromServer(event.request))
      } else {

        //Online strategy
        event.respondWith(caches.open(PRECACHE).then(cache => cache.match(event.request.clone())).then(cachedResponse => {
          if (cachedResponse) {
            //Request precached resources, return the response directly.

            return cachedResponse
          } else {
            //onLine resources first , always get a new one.

            return caches
              .open(RUNTIME)
              .then(cache => {
                return fetch(event.request).then(response => {
                  // Put a copy of the response in the runtime cache.
                  return cache
                    .put(event.request, response.clone())
                    .then(() => {
                      return response
                    })
                })
              })
          }
        }))
      }
    } else {
      //Off-line , Cache first, error second
      event.respondWith(caches.match(event.request).then(cachedResponse => {
        if (cachedResponse) {
          return cachedResponse
        } else {
          return Response.error()
        }
      }))
    }
  }
});

self.addEventListener('notificationclick', function (event) {

  switch (event.action) {
    case "dismiss":
      {}
    default:
      {
        event
          .notification
          .close()
        break;
      }
  }

  // This looks to see if the current is already open and focuses if it is
  // event.waitUntil(clients.matchAll({   type: "window"
  // }).then(function(clientList) {   for (var i = 0; i < clientList.length; i++)
  // {     var client = clientList[i];     if (client.url == '/' && 'focus' in
  // client)       return client.focus();   }   if (clients.openWindow)     return
  // clients.openWindow('/'); }));
});

self.addEventListener('message', function (e) {
  switch (e.data.action) {
    case 'activate-publicChanel':
      {
        // self.publicChanel = e.ports[0];
        break;
      }
    case "getconfig":
      {
        getDB().then(db => {
          db.transaction([NAME_CAT], 'readonly')
            .objectStore(NAME_CAT)
            .get('config')
            .onsuccess = function (event) {
            let result = event.target.result;
            if (result) {
              e
                .ports[0]
                .postMessage(event.target.result)
            } else {
              showNotification('没有配置文件~将初始化配置!');
              db.transaction([NAME_CAT], 'readwrite')
                .objectStore(NAME_CAT)
                .add(default_config, 'config')
                .onsuccess = success_e => {
                e
                  .ports[0]
                  .postMessage(default_config)
              };
            }
          }
        });

        break;
      }
    case "test":
      {
        showNotification("推送测试!")
        break;
      }
    case "saveconfig":
      {
        getDB().then(db => {
          db.transaction([NAME_CAT], 'readwrite')
            .objectStore(NAME_CAT)
            .put(e.data.data, 'config')
            .onsuccess = function (event) {
            e
              .ports[0]
              .postMessage({code: 0})

          }
        });

        break;
      }
  }
})

/**
 *   ShowNotifications；
 *   current service worker can not response at a specific time ,only to set a timer;
 */
setInterval(function () {
  getDB().then(db => {
    let theCat = db.transaction([NAME_CAT], 'readwrite').objectStore(NAME_CAT);

    let currentTime = new Date();
    let currentY = currentTime.getFullYear()
    let currentMo = currentTime.getMonth();
    let currentD = currentTime.getDate();
    let currentH = currentTime.getHours();
    let currentMin = currentTime.getMinutes();

    let user = {};
    let config = {};
    let today_list = [];
    let timeOption = doTimeCount(currentTime);
    let timeOptionID = timeOption.id;

    theCat
      .get(NAME_CAT)
      .onsuccess = (e) => {
      user = e.target.result;

      if (!user) {
        showNotification("欧漏！后台用户列表丢失~要注销重新登录！>_<")
        return false;
      }
      let today_transition = theCat.get('today');

      today_transition.onerror = (e) => {

        today_list = user
          .list
          .filter(function (el) {
            if (el.weekend.indexOf(timeOption.weekendPast) != -1 && el.day == timeOption.todayDay) {
              return true;
            } else {
              return false;
            }
          });
        if (today_list.length == 0)
          return;
        today_list.sort(sortClassList);
        theCat.put({
          id: timeOptionID,
          list: today_list
        }, 'today').onsuccess = (e) => {
          afterGetToday(today_list);
        };
      }
      today_transition.onsuccess = (e) => {
        let today = e.target.result;

        if (today && today.id == timeOptionID) {
          today_list = today.list;
        } else {
          today_list = user
            .list
            .filter(function (el) {
              if (el.weekend.indexOf(timeOption.weekendPast) != -1 && el.day == timeOption.todayDay) {
                return true;
              } else {
                return false;
              }
            });
          if (today_list.length == 0)
            return;
          today_list.sort(sortClassList);
          theCat.put({
            id: timeOptionID,
            list: today_list
          }, 'today');
        }
        afterGetToday(today_list);
      };

      function afterGetToday(today_list) {

        //保证today_list
        if (today_list.length == 0)
          return;

        //今天没有课，就什么也不做；如果有课，继续执行
        theCat
          .get('config')
          .onsuccess = (e) => {
          config = e.target.result;
          //showNotification()
          if (config.show == false) {
            return;
          }

          // if closed ,return
          if (!(config.openFirst || config.openSecond || config.openThird)) {
            return;
          }

          //预先准备索引，用于判断将要提醒的课程是否真正上课
          let indexList = [];
          today_list.forEach(function (el) {
            indexList.push(getRowIndex(el))
          });
          indexList.sort();
          let isHavingLastClass = function (currentIndex, hour, minute) {
            var i = indexList.indexOf(currentIndex);
            var lastIndex = indexList[i - 1];

            if (lastIndex) {
              return isHavingClass(lastIndex, hour, minute, false);
            } else {
              return false;
            }
          };

          //循环遍历每一节课
          today_list.forEach(function (el) {
            var index = getRowIndex(el);
            var classtime = TIME_GAP[index];
            var firstTime,
              secondTime,
              thirdTime;

            //显示第一次提醒
            if (config.openFirst && Number(config.first) > 0) {
              if (!el.firstN) {
                firstTime = new Date(currentY, currentMo, currentD, currentH, (currentMin + Number(config.first)))
                let h = firstTime.getHours();
                let m = firstTime.getMinutes();
                if (classtime.fromH == h && classtime.fromM == m && !isHavingLastClass(index, h, m)) {

                  showNotification("【" + el.where + "】将在" + config.first + "分钟后上课！", {
                    body: el.className + '\n' + el.who,
                    tag: "first"
                  });
                  el.firstN = true;
                }
              }
            }

            //显示第二次提醒
            if (config.openSecond && Number(config.second) > 0) {
              if (!el.secondN) {
                secondTime = new Date(currentY, currentMo, currentD, currentH, (currentMin + Number(config.second)))
                let h = secondTime.getHours();
                let m = secondTime.getMinutes();
                if (classtime.fromH == h && classtime.fromM == m && !isHavingLastClass(index, h, m)) {
                  showNotification("【" + el.where + "】将在" + config.second + "分钟后上课！", {
                    body: el.className + '\n' + el.who,
                    tag: "second"
                  });
                  el.secondN = true;
                }
              }
            }

            //显示第三次提醒
            if (config.openThird && Number(config.third) > 0) {
              if (!el.thirdN) {
                thirdTime = new Date(currentY, currentMo, currentD, currentH, (currentMin + Number(config.third)))
                let h = thirdTime.getHours();
                let m = thirdTime.getMinutes();
                if (classtime.fromH == h && classtime.fromM == m && !isHavingLastClass(index, h, m)) {
                  showNotification("【" + el.where + "】将在" + config.third + "分钟后上课！", {
                    body: el.className + '\n' + el.who,
                    tag: "third"
                  });
                  el.thirdN = true;
                }
              }
            }

            if (!el.rightNow) {
              if (classtime.fromH == currentH && classtime.fromM == currentMin) {
                showNotification('【' + el.where + '】正在上课！', {
                  body: el.className + '\n' + el.who,
                  tag: "rightNow"
                });
                el.rightNow = true;
              }
            }

          });

          theCat.put({
            id: timeOptionID,
            list: today_list
          }, 'today').onsuccess = e => {};

        }
      }

    }
  })

}, 20000)

function getListFromServer(request) {
  return fetch(request.url).then(resp => {
    return new Promise((resolve, reject) => {
      resp
        .clone()
        .json()
        .then(json => {

          if (json.code != 0) {
            showNotification('更新课表失败!')
            resolve(resp);
          } else {

            getDB().then(db => {
              let transaction = db.transaction([
                NAME_CAT, NAME_LIST_ALL
              ], "readwrite");
              let cat = transaction.objectStore(NAME_CAT);
              let listStore = transaction.objectStore(NAME_LIST_ALL);
              let afterStore = () => {
                if (request.headers.get('X-ROOTUSER') == "ROOT") {
                  cat
                    .put(json, NAME_CAT)
                    .onsuccess = () => {
                    showNotification('更新课表成功!', {
                      body: "学号为" + json.id
                    })
                    resolve(resp);
                  }
                } else {
                  resolve(resp)
                }
              };

              listStore
                .put(json)
                .onsuccess = afterStore;

            })
          }
        });
    })

  })
}

function showNotification(title, option) {
  let default_option = Object.assign({
    tag: "public",
    icon: "./cykb192.png",
    vibrate: [
      500,
      700,
      700,
      700,
      1000,
      700,
      1000
    ],
    actions: [
      {
        action: "dismiss",
        title: "我我我知道了(●￣(ｴ)￣●)"
      }
    ]
  }, option);

  if (Notification.permission == 'granted') {
    return registration.active && registration.showNotification(title, default_option)
  } else {
    console.warn("No permission")
    return Promise.reject("No permission")
  }

}

function uninstall() {
  indexedDB.deleteDatabase(DB_NAME);
  caches
    .keys()
    .then(cacheNames => {
      return Promise.all(cachesToDelete.map(cacheToDelete => {
        return caches.delete(cacheToDelete);
      }));
    })
    .then(() => {
      registration
        .unregister()
        .then(e => {
          postMessage(JSON.stringify({tag: 'alert', msg: '已卸载该应用'}))
        })
    });
}
<|endoftext|>


<commit_before>//Copyright 2014, Small Picture, Inc.
	//Last update: 1/26/2014; 11:09:28 AM Eastern.

var myVersion = "0.65";

var s3HostingPath = process.env.fpHostingPath; //where we store all the users' HTML and XML files
var s3defaultType = "text/plain";
var s3defaultAcl = "public-read";

var s3DataPath = process.env.fpDataPath;
var s3NamesPath = s3DataPath + "names";
var s3StatsPath = s3DataPath + "stats";

var myDomain = process.env.fpDomain; //something like smallpict.com
var myPort = process.env.fpServerPort; //what port should the server run on?

var http = require ("http");
var request = require ("request");
var urlpack = require ("url");
var AWS = require ("aws-sdk");
var s3 = new AWS.S3 ();

function httpReadUrl (url, callback) {
	request (url, function (error, response, body) {
		if (!error && response.statusCode == 200) {
			callback (body)
			}
		});
	}
function s3SplitPath (path) { //split path like this: /tmp.scripting.com/testing/one.txt -- into bucketname and path.
	var bucketname = "";
	if (path.length > 0) {
		if (path [0] == "/") { //delete the slash
			path = path.substr (1);
			}
		var ix = path.indexOf ("/");
		bucketname = path.substr (0, ix);
		path = path.substr (ix + 1);
		}
	return ({Bucket: bucketname, Key: path});
	}
function s3NewObject (path, data, type, acl, callback) {
	var splitpath = s3SplitPath (path);
	if (type == undefined) {
		type = s3defaultType;
		}
	if (acl == undefined) {
		acl = s3defaultAcl;
		}
	var params = {
		ACL: acl,
		ContentType: type,
		Body: data,
		Bucket: splitpath.Bucket,
		Key: splitpath.Key
		};
	s3.putObject (params, function (err, data) {
		console.log ("s3NewObject: http://" + splitpath.Bucket + "/" + splitpath.Key);
		if (callback != undefined) {
			callback (err, data);
			}
		});
	}
function s3GetObjectMetadata (path, callback) {
	var params = s3SplitPath (path);
	s3.headObject (params, function (err, data) {
		callback (data);
		});
	}
function s3GetObject (path, callback) {
	var params = s3SplitPath (path);
	s3.getObject (params, function (err, data) {
		callback (data);
		});
	}

function updateNameRecord (name, obj, callback) {
	s3NewObject (s3NamesPath + "/" + name + ".json", JSON.stringify (obj, undefined, 3), "text/plain", "public-read", function (err, data) {
		if (callback != undefined) {
			callback (err, data);
			}
		});
	}
function addNameRecord (name, opmlUrl, callback) {
	var data = {
		"name": name,
		"opmlUrl": opmlUrl,
		"whenCreated": new Date ().toString ()
		};
	updateNameRecord (name, data, callback);
	}
function isNameDefined (name, callback) {
	s3GetObjectMetadata (s3NamesPath + "/" + name + ".json", function (metadata) {
		callback (metadata != null);
		});
	}
function getNameRecord (name, callback) {
	s3GetObject (s3NamesPath + "/" + name + ".json", function (data) {
		if (data == null) {
			callback (null);
			}
		else {
			callback (data.Body);
			}
		});
	}

function padWithZeros (num, ctplaces) {
	var s = num.toString ();
	while (s.length < ctplaces) {
		s = "0" + s;
		}
	return (s);
	}
function isAlpha (ch) {
	return (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')));
	}
function isNumeric (ch) {
	return ((ch >= '0') && (ch <= '9'));
	}
function cleanName (name) {
	var s = "";
	for (var i = 0; i < name.length; i++) {
		var ch = name [i];
		if (isAlpha (ch) || isNumeric (ch)) {
			s += ch;
			}
		}
	return (s.toLowerCase (s));
	}
function scrapeTagValue (sourcestring, tagname) {
	var s = sourcestring; //work with a copy
	var opentag = "<" + tagname + ">", closetag = "</" + tagname + ">";
	var ix = s.indexOf (opentag);
	if (ix >= 0) {
		s = s.substr (ix + opentag.length);
		ix = s.indexOf (closetag);
		if (ix >= 0) {
			s = s.substr (0, ix);
			return (s);
			}
		}
	return ("");
	}
function parsePackages (name, s) { //name is something like "dave"
	var magicpattern = "<[{~#--- ", ix, path, htmltext;
	while (s.length > 0) {
		ix = s.indexOf (magicpattern);
		if (ix < 0) {
			break;
			}
		s = s.substr (ix + magicpattern.length);
		ix = s.indexOf ("\n");
		path = s.substr (0, ix);
		s = s.substr (ix + 1);
		ix = s.indexOf (magicpattern);
		if (ix < 0) {
			htmltext = s;
			}
		else {
			htmltext = s.substr (0, ix);
			s = s.substr (ix);
			}

		if (path.length > 0) {
			if (path [0] == "/") { //delete leading slash, if present
				path = path.substr (1);
				}
			s3NewObject (s3HostingPath + name + "/" + path, htmltext, "text/html");
			}
		}
	}
function handlePackagePing (subdomain) { //something like http://dave.smallpict.com/
	var parsedUrl = urlpack.parse (subdomain, true);
	var sections = parsedUrl.host.split (".");
	var name = sections [0];

	getNameRecord (name, function (jsontext) {
		var obj = JSON.parse (jsontext);
		httpReadUrl (obj.opmlUrl, function (httptext) {
			var urlpackage = scrapeTagValue (httptext, "linkHosting");
			httpReadUrl (urlpackage, function (packagetext) {
				parsePackages (name, packagetext);

				obj.whenLastUpdate = new Date ().toString ();
				obj.urlRedirect = "http:/" + s3HostingPath + name + "/";
				updateNameRecord (name, obj);
				});
			});
		});
	}

console.log ("Fargo Publisher server v" + myVersion);

var server = http.createServer (function (httpRequest, httpResponse) {
	if (httpRequest.method == "HEAD") {
		httpRequest.end (null);
		}
	else {
		var parsedUrl = urlpack.parse (httpRequest.url, true);

		console.log (parsedUrl.hostname + "/" + parsedUrl.pathname);

		switch (parsedUrl.pathname.toLowerCase ()) {
			case "/pingpackage":
				httpResponse.writeHead (200, {"Content-Type": "application/json", "Access-Control-Allow-Origin": "fargo.io"});

				handlePackagePing (parsedUrl.query.link);

				var x = {"url": parsedUrl.query.link};
				var s = "getData (" + JSON.stringify (x) + ")";
				httpResponse.end (s);

				break;
			case "/isnameavailable":
				function sendStringBack (s) {
					var x = {"message": s};
					httpResponse.end ("getData (" + JSON.stringify (x) + ")");
					}
				httpResponse.writeHead (200, {"Content-Type": "application/json", "Access-Control-Allow-Origin": "fargo.io"});

				var name = cleanName (parsedUrl.query.name);

				if (name.length == 0) {
					sendStringBack ("");
					}
				else {
					if (name.length < 4) {
						sendStringBack ("Name must be 4 or more characters.");
						}
					else {
						isNameDefined (name, function (fldefined) {
							var color, answer;
							if (fldefined) {
								color = "red";
								answer = "is not";
								}
							else {
								color = "green";
								answer = "is";
								}
							sendStringBack ("" + name + "." + myDomain + " " + answer + " available.")
							});
						}
					}

				break;
			case "/newoutlinename":
				var recordkey = cleanName (parsedUrl.query.name), url = parsedUrl.query.url;

				console.log ("Create new outline name: " + recordkey + ", url=" + url);

				httpResponse.writeHead (200, {"Content-Type": "application/json", "Access-Control-Allow-Origin": "fargo.io"});

				if (url == undefined) {
					var x = {flError: true, errorString: "Can't assign the name because there is no url parameter provided."};
					httpResponse.end ("getData (" + JSON.stringify (x) + ")");
					}
				else {
					isNameDefined (recordkey, function (fldefined) {
						if (fldefined) {
							var x = {flError: true, errorString: "Can't assign the name '" + recordkey + "' to the outline because there already is an outline with that name."};
							httpResponse.end ("getData (" + JSON.stringify (x) + ")");
							}
						else {
							addNameRecord (recordkey, url, function (err, data) {
								if (err) {
									httpResponse.end ("getData (" + JSON.stringify (err) + ")");
									}
								else {
									var x = {flError: false, name: recordkey + "." + myDomain};
									httpResponse.end ("getData (" + JSON.stringify (x) + ")");
									}
								});
							}
						});
					}
				break;
			case "/geturlfromname":
				var name = cleanName (parsedUrl.query.name);
				httpResponse.writeHead (200, {"Content-Type": "application/json", "Access-Control-Allow-Origin": "fargo.io"});
				getNameRecord (name, function (jsontext) {
					if (jsontext == null) {
						var x = {flError: true, errorString: "Can't open the outline named '" + name + "' because there is no outline with that name."};
						httpResponse.end ("getData (" + JSON.stringify (x) + ")");
						}
					else {
						var obj = JSON.parse (jsontext);
						var x = {flError: false, url: obj.opmlUrl};
						httpResponse.end ("getData (" + JSON.stringify (x) + ")");
						}
					});
				break;
			case "/version":
				httpResponse.writeHead (200, {"Content-Type": "text/plain", "Access-Control-Allow-Origin": "fargo.io"});
				httpResponse.end (myVersion);
				break;
			default:
				httpResponse.writeHead (404, {"Content-Type": "text/plain", "Access-Control-Allow-Origin": "fargo.io"});
				httpResponse.end ("404 Not Found");
				break;
			}
		}
	});

if (myPort == undefined) {
	myPort = 80;
	}
server.listen (myPort);
<commit_msg>Update<commit_after>//Copyright 2014, Small Picture, Inc.
	//Last update: 1/26/2014; 11:13:51 AM Eastern.

var myVersion = "0.65";

var s3HostingPath = process.env.fpHostingPath; //where we store all the users' HTML and XML files
var s3defaultType = "text/plain";
var s3defaultAcl = "public-read";

var s3DataPath = process.env.fpDataPath;
var s3NamesPath = s3DataPath + "names";
var s3StatsPath = s3DataPath + "stats";

var myDomain = process.env.fpDomain; //something like smallpict.com
var myPort = process.env.fpServerPort; //what port should the server run on?

var http = require ("http");
var request = require ("request");
var urlpack = require ("url");
var AWS = require ("aws-sdk");
var s3 = new AWS.S3 ();

function httpReadUrl (url, callback) {
	request (url, function (error, response, body) {
		if (!error && response.statusCode == 200) {
			callback (body)
			}
		});
	}
function s3SplitPath (path) { //split path like this: /tmp.scripting.com/testing/one.txt -- into bucketname and path.
	var bucketname = "";
	if (path.length > 0) {
		if (path [0] == "/") { //delete the slash
			path = path.substr (1);
			}
		var ix = path.indexOf ("/");
		bucketname = path.substr (0, ix);
		path = path.substr (ix + 1);
		}
	return ({Bucket: bucketname, Key: path});
	}
function s3NewObject (path, data, type, acl, callback) {
	var splitpath = s3SplitPath (path);
	if (type == undefined) {
		type = s3defaultType;
		}
	if (acl == undefined) {
		acl = s3defaultAcl;
		}
	var params = {
		ACL: acl,
		ContentType: type,
		Body: data,
		Bucket: splitpath.Bucket,
		Key: splitpath.Key
		};
	s3.putObject (params, function (err, data) {
		console.log ("s3NewObject: http://" + splitpath.Bucket + "/" + splitpath.Key);
		if (callback != undefined) {
			callback (err, data);
			}
		});
	}
function s3GetObjectMetadata (path, callback) {
	var params = s3SplitPath (path);
	s3.headObject (params, function (err, data) {
		callback (data);
		});
	}
function s3GetObject (path, callback) {
	var params = s3SplitPath (path);
	s3.getObject (params, function (err, data) {
		callback (data);
		});
	}

function updateNameRecord (name, obj, callback) {
	s3NewObject (s3NamesPath + "/" + name + ".json", JSON.stringify (obj, undefined, 3), "text/plain", "public-read", function (err, data) {
		if (callback != undefined) {
			callback (err, data);
			}
		});
	}
function addNameRecord (name, opmlUrl, callback) {
	var data = {
		"name": name,
		"opmlUrl": opmlUrl,
		"whenCreated": new Date ().toString ()
		};
	updateNameRecord (name, data, callback);
	}
function isNameDefined (name, callback) {
	s3GetObjectMetadata (s3NamesPath + "/" + name + ".json", function (metadata) {
		callback (metadata != null);
		});
	}
function getNameRecord (name, callback) {
	s3GetObject (s3NamesPath + "/" + name + ".json", function (data) {
		if (data == null) {
			callback (null);
			}
		else {
			callback (data.Body);
			}
		});
	}

function padWithZeros (num, ctplaces) {
	var s = num.toString ();
	while (s.length < ctplaces) {
		s = "0" + s;
		}
	return (s);
	}
function isAlpha (ch) {
	return (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')));
	}
function isNumeric (ch) {
	return ((ch >= '0') && (ch <= '9'));
	}
function cleanName (name) {
	var s = "";
	for (var i = 0; i < name.length; i++) {
		var ch = name [i];
		if (isAlpha (ch) || isNumeric (ch)) {
			s += ch;
			}
		}
	return (s.toLowerCase (s));
	}
function scrapeTagValue (sourcestring, tagname) {
	var s = sourcestring; //work with a copy
	var opentag = "<" + tagname + ">", closetag = "</" + tagname + ">";
	var ix = s.indexOf (opentag);
	if (ix >= 0) {
		s = s.substr (ix + opentag.length);
		ix = s.indexOf (closetag);
		if (ix >= 0) {
			s = s.substr (0, ix);
			return (s);
			}
		}
	return ("");
	}
function parsePackages (name, s) { //name is something like "dave"
	var magicpattern = "<[{~#--- ", ix, path, htmltext;
	while (s.length > 0) {
		ix = s.indexOf (magicpattern);
		if (ix < 0) {
			break;
			}
		s = s.substr (ix + magicpattern.length);
		ix = s.indexOf ("\n");
		path = s.substr (0, ix);
		s = s.substr (ix + 1);
		ix = s.indexOf (magicpattern);
		if (ix < 0) {
			htmltext = s;
			}
		else {
			htmltext = s.substr (0, ix);
			s = s.substr (ix);
			}

		if (path.length > 0) {
			if (path [0] == "/") { //delete leading slash, if present
				path = path.substr (1);
				}
			s3NewObject (s3HostingPath + name + "/" + path, htmltext, "text/html");
			}
		}
	}
function handlePackagePing (subdomain) { //something like http://dave.smallpict.com/
	var parsedUrl = urlpack.parse (subdomain, true);
	var sections = parsedUrl.host.split (".");
	var name = sections [0];

	getNameRecord (name, function (jsontext) {
		var obj = JSON.parse (jsontext);
		httpReadUrl (obj.opmlUrl, function (httptext) {
			var urlpackage = scrapeTagValue (httptext, "linkHosting");
			httpReadUrl (urlpackage, function (packagetext) {
				parsePackages (name, packagetext);

				obj.whenLastUpdate = new Date ().toString ();
				obj.urlRedirect = "http:/" + s3HostingPath + name + "/";
				updateNameRecord (name, obj);
				});
			});
		});
	}

console.log ("Fargo Publisher server v" + myVersion);

var server = http.createServer (function (httpRequest, httpResponse) {
	if (httpRequest.method == "HEAD") {
		httpRequest.end (null);
		}
	else {
		var parsedUrl = urlpack.parse (httpRequest.url, true);

		console.log (parsedUrl.hostname + "/" + parsedUrl.pathname);

		switch (parsedUrl.pathname.toLowerCase ()) {
			case "/pingpackage":
				httpResponse.writeHead (200, {"Content-Type": "application/json", "Access-Control-Allow-Origin": "fargo.io"});

				handlePackagePing (parsedUrl.query.link);

				var x = {"url": parsedUrl.query.link};
				var s = "getData (" + JSON.stringify (x) + ")";
				httpResponse.end (s);

				break;
			case "/isnameavailable":
				function sendStringBack (s) {
					var x = {"message": s};
					httpResponse.end ("getData (" + JSON.stringify (x) + ")");
					}
				httpResponse.writeHead (200, {"Content-Type": "application/json", "Access-Control-Allow-Origin": "fargo.io"});

				var name = cleanName (parsedUrl.query.name);

				if (name.length == 0) {
					sendStringBack ("");
					}
				else {
					if (name.length < 4) {
						sendStringBack ("Name must be 4 or more characters.");
						}
					else {
						isNameDefined (name, function (fldefined) {
							var color, answer;
							if (fldefined) {
								color = "red";
								answer = "is not";
								}
							else {
								color = "green";
								answer = "is";
								}
							sendStringBack ("" + name + "." + myDomain + " " + answer + " available.")
							});
						}
					}

				break;
			case "/newoutlinename":
				var recordkey = cleanName (parsedUrl.query.name), url = parsedUrl.query.url;

				console.log ("Create new outline name: " + recordkey + ", url=" + url);

				httpResponse.writeHead (200, {"Content-Type": "application/json", "Access-Control-Allow-Origin": "fargo.io"});

				if (url == undefined) {
					var x = {flError: true, errorString: "Can't assign the name because there is no url parameter provided."};
					httpResponse.end ("getData (" + JSON.stringify (x) + ")");
					}
				else {
					isNameDefined (recordkey, function (fldefined) {
						if (fldefined) {
							var x = {flError: true, errorString: "Can't assign the name '" + recordkey + "' to the outline because there already is an outline with that name."};
							httpResponse.end ("getData (" + JSON.stringify (x) + ")");
							}
						else {
							addNameRecord (recordkey, url, function (err, data) {
								if (err) {
									httpResponse.end ("getData (" + JSON.stringify (err) + ")");
									}
								else {
									var x = {flError: false, name: recordkey + "." + myDomain};
									httpResponse.end ("getData (" + JSON.stringify (x) + ")");
									}
								});
							}
						});
					}
				break;
			case "/geturlfromname":
				var name = cleanName (parsedUrl.query.name);
				httpResponse.writeHead (200, {"Content-Type": "application/json", "Access-Control-Allow-Origin": "fargo.io"});
				getNameRecord (name, function (jsontext) {
					if (jsontext == null) {
						var x = {flError: true, errorString: "Can't open the outline named '" + name + "' because there is no outline with that name."};
						httpResponse.end ("getData (" + JSON.stringify (x) + ")");
						}
					else {
						var obj = JSON.parse (jsontext);
						var x = {flError: false, url: obj.opmlUrl};
						httpResponse.end ("getData (" + JSON.stringify (x) + ")");
						}
					});
				break;
			case "/version":
				httpResponse.writeHead (200, {"Content-Type": "text/plain", "Access-Control-Allow-Origin": "fargo.io"});
				httpResponse.end (myVersion);
				break;
			default:
				httpResponse.writeHead (404, {"Content-Type": "text/plain", "Access-Control-Allow-Origin": "fargo.io"});
				httpResponse.end ("404 Not Found");
				break;
			}
		}
	});

if (myPort == undefined) {
	myPort = 80;
	}
server.listen (myPort);
<|endoftext|>


<commit_before><commit_msg>Don't have disassemblers chase around foes<commit_after><|endoftext|>


<commit_before>'use strict';

module.exports = {
  db: 'mongodb://localhost/mean-test',
  http: {
    port: 3001
  },
  app: {
    name: 'MEAN - A Modern Stack - Test'
  },
  facebook: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/facebook/callback'
  },
  twitter: {
    clientID: 'CONSUMER_KEY',
    clientSecret: 'CONSUMER_SECRET',
    callbackURL: 'http://localhost:3000/auth/twitter/callback'
  },
  github: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/github/callback'
  },
  google: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/google/callback'
  },
  linkedin: {
    clientID: 'API_KEY',
    clientSecret: 'SECRET_KEY',
    callbackURL: 'http://localhost:3000/auth/linkedin/callback'
  },
  emailFrom: 'SENDER EMAIL ADDRESS', // sender address like ABC <abc@example.com>
  mailer: {
    service: 'SERVICE_PROVIDER',
    auth: {
      user: 'EMAIL_ID',
      pass: 'PASSWORD'
    }
  }
};
<commit_msg>fix: detect mongo host in docker env<commit_after>'use strict';

module.exports = {
  db: 'mongodb://' + (process.env.DB_PORT_27017_TCP_ADDR || 'localhost') + '/mean-test',
  http: {
    port: 3001
  },
  app: {
    name: 'MEAN - A Modern Stack - Test'
  },
  facebook: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/facebook/callback'
  },
  twitter: {
    clientID: 'CONSUMER_KEY',
    clientSecret: 'CONSUMER_SECRET',
    callbackURL: 'http://localhost:3000/auth/twitter/callback'
  },
  github: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/github/callback'
  },
  google: {
    clientID: 'APP_ID',
    clientSecret: 'APP_SECRET',
    callbackURL: 'http://localhost:3000/auth/google/callback'
  },
  linkedin: {
    clientID: 'API_KEY',
    clientSecret: 'SECRET_KEY',
    callbackURL: 'http://localhost:3000/auth/linkedin/callback'
  },
  emailFrom: 'SENDER EMAIL ADDRESS', // sender address like ABC <abc@example.com>
  mailer: {
    service: 'SERVICE_PROVIDER',
    auth: {
      user: 'EMAIL_ID',
      pass: 'PASSWORD'
    }
  }
};
<|endoftext|>


<commit_before>import React from 'react';
import {Whiteboard, Events, SvgConverter} from '../../../src/index';

const strokeColors = ['black', 'red', 'blue', 'green', 'yellow'];
let strokeColorIndex = 0;

export default class App extends React.Component {

    constructor() {
        super();

        this.events = new Events();
    }

    componentDidMount() {
        document.body.addEventListener('keydown', (ev) => {
            if (ev.keyCode >= 49 && ev.keyCode <= 57) { // '1' - '9'
                this.events.changeStrokeWidth(ev.keyCode - 48);
            }
            if (ev.keyCode === 67) { // 'c'
                strokeColorIndex = (strokeColorIndex + 1) % strokeColors.length;
                this.events.changeStrokeColor(strokeColors[strokeColorIndex]);
            }
        });

        // TODO
        this.undoLink.addEventListener('mouseover', (ev) => {
            this.events.undoPoint();
        });

        // TODO
        this.redoLink.addEventListener('mouseover', (ev) => {
            this.events.redoPoint();
        });

        this.clearButton.addEventListener('click', (ev) => {
            this.events.clearPoint();
            ev.preventDefault();
            ev.stopPropagation();
            return false;
        });

        let downloadAsPng = document.querySelector('#download-as-png');
        downloadAsPng.addEventListener('mouseover', () => {
            let svg = document.querySelector('svg');
            let converter = new SvgConverter(svg);
            converter.toPngData().then(data => {
                downloadAsPng.href = data;
                downloadAsPng.download = 'xxx.png';
            });
        });

        let downloadAsJpeg = document.querySelector('#download-as-jpeg');
        downloadAsJpeg.addEventListener('mouseover', () => {
            let svg = document.querySelector('svg');
            let converter = new SvgConverter(svg);
            converter.toJpegData().then(data => {
                downloadAsJpeg.href = data;
                downloadAsJpeg.download = 'xxx.jpeg';
            });
        });

        let downloadAsSvg = document.querySelector('#download-as-svg');
        downloadAsSvg.addEventListener('mouseover', () => {
            let svg = document.querySelector('svg');
            let converter = new SvgConverter(svg);
            converter.toSvgData().then(data => {
                downloadAsSvg.href = data;
                downloadAsSvg.download = 'xxx.svg';
            });
        });
    }

    render() {
        const linkLikeStyle = {
            borderBottom: '1px solid blue',
            color: 'blue',
            cursor: 'pointer',
        };

        return (

React-Whiteboard Sample

{'To start/stop drawing line, click in the whiteboard.'}

{'To switch color, black -> red -> blue -> green -> yellow, press c key.'}

{'To select stroke width, press 1-9 key.'}

                <Whiteboard events={this.events} width={800} height={600} style={{backgroundColor: 'lightyellow'}}></Whiteboard>
                [ this.undoLink = undoLink} style={linkLikeStyle}>{' << '}]
                [ this.redoLink = redoLink} style={linkLikeStyle}>{' >> '}]
                <button ref={clearButton => this.clearButton = clearButton}>{'clear'}</button>

        );
    }
}
<commit_msg>Update sample<commit_after>import React from 'react';
import {Whiteboard, Events, SvgConverter} from '../../../src/index';

const strokeColors = ['black', 'red', 'blue', 'green', 'yellow'];
let strokeColorIndex = 0;

export default class App extends React.Component {

    constructor() {
        super();

        this.events = new Events();
    }

    componentDidMount() {
        document.body.addEventListener('keydown', (ev) => {
            if (ev.keyCode >= 49 && ev.keyCode <= 57) { // '1' - '9'
                this.events.changeStrokeWidth(ev.keyCode - 48);
            }
            if (ev.keyCode === 67) { // 'c'
                strokeColorIndex = (strokeColorIndex + 1) % strokeColors.length;
                this.events.changeStrokeColor(strokeColors[strokeColorIndex]);
            }
        });

        this.undoButton.addEventListener('click', () => {
            this.events.undoPoint();
        });

        this.redoButton.addEventListener('click', () => {
            this.events.redoPoint();
        });

        this.clearButton.addEventListener('click', () => {
            this.events.clearPoint();
        });

        let downloadAsPng = document.querySelector('#download-as-png');
        downloadAsPng.addEventListener('mouseover', () => {
            let svg = document.querySelector('svg');
            let converter = new SvgConverter(svg);
            converter.toPngData().then(data => {
                downloadAsPng.href = data;
                downloadAsPng.download = 'xxx.png';
            });
        });

        let downloadAsJpeg = document.querySelector('#download-as-jpeg');
        downloadAsJpeg.addEventListener('mouseover', () => {
            let svg = document.querySelector('svg');
            let converter = new SvgConverter(svg);
            converter.toJpegData().then(data => {
                downloadAsJpeg.href = data;
                downloadAsJpeg.download = 'xxx.jpeg';
            });
        });

        let downloadAsSvg = document.querySelector('#download-as-svg');
        downloadAsSvg.addEventListener('mouseover', () => {
            let svg = document.querySelector('svg');
            let converter = new SvgConverter(svg);
            converter.toSvgData().then(data => {
                downloadAsSvg.href = data;
                downloadAsSvg.download = 'xxx.svg';
            });
        });
    }

    render() {
        return (

React-Whiteboard Sample

{'To start/stop drawing line, click in the whiteboard.'}

{'To switch color, black -> red -> blue -> green -> yellow, press c key.'}

{'To select stroke width, press 1-9 key.'}

                <Whiteboard events={this.events} width={800} height={600} style={{backgroundColor: 'lightyellow'}}></Whiteboard>
                <button ref={undoButton => this.undoButton = undoButton}>Undo</button>
                <button ref={redoButton => this.redoButton = redoButton}>Redo</button>
                <button ref={clearButton => this.clearButton = clearButton}>Clear</button>

        );
    }
}
<|endoftext|>


<commit_before>// TODO: think about clear noise option. See beer example.
var Pixfinder = function (options) {
    var opt = options;

    opt.accuracy = opt.accuracy || 3;
    opt.distance = opt.distance || 15;
    opt.tolerance = opt.tolerance || 50;
    opt.colors = Pixfinder._colorsToRgb(opt.colors);
    opt.img = Object.prototype.toString.call(opt.img) === '[object String]' ?
        document.getElementById(opt.img) : opt.img;

    var processImg = function() {
        var canv = Pixfinder._wrapByCanvas(opt.img);
        var t0 = new Date();
        var regionsPxs = Pixfinder._getRegionsPixels(canv, opt.colors, opt.accuracy, opt.tolerance);
        var t1 = new Date();
        console.log('_getRegionsPixels: ', t1 - t0); // 10340 ms. -> 288 ms. Fuck yeah!

        var t0 = new Date();
        var edges = Pixfinder._splitByDist(regionsPxs, opt.distance);
        var t1 = new Date();
        console.log('_splitByDist: ', t1 - t0);

        if (typeof options.onload !== 'undefined') {
            options.onload({
                edges: edges
            });
        }
    }

    if (Pixfinder._isImgLoaded(opt.img)) {
        processImg();
    } else {
        Pixfinder._on('load', opt.img, processImg);
    }
}

Pixfinder._on = function(ev, el, func) {
    if (el.addEventListener) {
        el.addEventListener(ev, func, false);
    } else if (el.attachEvent)  {
        el.attachEvent('on' + ev, func);
    }
}

Pixfinder._isImgLoaded = function(img) {
    return !(typeof img.naturalWidth !== "undefined" && img.naturalWidth == 0);
}

Pixfinder._colorsToRgb = function(cols) { // (Array) -> Array
    for (var i = 0; i < cols.length; i++) {
        cols[i] = Pixfinder.Util.Color.toRGB(cols[i]);
    };
    return cols;
}

Pixfinder._wrapByCanvas = function(img) { // (HTMLImageElement) -> HTMLCanvasElement
    var canv = document.createElement('canvas');
    canv.width = img.width;
    canv.height = img.height;
    canv.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
    return canv;
}

Pixfinder._getRegionsPixels = function(canvas, colors, accuracy, tolerance) { // (HTMLCanvasElement, Array, Number, Number) -> Array
    var res = [],
        ctx = canvas.getContext('2d'),
        imgSize = {
            w: canvas.width,
            h: canvas.height
        },
        imgCols = ctx.getImageData(0, 0, imgSize.w, imgSize.h).data;

    for (var i = 0; i < imgCols.length; i+=4) { // 4 - rgba
        var pxCol = [imgCols[i], imgCols[i+1], imgCols[i+2], imgCols[i+3]],
            nPxCols = Pixfinder._getNeighborPixelsColors(i, imgCols, {
                w: canvas.width,
                h: canvas.height
            }, accuracy),
            px = Pixfinder._getPixelByColorPosition(i, imgSize);

        // skip if px is not a boundary pixel of the feature
        if (Pixfinder._areColorsEqualToColor(Pixfinder.Util.Color.areSimilar, nPxCols, pxCol, tolerance) === true) {
            continue;
        }

        // is it pixel of the feature?
        if(Pixfinder._isColorInColors(Pixfinder.Util.Color.areSimilar, pxCol, colors, tolerance)) {
            res.push(px);
        }
    }

    return res;
}

Pixfinder._getPixelByColorPosition = function(colPos, imgSize) { // (Number, Object) -> Object
    px = {x: 0, y: 0};
    px.y = parseInt(colPos / (imgSize.w*4));
    px.x = colPos/4 - px.y*imgSize.w;
    return px;
}

Pixfinder._getNeighborPixelsColors = function(colPos, imgCols, imgSize, accuracy) { // (Number, Array, Object, Number) -> Array
    var res = [],
        tlPos, tPos, trPos, rPos,
        brPos, bPos, blPos, lPos,
        px = Pixfinder._getPixelByColorPosition(colPos, imgSize);

    if (px.x > 0 && px.y > 0) {
        tlPos = colPos - 4 - imgSize.w*4;
        res.push([ // top left color
            imgCols[tlPos],
            imgCols[tlPos+1],
            imgCols[tlPos+2],
            imgCols[tlPos+3]
        ]);
    }

    if (px.y > 0) {
        tPos = colPos - imgSize.w*4;
        res.push([ // top color
            imgCols[tPos],
            imgCols[tPos+1],
            imgCols[tPos+2],
            imgCols[tPos+3]
        ]);
    }

    if (px.x < imgSize.w && px.y > 0) {
        trPos = colPos - imgSize.w*4 + 4;
        res.push([ // top right color
            imgCols[trPos],
            imgCols[trPos+1],
            imgCols[trPos+2],
            imgCols[trPos+3]
        ]);
    }

    if (px.x < imgSize.w) {
        rPos = colPos + 4;
        res.push([ // right color
            imgCols[rPos],
            imgCols[rPos+1],
            imgCols[rPos+2],
            imgCols[rPos+3]
        ]);
    };

    if (px.x < imgSize.w && px.y < imgSize.h) {
        brPos = colPos + imgSize.w*4 + 4;
        res.push([ // bottom right color
            imgCols[brPos],
            imgCols[brPos+1],
            imgCols[brPos+2],
            imgCols[brPos+3]
        ]);
    }

    if (px.y < imgSize.h) {
        bPos = colPos + imgSize.w*4;
        res.push([ // bottom color
            imgCols[bPos],
            imgCols[bPos+1],
            imgCols[bPos+2],
            imgCols[bPos+3]
        ]);
    }

    if (px.x > 0 && px.y < imgSize.h) {
        blPos = colPos + imgSize.w*4 - 4;
        res.push([ // bottom left color
            imgCols[blPos],
            imgCols[blPos+1],
            imgCols[blPos+2],
            imgCols[blPos+3]
        ]);
    }

    if (px.x > 0) {
        lPos = colPos - 4;
        res.push([ // left color
            imgCols[lPos],
            imgCols[lPos+1],
            imgCols[lPos+2],
            imgCols[lPos+3]
        ]);
    }

    return res;
}

Pixfinder._areColorsEqualToColor = function(checkingFunc, cols, col, tolerance) { // (Function, Array, Array, Number) -> Boolean
    for (var i = 0; i < cols.length; i++) {
        if (checkingFunc(col, cols[i], tolerance) === false) {
            return false;
        };
    };
    return true;
}

Pixfinder._isColorInColors = function(checkingFunc, col, cols, tolerance) { // (Function, Array, Array, Number) -> Boolean
    for (var i = 0; i < cols.length; i++) {
        if (checkingFunc(col, cols[i], tolerance) === true) {
            return true;
        };
    };
    return false;
}

Pixfinder._splitByDist = function(pixels, dist) { // (Array, Number) -> Array
    var set = disjointSet(),
        res;

    for (var i = 0; i < pixels.length; i++) {
        set.add(pixels[i]);
        for (var j = i; j >= 0; j--) {
            set.add(pixels[j]);
            if (Pixfinder.Util.Math.getDistance(pixels[i], pixels[j]) <= dist) {
                if (!set.connected(pixels[i], pixels[j])) {
                    set.union(pixels[i], pixels[j]);
                }
            }
        }
    }
    res = set.extract();
    set.destroy();
    return res;
}

var pixfinder = function(options) {
    return new Pixfinder(options);
}<commit_msg>accuracy fixed<commit_after>// TODO: think about clear noise option. See beer example.
var Pixfinder = function (options) {
    var opt = options;

    opt.accuracy = opt.accuracy || 3;
    opt.distance = opt.distance || 15;
    opt.tolerance = opt.tolerance || 50;
    opt.colors = Pixfinder._colorsToRgb(opt.colors);
    opt.img = Object.prototype.toString.call(opt.img) === '[object String]' ?
        document.getElementById(opt.img) : opt.img;

    var processImg = function() {
        var canv = Pixfinder._wrapByCanvas(opt.img);
        var t0 = new Date();
        var regionsPxs = Pixfinder._getRegionsPixels(canv, opt.colors, opt.accuracy, opt.tolerance);
        var t1 = new Date();
        console.log('_getRegionsPixels: ', t1 - t0); // 10340 ms. -> 288 ms. Fuck yeah!

        var t0 = new Date();
        var edges = Pixfinder._splitByDist(regionsPxs, opt.distance);
        var t1 = new Date();
        console.log('_splitByDist: ', t1 - t0);

        if (typeof options.onload !== 'undefined') {
            options.onload({
                edges: edges
            });
        }
    }

    if (Pixfinder._isImgLoaded(opt.img)) {
        processImg();
    } else {
        Pixfinder._on('load', opt.img, processImg);
    }
}

Pixfinder._on = function(ev, el, func) {
    if (el.addEventListener) {
        el.addEventListener(ev, func, false);
    } else if (el.attachEvent)  {
        el.attachEvent('on' + ev, func);
    }
}

Pixfinder._isImgLoaded = function(img) {
    return !(typeof img.naturalWidth !== "undefined" && img.naturalWidth == 0);
}

Pixfinder._colorsToRgb = function(cols) { // (Array) -> Array
    for (var i = 0; i < cols.length; i++) {
        cols[i] = Pixfinder.Util.Color.toRGB(cols[i]);
    };
    return cols;
}

Pixfinder._wrapByCanvas = function(img) { // (HTMLImageElement) -> HTMLCanvasElement
    var canv = document.createElement('canvas');
    canv.width = img.width;
    canv.height = img.height;
    canv.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
    return canv;
}

Pixfinder._getRegionsPixels = function(canvas, colors, accuracy, tolerance) { // (HTMLCanvasElement, Array, Number, Number) -> Array
    var res = [],
        ctx = canvas.getContext('2d'),
        imgSize = {
            w: canvas.width,
            h: canvas.height
        },
        imgCols = ctx.getImageData(0, 0, imgSize.w, imgSize.h).data;

    for (var i = 0; i < imgCols.length; i+=(4*accuracy)) { // 4 - rgba
        var pxCol = [imgCols[i], imgCols[i+1], imgCols[i+2], imgCols[i+3]],
            nPxCols = Pixfinder._getNeighborPixelsColors(i, imgCols, {
                w: canvas.width,
                h: canvas.height
            }, accuracy),
            px = Pixfinder._getPixelByColorPosition(i, imgSize);

        // skip if px is not a boundary pixel of the feature
        if (Pixfinder._areColorsEqualToColor(Pixfinder.Util.Color.areSimilar, nPxCols, pxCol, tolerance) === true) {
            continue;
        }

        // is it pixel of the feature?
        if(Pixfinder._isColorInColors(Pixfinder.Util.Color.areSimilar, pxCol, colors, tolerance)) {
            res.push(px);
        }
    }

    return res;
}

Pixfinder._getPixelByColorPosition = function(colPos, imgSize) { // (Number, Object) -> Object
    px = {x: 0, y: 0};
    px.y = parseInt(colPos / (imgSize.w*4));
    px.x = colPos/4 - px.y*imgSize.w;
    return px;
}

Pixfinder._getNeighborPixelsColors = function(colPos, imgCols, imgSize) { // (Number, Array, Object) -> Array
    var res = [],
        tlPos, tPos, trPos, rPos,
        brPos, bPos, blPos, lPos,
        px = Pixfinder._getPixelByColorPosition(colPos, imgSize);

    if (px.x > 0 && px.y > 0) {
        tlPos = colPos - 4 - imgSize.w*4;
        res.push([ // top left color
            imgCols[tlPos],
            imgCols[tlPos+1],
            imgCols[tlPos+2],
            imgCols[tlPos+3]
        ]);
    }

    if (px.y > 0) {
        tPos = colPos - imgSize.w*4;
        res.push([ // top color
            imgCols[tPos],
            imgCols[tPos+1],
            imgCols[tPos+2],
            imgCols[tPos+3]
        ]);
    }

    if (px.x < imgSize.w && px.y > 0) {
        trPos = colPos - imgSize.w*4 + 4;
        res.push([ // top right color
            imgCols[trPos],
            imgCols[trPos+1],
            imgCols[trPos+2],
            imgCols[trPos+3]
        ]);
    }

    if (px.x < imgSize.w) {
        rPos = colPos + 4;
        res.push([ // right color
            imgCols[rPos],
            imgCols[rPos+1],
            imgCols[rPos+2],
            imgCols[rPos+3]
        ]);
    };

    if (px.x < imgSize.w && px.y < imgSize.h) {
        brPos = colPos + imgSize.w*4 + 4;
        res.push([ // bottom right color
            imgCols[brPos],
            imgCols[brPos+1],
            imgCols[brPos+2],
            imgCols[brPos+3]
        ]);
    }

    if (px.y < imgSize.h) {
        bPos = colPos + imgSize.w*4;
        res.push([ // bottom color
            imgCols[bPos],
            imgCols[bPos+1],
            imgCols[bPos+2],
            imgCols[bPos+3]
        ]);
    }

    if (px.x > 0 && px.y < imgSize.h) {
        blPos = colPos + imgSize.w*4 - 4;
        res.push([ // bottom left color
            imgCols[blPos],
            imgCols[blPos+1],
            imgCols[blPos+2],
            imgCols[blPos+3]
        ]);
    }

    if (px.x > 0) {
        lPos = colPos - 4;
        res.push([ // left color
            imgCols[lPos],
            imgCols[lPos+1],
            imgCols[lPos+2],
            imgCols[lPos+3]
        ]);
    }

    return res;
}

Pixfinder._areColorsEqualToColor = function(checkingFunc, cols, col, tolerance) { // (Function, Array, Array, Number) -> Boolean
    for (var i = 0; i < cols.length; i++) {
        if (checkingFunc(col, cols[i], tolerance) === false) {
            return false;
        };
    };
    return true;
}

Pixfinder._isColorInColors = function(checkingFunc, col, cols, tolerance) { // (Function, Array, Array, Number) -> Boolean
    for (var i = 0; i < cols.length; i++) {
        if (checkingFunc(col, cols[i], tolerance) === true) {
            return true;
        };
    };
    return false;
}

Pixfinder._splitByDist = function(pixels, dist) { // (Array, Number) -> Array
    var set = disjointSet(),
        res;

    for (var i = 0; i < pixels.length; i++) {
        set.add(pixels[i]);
        for (var j = i; j >= 0; j--) {
            set.add(pixels[j]);
            if (Pixfinder.Util.Math.getDistance(pixels[i], pixels[j]) <= dist) {
                if (!set.connected(pixels[i], pixels[j])) {
                    set.union(pixels[i], pixels[j]);
                }
            }
        }
    }
    res = set.extract();
    set.destroy();
    return res;
}

var pixfinder = function(options) {
    return new Pixfinder(options);
}<|endoftext|>