<commit_before>/**
 * Copyright 2013, 2015 IBM Corp.
 *
 * 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.
 *
 * Original and good work by IBM
 * "Big Nodes" mods by Jacques W
 *
 * /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
 *
 *  Big Nodes principles:
 *
 *  #1 can handle big data
 *  #2 send start/end messages
 *  #3 tell what they are doing
 *
 *  Any issues? https://github.com/Jacques44
 *
 * /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
 *
 **/

module.exports = function(RED) {
    "use strict";
    var fs = require("fs-extra");
    var os = require("os");
    var stream = require('stream');
    var domain = require('domain');
    var filesize = require('filesize');

    function BigFileNode(n) {
        RED.nodes.createNode(this,n);
        this.filename = n.filename;
        this.appendNewline = n.appendNewline;
        this.overwriteFile = n.overwriteFile.toString();
        this.createDir = n.createDir || false;
        var node = this;

        this.on("input",function(msg) {
            var filename = node.filename || msg.filename || "";
            if (!node.filename) {
                node.status({fill:"grey",shape:"dot",text:filename});
            }
            if (filename === "") {
                node.warn(RED._("file.errors.nofilename"));
            } else if (msg.hasOwnProperty("payload") && (typeof msg.payload !== "undefined")) {
                var data = msg.payload;
                if ((typeof data === "object") && (!Buffer.isBuffer(data))) {
                    data = JSON.stringify(data);
                }
                if (typeof data === "boolean") { data = data.toString(); }
                if (typeof data === "number") { data = data.toString(); }
                if ((this.appendNewline) && (!Buffer.isBuffer(data))) { data += os.EOL; }
                data = new Buffer(data);
                if (this.overwriteFile === "true") {
                    // using "binary" not {encoding:"binary"} to be 0.8 compatible for a while
                    fs.writeFile(filename, data, "binary", function (err) {
                    //fs.writeFile(filename, data, {encoding:"binary"}, function (err) {
                        if (err) {
                            if ((err.code === "ENOENT") && node.createDir) {
                                fs.ensureFile(filename, function (err) {
                                    if (err) { node.error(RED._("file.errors.createfail",{error:err.toString()}),msg); }
                                    else {
                                        fs.writeFile(filename, data, "binary", function (err) {
                                            if (err) { node.error(RED._("file.errors.writefail",{error:err.toString()}),msg); }
                                        });
                                    }
                                });
                            }
                            else { node.error(RED._("file.errors.writefail",{error:err.toString()}),msg); }
                        }
                        else if (RED.settings.verbose) { node.log(RED._("file.status.wrotefile",{file:filename})); }
                    });
                }
                else if (this.overwriteFile === "delete") {
                    fs.unlink(filename, function (err) {
                        if (err) { node.error(RED._("file.errors.deletefail",{error:err.toString()}),msg); }
                        else if (RED.settings.verbose) { node.log(RED._("file.status.deletedfile",{file:filename})); }
                    });
                }
                else {
                    // using "binary" not {encoding:"binary"} to be 0.8 compatible for a while longer
                    fs.appendFile(filename, data, "binary", function (err) {
                    //fs.appendFile(filename, data, {encoding:"binary"}, function (err) {
                        if (err) {
                            if ((err.code === "ENOENT") && node.createDir) {
                                fs.ensureFile(filename, function (err) {
                                    if (err) { node.error(RED._("file.errors.createfail",{error:err.toString()}),msg); }
                                    else {
                                        fs.appendFile(filename, data, "binary", function (err) {
                                            if (err) { node.error(RED._("file.errors.appendfail",{error:err.toString()}),msg); }
                                        });
                                    }
                                });
                            }
                            else { node.error(RED._("file.errors.appendfail",{error:err.toString()}),msg); }
                        }
                        else if (RED.settings.verbose) { node.log(RED._("file.status.appendedfile",{file:filename})); }
                    });
                }
            }
        });
    }
    RED.nodes.registerType("bigfile",BigFileNode);

    function BigFileInNode(n) {
        RED.nodes.createNode(this,n);

        this.filename = n.filename;
        this.format = n.format;
        var node = this;
        var options = {};
        if (this.format) {
            options['encoding'] = this.format;
        }

        var bignode = n.bignode || true;
        var buffer = n.buffer || 64;

        this.on("input",function(msg) {

            // Now filename maybe payload because it's a pain to add a function just to rename payload to filename!
            var filename = node.filename || msg.filename || msg.payload || "";
            if (!node.filename) {
                node.status({fill:"grey",shape:"dot",text:filename});
            }
            if (filename === "") {
                node.warn(RED._("file.errors.nofilename"));
            } else {
                msg.filename = filename;

                if (bignode) {

                    // Big Blue nodes principles !

                    var nb = 0;
                    var size = 0;

                    var outer = new stream.Transform({ objectMode: true });
                    outer._transform = function(data, encoding, done) {

                        size += data.length;

                        // #3 big node principle: tell me what you are doing, so far
                        if (++nb % 1 == 0) node.status({fill: "blue", shape: "dot", text: "sending... " + filesize(size) + " so far"});

                        // #1 big node principle: send blocks for big files management
                        node.send({ payload: data });

                        done();
                    }

                    var finished = function() {
                        outer.end();

                        // #2 big node principle: send controls using msg.control messages
                        node.send({ control: "end" });

                        // #3 big node principle: tell me when you've done the work
                        node.status({fill: "green", shape: "dot", text: "done with " + filesize(size) });
                    }

                    options.bufferSize = buffer * 1024;

                    // Error management using domain
                    var d = domain.create();
                    d.on('error', function(err) {
                        // #3 big node principle: don't be shy, tell me too when something wrong
                        node.status({fill: "red", shape: "dot", text: err.message });
                        node.error(err);
                    });

                    // #2 big node principle: send controls using msg.control messages
                    node.send({ control: "start" });

                    // Go!
                    d.run(function() {
                        fs.createReadStream(filename, options)
                        .pipe(outer)
                        .on('finish', finished);
                    });

                } else {

                    // Original work by IBM
                    fs.readFile(filename,options,function(err,data) {
                        if (err) {
                            node.error(err,msg);
                            msg.error = err;
                            delete msg.payload;
                        } else {
                            msg.payload = data;
                            delete msg.error;
                        }
                        node.send(msg);
                    });

                }

            }
        });
    }
    RED.nodes.registerType("bigfile in",BigFileInNode);
}
<commit_msg>Big work<commit_after>/**
 * Copyright 2013, 2015 IBM Corp.
 *
 * 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.
 *
 * Original and good work by IBM
 * "Big Nodes" mods by Jacques W
 *
 * /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
 *
 *  Big Nodes principles:
 *
 *  #1 can handle big data
 *  #2 send start/end messages
 *  #3 tell what they are doing
 *
 *  Any issues? https://github.com/Jacques44
 *
 * /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
 *
 **/

module.exports = function(RED) {
    "use strict";
    var fs = require("fs-extra");
    var os = require("os");
    var stream = require('stream');
    var domain = require('domain');
    var filesize = require('filesize');

    function BigFileNode(n) {
        RED.nodes.createNode(this,n);
        this.filename = n.filename;
        this.appendNewline = n.appendNewline;
        this.overwriteFile = n.overwriteFile.toString();
        this.createDir = n.createDir || false;
        var node = this;

        this.on("input",function(msg) {
            var filename = node.filename || msg.filename || "";
            if (!node.filename) {
                node.status({fill:"grey",shape:"dot",text:filename});
            }
            if (filename === "") {
                node.warn(RED._("file.errors.nofilename"));
            } else if (msg.hasOwnProperty("payload") && (typeof msg.payload !== "undefined")) {
                var data = msg.payload;
                if ((typeof data === "object") && (!Buffer.isBuffer(data))) {
                    data = JSON.stringify(data);
                }
                if (typeof data === "boolean") { data = data.toString(); }
                if (typeof data === "number") { data = data.toString(); }
                if ((this.appendNewline) && (!Buffer.isBuffer(data))) { data += os.EOL; }
                data = new Buffer(data);
                if (this.overwriteFile === "true") {
                    // using "binary" not {encoding:"binary"} to be 0.8 compatible for a while
                    fs.writeFile(filename, data, "binary", function (err) {
                    //fs.writeFile(filename, data, {encoding:"binary"}, function (err) {
                        if (err) {
                            if ((err.code === "ENOENT") && node.createDir) {
                                fs.ensureFile(filename, function (err) {
                                    if (err) { node.error(RED._("file.errors.createfail",{error:err.toString()}),msg); }
                                    else {
                                        fs.writeFile(filename, data, "binary", function (err) {
                                            if (err) { node.error(RED._("file.errors.writefail",{error:err.toString()}),msg); }
                                        });
                                    }
                                });
                            }
                            else { node.error(RED._("file.errors.writefail",{error:err.toString()}),msg); }
                        }
                        else if (RED.settings.verbose) { node.log(RED._("file.status.wrotefile",{file:filename})); }
                    });
                }
                else if (this.overwriteFile === "delete") {
                    fs.unlink(filename, function (err) {
                        if (err) { node.error(RED._("file.errors.deletefail",{error:err.toString()}),msg); }
                        else if (RED.settings.verbose) { node.log(RED._("file.status.deletedfile",{file:filename})); }
                    });
                }
                else {
                    // using "binary" not {encoding:"binary"} to be 0.8 compatible for a while longer
                    fs.appendFile(filename, data, "binary", function (err) {
                    //fs.appendFile(filename, data, {encoding:"binary"}, function (err) {
                        if (err) {
                            if ((err.code === "ENOENT") && node.createDir) {
                                fs.ensureFile(filename, function (err) {
                                    if (err) { node.error(RED._("file.errors.createfail",{error:err.toString()}),msg); }
                                    else {
                                        fs.appendFile(filename, data, "binary", function (err) {
                                            if (err) { node.error(RED._("file.errors.appendfail",{error:err.toString()}),msg); }
                                        });
                                    }
                                });
                            }
                            else { node.error(RED._("file.errors.appendfail",{error:err.toString()}),msg); }
                        }
                        else if (RED.settings.verbose) { node.log(RED._("file.status.appendedfile",{file:filename})); }
                    });
                }
            }
        });
    }
    RED.nodes.registerType("bigfile",BigFileNode);

    function BigFileInNode(n) {
        RED.nodes.createNode(this,n);

        this.filename = n.filename;
        this.format = n.format;
        var node = this;
        var options = {};
        if (this.format) {
            options['encoding'] = this.format;
        }

        var bignode = n.bignode || true;
        var buffer = n.buffer || 64;

        this.on("input",function(msg) {

            // Now filename maybe payload because it's a pain to add a function just to rename payload to filename!
            var filename = node.filename || msg.filename || msg.payload || "";
            if (!node.filename) {
                node.status({fill:"grey",shape:"dot",text:filename});
            }
            if (filename === "") {
                node.warn(RED._("file.errors.nofilename"));
            } else {
                msg.filename = filename;

                if (bignode) {

                    // Big Blue nodes principles !

                    var nb = 0;
                    var size = 0;

                    var outer = new stream.Transform({ objectMode: true });
                    outer._transform = function(data, encoding, done) {

                        size += data.length;

                        // #3 big node principle: tell me what you are doing, so far
                        if (++nb % 1 == 0) node.status({fill: "blue", shape: "dot", text: "sending... " + filesize(size) + " so far"});

                        // #1 big node principle: send blocks for big files management
                        node.send({ payload: data });

                        done();
                    }

                    var finished = function() {
                        outer.end();

                        // #2 big node principle: send controls using msg.control messages
                        node.send({ control: { state: "end" } });

                        // #3 big node principle: tell me when you've done the work
                        node.status({fill: "green", shape: "dot", text: "done with " + filesize(size) });
                    }

                    options.bufferSize = buffer * 1024;

                    // Error management using domain
                    var d = domain.create();
                    d.on('error', function(err) {
                        // #3 big node principle: don't be shy, tell me too when something wrong
                        node.status({fill: "red", shape: "dot", text: err.message });
                        node.error(err);
                    });

                    // #2 big node principle: send controls using msg.control messages
                    node.send({ control: { state: "start" } });

                    // Go!
                    d.run(function() {
                        fs.createReadStream(filename, options)
                        .pipe(outer)
                        .on('finish', finished);
                    });

                } else {

                    // Original work by IBM
                    fs.readFile(filename,options,function(err,data) {
                        if (err) {
                            node.error(err,msg);
                            msg.error = err;
                            delete msg.payload;
                        } else {
                            msg.payload = data;
                            delete msg.error;
                        }
                        node.send(msg);
                    });

                }

            }
        });
    }
    RED.nodes.registerType("bigfile in",BigFileInNode);
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
"use strict";
const resolve = require('../lib/resolve');

process.title = 'aurelia';

let originalBaseDir = process.cwd();

resolve('aurelia-cli', {
  basedir: originalBaseDir
}, function(error, projectLocalCli) {
  let cli;

  if (error) {
    cli = new (require('../lib/index').CLI);
    cli.options.runningGlobally = true;
  } else {
    cli = new (require(projectLocalCli).CLI);
    cli.options.runningLocally = true;
  }

  cli.options.originalBaseDir = originalBaseDir;

  let userArgs = process.argv.slice(2);
  let commandName = userArgs[0];
  let commandArgs = userArgs.slice(1);

  cli.run(commandName, commandArgs).catch(error => console.log(error));
});
<commit_msg>feat(cli): add exit error code<commit_after>#!/usr/bin/env node
"use strict";
const resolve = require('../lib/resolve');

process.title = 'aurelia';

let originalBaseDir = process.cwd();

resolve('aurelia-cli', {
  basedir: originalBaseDir
}, function(error, projectLocalCli) {
  let cli;

  if (error) {
    cli = new (require('../lib/index').CLI);
    cli.options.runningGlobally = true;
  } else {
    cli = new (require(projectLocalCli).CLI);
    cli.options.runningLocally = true;
  }

  cli.options.originalBaseDir = originalBaseDir;

  let userArgs = process.argv.slice(2);
  let commandName = userArgs[0];
  let commandArgs = userArgs.slice(1);

  cli.run(commandName, commandArgs).catch((error) => {
      console.log(error);
      process.exit(1);
  });
});
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

var fs = require('fs');

var minimist = require('minimist');
var log = require('npmlog');

var hammerTime = require('../');

var args;
var host;
var port = 80;
var concurrent = 100;
var frequency = 50;
var duration = 60000;
var generator;
var start = (new Date()).getTime();
var verbose = false;
var msgCount = 0;

function usage() {
  fs.createReadStream('usage.txt').pipe(process.stdout);
}

args = minimist(process.argv.slice(2));
host = args._[0];

if (args.version) {
  var pkg = require('../package');
  log.info(pkg.name, 'version', pkg.version);
  process.exit(0);
}

if (!host || args.help || args.h) {
  return usage();
}

port = args.port || args.p || port;
concurrent = args.concurrent || args.c || concurrent;
frequency = args.frequency || args.f || frequency;
duration = args.duration || args.d || duration;
generator = args.generator || args.g || generator;
verbose = args.verbose || args.v || verbose;

hammerTime(host, port, concurrent, frequency, duration, generator)
  .on('start', function() {
    if (verbose) {
      log.info('hammer-time', 'Stop! Hammertime!');
    }
  })
  .on('client-connected', function() {
    if (verbose) {
      log.info('hammer-time', 'Client connected');
    }
  })
  .on('error', function(err) {
    log.error('hammer-time', 'An error occurred')
    log.error('hammer-time', err);
  })
  .on('disconnect', function() {
    if (verbose) {
      log.info('hammer-time', 'Client disconnected');
    }
  })
  .on('message', function(msg) {
    ++msgCount;
    if (verbose) {
      log.info('hammer-time', msg)
    } else {
      process.stdout.write('.');
    }
  })
  .on('end', function() {
    console.log();
    log.info('hammer-time', 'Sent %j messages in %j seconds', msgCount, ((new Date()).getTime() - start) / 1000);
    process.exit(0);
  });<commit_msg>paths for files<commit_after>#!/usr/bin/env node

'use strict';

var fs = require('fs');
var path = require('path');

var minimist = require('minimist');
var log = require('npmlog');

var hammerTime = require('../');

var args;
var host;
var port = 80;
var concurrent = 100;
var frequency = 50;
var duration = 60000;
var generator;
var start = (new Date()).getTime();
var verbose = false;
var msgCount = 0;

function usage() {
  fs.createReadStream(path.join(__dirname, '../usage.txt')).pipe(process.stdout);
}

args = minimist(process.argv.slice(2));
host = args._[0];

if (args.version) {
  var pkg = require(path.join(__dirname, '../package'));
  log.info(pkg.name, 'version', pkg.version);
  process.exit(0);
}

if (!host || args.help || args.h) {
  return usage();
}

port = args.port || args.p || port;
concurrent = args.concurrent || args.c || concurrent;
frequency = args.frequency || args.f || frequency;
duration = args.duration || args.d || duration;
generator = args.generator || args.g || generator;
verbose = args.verbose || args.v || verbose;

hammerTime(host, port, concurrent, frequency, duration, generator)
  .on('start', function() {
    if (verbose) {
      log.info('hammer-time', 'Stop! Hammertime!');
    }
  })
  .on('client-connected', function() {
    if (verbose) {
      log.info('hammer-time', 'Client connected');
    }
  })
  .on('error', function(err) {
    log.error('hammer-time', 'An error occurred')
    log.error('hammer-time', err);
  })
  .on('disconnect', function() {
    if (verbose) {
      log.info('hammer-time', 'Client disconnected');
    }
  })
  .on('message', function(msg) {
    ++msgCount;
    if (verbose) {
      log.info('hammer-time', msg)
    } else {
      process.stdout.write('.');
    }
  })
  .on('end', function() {
    console.log();
    log.info('hammer-time', 'Sent %j messages in %j seconds', msgCount, ((new Date()).getTime() - start) / 1000);
    process.exit(0);
  });<|endoftext|>


<commit_before>#!/usr/bin/env node

/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

var console = require("console");
var fs = require("fs");
var path = require("path");
var spawn = require("child_process").spawn;
var util = require("util");

var usage = (
    "iJavascript Notebook\n" +
    "\n" +
    "Usage:\n" +
    "\n" +
    "    ijs <options>\n" +
    "\n" +
    "The recognised options are:\n" +
    "\n" +
    "    --ijs-debug             enable debug log level\n" +
    "    --ijs-help              show this help\n" +
    "    --ijs-working-dir=path  set working directory for Javascript sessions\n" +
    "                            (default = current working directory)\n" +
    "\n" +
    "and any other options recognised by the IPython notebook; run:\n" +
    "\n" +
    "    ipython notebook --help\n" +
    "\n" +
    "for a full list.\n"
);

var config = {
    nodePath: process.argv[0],
    ijsPath: fs.realpathSync(process.argv[1]),
};
config.rootPath = path.dirname(path.dirname(config.ijsPath));
config.kernelPath = path.join(config.rootPath, "lib", "kernel.js");
config.kernelArgs = [config.nodePath, config.kernelPath];
config.ipythonArgs = ["notebook"];

// Parse command arguments
process.argv.slice(2).forEach(function(e) {
    if (e.lastIndexOf("--KernelManager.kernel_cmd=", 0) === 0) {
        console.warn(util.format("Warning: Flag '%s' skipped", e));
    } else if (e.lastIndexOf("--ijs-debug", 0) === 0) {
        config.kernelArgs.push("--debug");
    } else if (e.lastIndexOf("--ijs-working-dir=", 0) === 0) {
        config.cwd = fs.realpathSync(e.slice(18));
    } else if (e.lastIndexOf("--ijs-help", 0) === 0) {
        console.warn(usage);
        process.exit(0);
    } else if (e.lastIndexOf("--ijs-", 0) === 0) {
        console.warn(util.format("Error: Unrecognised flag '%s'\n", e));
        console.warn(usage);
        process.exit(1);
    } else {
        config.ipythonArgs.push(e);
    }
});

config.cwd = config.cwd || process.cwd();

config.kernelArgs.push(config.cwd);
config.kernelArgs.push("{connection_file}");

config.ipythonArgs.push(util.format(
    "--KernelManager.kernel_cmd=['%s']",
    config.kernelArgs.join("', '")
));

// Run IPython notebook
var ipython = spawn("ipython", config.ipythonArgs, {
    stdio: "inherit"
});

// Relay SIGINT onto ipython
var signal = "SIGINT";
process.on(signal, function() {
    ipython.emit(signal);
});
<commit_msg>bin: Create a kernel spec for IPython 3<commit_after>#!/usr/bin/env node

/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

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

var usage = (
    "iJavascript Notebook\n" +
    "\n" +
    "Usage:\n" +
    "\n" +
    "    ijs <options>\n" +
    "\n" +
    "The recognised options are:\n" +
    "\n" +
    "    --ijs-debug             enable debug log level\n" +
    "    --ijs-help              show this help\n" +
    "    --ijs-working-dir=path  set working directory for Javascript sessions\n" +
    "                            (default = current working directory)\n" +
    "\n" +
    "and any other options recognised by the IPython notebook; run:\n" +
    "\n" +
    "    ipython notebook --help\n" +
    "\n" +
    "for a full list.\n"
);

var config = {
    nodePath: process.argv[0],
    ijsPath: fs.realpathSync(process.argv[1]),
};
config.rootPath = path.dirname(path.dirname(config.ijsPath));
config.kernelPath = path.join(config.rootPath, "lib", "kernel.js");
config.kernelArgs = [config.nodePath, config.kernelPath];
config.ipythonArgs = ["notebook"];

// Parse command arguments
process.argv.slice(2).forEach(function(e) {
    if (e.lastIndexOf("--KernelManager.kernel_cmd=", 0) === 0) {
        console.warn(util.format("Warning: Flag '%s' skipped", e));
    } else if (e.lastIndexOf("--ijs-debug", 0) === 0) {
        config.kernelArgs.push("--debug");
    } else if (e.lastIndexOf("--ijs-working-dir=", 0) === 0) {
        config.cwd = fs.realpathSync(e.slice(18));
    } else if (e.lastIndexOf("--ijs-help", 0) === 0) {
        console.warn(usage);
        process.exit(0);
    } else if (e.lastIndexOf("--ijs-", 0) === 0) {
        console.warn(util.format("Error: Unrecognised flag '%s'\n", e));
        console.warn(usage);
        process.exit(1);
    } else {
        config.ipythonArgs.push(e);
    }
});

config.cwd = config.cwd || process.cwd();

config.kernelArgs.push(config.cwd);
config.kernelArgs.push("{connection_file}");

config.ipythonArgs.push(util.format(
    "--KernelManager.kernel_cmd=['%s']",
    config.kernelArgs.join("', '")
));

// Determine IPython version and start the IPython notebook accordingly
exec("ipython --version && ipython locate", function(error, stdout, stderr) {
    var lines = stdout.toString().split("\n", 2);

    config.ipythonVersion = lines[0].split(".").map(function(e) {
        return parseInt(e);
    });

    if (isNaN(config.ipythonVersion[0]) || lines.length != 2) {
        console.warn("Error running `ipython --version && ipython locate`");
        process.exit(1);
    }

    config.ipythonConfigDir = lines[1];
    config.ipythonKernelsDir = path.join(config.ipythonConfigDir, "kernels");
    config.ijsKernelSpecDir = path.join(
        config.ipythonKernelsDir, "javascript"
    );
    config.ijsKernelSpecFile = path.join(
        config.ijsKernelSpecDir, "kernel.json"
    );

    var ipython;
    if (config.ipythonVersion[0] < 3) {
        // Start the IPython notebook
        ipython = spawn("ipython", config.ipythonArgs, {
            stdio: "inherit"
        });
    } else {
        // Create the spec for a Javascript kernel
        try {
            fs.mkdirSync(config.ipythonKernelsDir);
        } catch (e) {
            if (e.code != 'EEXIST') {
                throw e;
            }
        }

        try {
            fs.mkdirSync(config.ijsKernelSpecDir);
        } catch (e) {
            if (e.code != 'EEXIST') {
                throw e;
            }
        }

        var ijsSpec = {
            argv: config.kernelArgs,
            display_name: "Javascript (Node.js)",
            language: "javascript",
        };
        fs.writeFileSync(config.ijsKernelSpecFile, JSON.stringify(ijsSpec));

        // Start the IPython notebook with the default profile
        ipython = spawn("ipython", ["notebook"], {
            stdio: "inherit"
        });
    }

    // Relay SIGINT onto ipython
    var signal = "SIGINT";
    process.on(signal, function() {
        ipython.emit(signal);
    });
});
<|endoftext|>


<commit_before>/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global require, exports */

(function () {
    "use strict";

    var async = require("async");
    var process_runner = require("./process_runner.js");
    var config = require("./config.js");
    var utils = require("./utils.js");
    var fs = require("fs");
    var os = require("os");

    var restartUDHCPD = function (callback) {
        //service udhcpd restart 2>&1 >/dev/null
        process_runner.exec("service", ["udhcpd", "restart"], callback);
    };

    var restartAPD = function (callback) {
        //service hostapd restart 2>&1 >/dev/null
        process_runner.exec("service", ["hostapd", "restart"], callback);
    };

    var loadAPConfTemplate = function (callback) {
        fs.readFile(config.HOSTAPD_CONF_TEMPLATE_PATH, {encoding: "utf8"}, callback);
    };

    var createAPConf = function (ssid, template) {
        template.replace("{{SSID}}", ssid);
        return template;
    };

    var writeAPConf = function (data, callback) {
        fs.writeFile(config.HOSTAPD_CONF_PATH, data, callback);
    };

    //todo: we may want to cache the results of this, so we dont have to read a file
    //everytime this api is called. Might not be a big deal though, since there should only ever be
    //one person accessing the site
    var readRestrictedAccessList = function (callback) {
        fs.readFile(config.HOSTAPD_RESTRICTED_ADDRESSES_PATH,
                    {encoding: "utf8"},
            function (err, data) {
                var addresses = data.split(os.EOL);
                callback(null, addresses);
            }
            );
    };

    var saveRestrictedAccessList = function (addresses, callback) {
        var tmp = addresses.join(os.EOL);
        fs.writeFile(config.HOSTAPD_RESTRICTED_ADDRESSES_PATH, tmp, callback);
    };

    var updateRestrictedAccessList = function (addresses, callback) {
        //todo: confirm whether we need to restart service after updating access list
        async.series(
            [
                function (callback) {saveRestrictedAccessList(addresses, callback); },
                restartAPD
            ],
            function (err, out) {
                if (err) {
                    callback(err);
                    return;
                }
                callback(null, out);
            }
        );
    };

    var updateAPConf = function (ssid, callback) {

        var _generateAPConf = function (err, data, callback) {

            console.log("_generateAPConf");
            console.log("err:", err);
            console.log("data : ", data);
            var conf = createAPConf(ssid, data);
            callback(null, conf);
        };

        //todo: do we need to restart restartUDHCPD (it is slow)
        async.waterfall(
            [
                loadAPConfTemplate,
                _generateAPConf,
                writeAPConf,
                restartAPD,
                restartUDHCPD
            ],
            function (err, out) {
                if (err) {
                    callback(err);
                    return;
                }
                callback(null, out);
            }
        );
    };

    var updateAccessPoint = function (ssid, address, callback) {

        if (!utils.isValidMacAddress(address)) {
            callback("Invalid mac address");
            return;
        }

        if (!ssid || !ssid.length) {
            callback("Invalid SSID.");
            return;
        }

        //note, the arguments are not passed on the shell, so we don't have to worry
        //about injection (although the script being called does)
        //http://stackoverflow.com/questions/15168071/how-secure-is-using-execfile-for-bash-scripts
        async.series(
            [
                function (callback) {process_runner.exec("ifconfig", [config.ACCESS_POINT_INTERFACE, "down"], callback); },
                function (callback) {process_runner.exec("ifconfig", [config.ACCESS_POINT_INTERFACE, "hw", "ether", address], callback); },
                function (callback) {process_runner.exec("ifconfig", [config.ACCESS_POINT_INTERFACE, "up"], callback); },
                function (callback) {updateAPConf(ssid, callback); },
                restartAPD,
                restartUDHCPD
            ],
            function (err, out) {
                if (err) {
                    callback(err);
                    return;
                }
                callback(null, out);
            }
        );
    };

    exports.updateAccessPoint = updateAccessPoint;
}());<commit_msg>fixing some bugs<commit_after>/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global require, exports */

(function () {
    "use strict";

    var async = require("async");
    var process_runner = require("./process_runner.js");
    var config = require("./config.js");
    var utils = require("./utils.js");
    var fs = require("fs");
    var os = require("os");

    var restartUDHCPD = function (callback) {
        //service udhcpd restart 2>&1 >/dev/null
        process_runner.exec("service", ["udhcpd", "restart"], callback);
    };

    var restartAPD = function (callback) {
        //service hostapd restart 2>&1 >/dev/null
        process_runner.exec("service", ["hostapd", "restart"], callback);
    };

    var loadAPConfTemplate = function (callback) {
        fs.readFile(config.HOSTAPD_CONF_TEMPLATE_PATH, {encoding: "utf8"}, callback);
    };

    var createAPConf = function (ssid, template) {
        template.replace("{{SSID}}", ssid);
        return template;
    };

    var writeAPConf = function (data, callback) {
        fs.writeFile(config.HOSTAPD_CONF_PATH, data, callback);
    };

    //todo: we may want to cache the results of this, so we dont have to read a file
    //everytime this api is called. Might not be a big deal though, since there should only ever be
    //one person accessing the site
    var readRestrictedAccessList = function (callback) {
        fs.readFile(config.HOSTAPD_RESTRICTED_ADDRESSES_PATH,
                    {encoding: "utf8"},
            function (err, data) {
                var addresses = data.split(os.EOL);
                callback(null, addresses);
            }
            );
    };

    var saveRestrictedAccessList = function (addresses, callback) {
        var tmp = addresses.join(os.EOL);
        fs.writeFile(config.HOSTAPD_RESTRICTED_ADDRESSES_PATH, tmp, callback);
    };

    var updateRestrictedAccessList = function (addresses, callback) {
        //todo: confirm whether we need to restart service after updating access list
        async.series(
            [
                function (callback) {saveRestrictedAccessList(addresses, callback); },
                restartAPD
            ],
            function (err, out) {
                if (err) {
                    callback(err);
                    return;
                }
                callback(null, out);
            }
        );
    };

    var updateAPConf = function (ssid, callback) {

        var _generateAPConf = function (data, callback) {

            //console.log("_generateAPConf");
            //console.log("data : ", data);
            var conf = createAPConf(ssid, data);
            callback(null, conf);
        };

        //todo: do we need to restart restartUDHCPD (it is slow)
        async.waterfall(
            [
                loadAPConfTemplate,
                _generateAPConf,
                writeAPConf,
                restartAPD,
                restartUDHCPD
            ],
            function (err, out) {
                if (err) {
                    callback(err);
                    return;
                }
                callback(null, out);
            }
        );
    };

    var updateAccessPoint = function (ssid, address, callback) {

        if (!utils.isValidMacAddress(address)) {
            callback("Invalid mac address");
            return;
        }

        if (!ssid || !ssid.length) {
            callback("Invalid SSID.");
            return;
        }

        //note, the arguments are not passed on the shell, so we don't have to worry
        //about injection (although the script being called does)
        //http://stackoverflow.com/questions/15168071/how-secure-is-using-execfile-for-bash-scripts
        async.series(
            [
                function (callback) {process_runner.exec("ifconfig", [config.ACCESS_POINT_INTERFACE, "down"], callback); },
                function (callback) {process_runner.exec("ifconfig", [config.ACCESS_POINT_INTERFACE, "hw", "ether", address], callback); },
                function (callback) {process_runner.exec("ifconfig", [config.ACCESS_POINT_INTERFACE, "up"], callback); },
                function (callback) {updateAPConf(ssid, callback); },
                restartAPD,
                restartUDHCPD
            ],
            function (err, out) {
                if (err) {
                    callback(err);
                    return;
                }
                callback(null, out);
            }
        );
    };

    exports.updateAccessPoint = updateAccessPoint;
}());<|endoftext|>


<commit_before>#! /usr/bin/env node

'use strict';

var pkg = require('../package.json'),
    updateNotifier = require('update-notifier'),
    utils = require('../lib/utils');

var nodeVersion =  utils.getVersion(process.version.replace('v', '').split('.')),
    requiredNodeVersion = utils.getVersion(pkg.engines.node.replace('>=','').split('.'));

// check node version compatibility
if(nodeVersion.major < requiredNodeVersion.major){
    console.log();
    console.error(utils.log.chalk.red.bold('✗ '), utils.log.chalk.red.bold('NODE ' + process.version + ' was detected. Siteshooter requires node version ' + pkg.engines.node));
    console.log();
    process.exit(1);
}
else{

    // check for new version of Siteshooter
    updateNotifier({ pkg }).notify();
}

var siteshooter = require('../index'),
    args = [].slice.call(process.argv, 2);

var exitCode = 0,
    isDebug = args.indexOf('--debug') !== -1;

process.on('exit', function() {
    if (isDebug) {
        console.log('EXIT', arguments);
    }
});

return siteshooter.cli(args).then(function() {

    if (isDebug) {
        console.log('CLI promise complete');
    }

}).catch(function(error) {
    exitCode = 1;

    var reportError = Array.isArray(error) ? error.join('\n') : error;

    console.log('\n\n', utils.log.chalk.red.bold('✗ '), utils.log.chalk.red(reportError));

}).done(function(){

    utils.log.log('\n', utils.log.chalk.green.bold('✔'), utils.log.chalk.green.bold('Siteshooter tasks complete\n'));

    process.exit(exitCode);

});

<commit_msg>Move update notifier to the end of the process.<commit_after>#! /usr/bin/env node

'use strict';

var pkg = require('../package.json'),
    utils = require('../lib/utils');

var nodeVersion =  utils.getVersion(process.version.replace('v', '').split('.')),
    requiredNodeVersion = utils.getVersion(pkg.engines.node.replace('>=','').split('.'));

// check node version compatibility
if(nodeVersion.major < requiredNodeVersion.major){
    console.log();
    console.error(utils.log.chalk.red.bold('✗ '), utils.log.chalk.red.bold('NODE ' + process.version + ' was detected. Siteshooter requires node version ' + pkg.engines.node));
    console.log();
    process.exit(1);
}

var args = [].slice.call(process.argv, 2),
    exitCode = 0,
    siteshooter = require('../index'),
    updateNotifier = require('update-notifier');

var isDebug = args.indexOf('--debug') !== -1;

process.on('exit', function() {
    if (isDebug) {
        console.log('EXIT', arguments);
    }
});

return siteshooter.cli(args).then(function() {

    // check for new version of Siteshooter
    updateNotifier({pkg}).notify();

}).catch(function(error) {

    exitCode = 1;
    error = Array.isArray(error) ? error.join('\n') : error;

    console.error('\n\n', utils.log.chalk.red.bold('✗ '), utils.log.chalk.red(error));

}).done(function(){

    utils.log.log('\n', utils.log.chalk.green.bold('✔'), utils.log.chalk.green.bold('Siteshooter tasks complete\n'));

    process.exit(exitCode);

});

<|endoftext|>


<commit_before>/* global AutoForm, _, Template */

import noUiSlider from 'nouislider';
import 'nouislider/src/nouislider.css';
import 'nouislider/src/nouislider.pips.css';
import 'nouislider/src/nouislider.tooltips.css';

AutoForm.addInputType("noUiSlider", {
  template: "afNoUiSlider",
  valueOut: function(){
    var slider = this.find('.nouislider');
    var isDecimal = this.closest(".at-nouislider").data("decimal");

    if( this.attr("data-type") === "Object" ){
      var parser = (isDecimal)? parseFloat : parseInt;
      var first = parser.call(null, slider.val()[0]);
      var second = parser.call(null, slider.val()[1]);
      var value = {
        lower: first > second ? second : first,
        upper: first > second ? first : second
      };
      return value;
    }else{
      return slider.val();
    }
  }
});

Template.afNoUiSlider.helpers({
  atts: function () {
    var data = Template.currentData(); // get data reactively
    var atts = data.atts;
    atts["data-type"] = data.schemaType.name;
    if( atts["class"] ){
      atts["class"] += " at-nouislider";
    }else{
      atts["class"] = "at-nouislider";
    }

    atts.doLabels = ( atts.labelLeft || atts.labelRight );

    atts["data-decimal"] = data.decimal;

    return _.omit(atts, 'noUiSliderOptions', 'noUiSlider_pipsOptions');
  }
});

var calculateOptions = function(data){
  var schemaMinMax = _.pick(data, 'max', 'min');
  var autoformOptions = _.pick(data.atts || {}, 'max', 'min', 'step', 'start', 'range');
  var noUiSliderOptions = (data.atts || {}).noUiSliderOptions;

  var options = _.extend({}, schemaMinMax, autoformOptions, noUiSliderOptions);

  // Adjust data initialization based on schema type
  if( options.start === undefined ){
    if( data.schemaType.name === "Object" ){
      if( data.value && data.value.lower ){
        options.start = [
          data.value.lower,
          data.value.upper
        ];
      }else{
        options.start = [
          typeof data.min === "number" ? data.min : 0,
          typeof data.max === "number" ? data.max : 100
        ];
      }
      options.connect = true;
    }else{
      options.start = data.value || 0;
    }
  }

  if( options.range === undefined ){
    options.range = {
      min: typeof options.min === "number" ? options.min : 0,
      max: typeof options.max === "number" ? options.max : 100
    };
  }

  delete options.min;
  delete options.max;

  // default step to 1 if not otherwise defined
  if( options.step === undefined ){
    options.step = 1;
  }

  return options;
};

Template.afNoUiSlider.rendered = function () {
  var template = this;
  var $s = template.$('.nouislider');

  var setup = function(c){
    var data = Template.currentData(); // get data reactively
    var options = calculateOptions( data );
    noUiSlider.create($s[0], options);

    if (c.firstRun) {
      $s.on('slide', function(){
        // This is a trick to fool some logic in AutoForm that makes
        // sure values have actually changed on whichever element
        // emits a change event. Eventually AutoForm will give
        // input types the control of indicating exactly when
        // their value changes rather than relying on the change event
        $s.parent()[0].value = JSON.stringify($s.val());
        $s.parent().change();
        $s.data('changed','true');
      });
    }

    if( data.atts.noUiSlider_pipsOptions ){
      $s[0].noUiSlider.pips(
        data.atts.noUiSlider_pipsOptions
      );
    }
  };

  template.autorun( setup );
};
<commit_msg>Updated value method for noUiSlider<commit_after>/* global AutoForm, _, Template */

import noUiSlider from 'nouislider';
import 'nouislider/src/nouislider.css';
import 'nouislider/src/nouislider.pips.css';
import 'nouislider/src/nouislider.tooltips.css';

AutoForm.addInputType("noUiSlider", {
  template: "afNoUiSlider",
  valueOut: function(){
    var slider = this.find('.nouislider')[0];
    var isDecimal = this.closest(".at-nouislider").data("decimal");

    if( this.attr("data-type") === "Object" ){
      var parser = (isDecimal)? parseFloat : parseInt;
      var first = parser.call(null, slider.noUiSlider.get()[0]);
      var second = parser.call(null, slider.noUiSlider.get()[1]);
      var value = {
        lower: first > second ? second : first,
        upper: first > second ? first : second
      };
      return value;
    }else{
      return slider.noUiSlider.get();
    }
  }
});

Template.afNoUiSlider.helpers({
  atts: function () {
    var data = Template.currentData(); // get data reactively
    var atts = data.atts;
    atts["data-type"] = data.schemaType.name;
    if( atts["class"] ){
      atts["class"] += " at-nouislider";
    }else{
      atts["class"] = "at-nouislider";
    }

    atts.doLabels = ( atts.labelLeft || atts.labelRight );

    atts["data-decimal"] = data.decimal;

    return _.omit(atts, 'noUiSliderOptions', 'noUiSlider_pipsOptions');
  }
});

var calculateOptions = function(data){
  var schemaMinMax = _.pick(data, 'max', 'min');
  var autoformOptions = _.pick(data.atts || {}, 'max', 'min', 'step', 'start', 'range');
  var noUiSliderOptions = (data.atts || {}).noUiSliderOptions;

  var options = _.extend({}, schemaMinMax, autoformOptions, noUiSliderOptions);

  // Adjust data initialization based on schema type
  if( options.start === undefined ){
    if( data.schemaType.name === "Object" ){
      if( data.value && data.value.lower ){
        options.start = [
          data.value.lower,
          data.value.upper
        ];
      }else{
        options.start = [
          typeof data.min === "number" ? data.min : 0,
          typeof data.max === "number" ? data.max : 100
        ];
      }
      options.connect = true;
    }else{
      options.start = data.value || 0;
    }
  }

  if( options.range === undefined ){
    options.range = {
      min: typeof options.min === "number" ? options.min : 0,
      max: typeof options.max === "number" ? options.max : 100
    };
  }

  delete options.min;
  delete options.max;

  // default step to 1 if not otherwise defined
  if( options.step === undefined ){
    options.step = 1;
  }

  return options;
};

Template.afNoUiSlider.rendered = function () {
  var template = this;
  var $s = template.$('.nouislider');

  var setup = function(c){
    var data = Template.currentData(); // get data reactively
    var options = calculateOptions( data );
    noUiSlider.create($s[0], options);

    if (c.firstRun) {
      $s.on('slide', function(){
        // This is a trick to fool some logic in AutoForm that makes
        // sure values have actually changed on whichever element
        // emits a change event. Eventually AutoForm will give
        // input types the control of indicating exactly when
        // their value changes rather than relying on the change event
        $s.parent()[0].value = JSON.stringify($s[0].noUiSlider.get());
        $s.parent().change();
        $s.data('changed','true');
      });
    }

    if( data.atts.noUiSlider_pipsOptions ){
      $s[0].noUiSlider.pips(
          data.atts.noUiSlider_pipsOptions
      );
    }
  };

  template.autorun( setup );
};
<|endoftext|>


<commit_before>const moment = require('moment');
const _ = require('lodash');

const api = require('../api');

/**
 * Generates the iso-weekday abbreviation for a given number, e.g. for the Schul-Cloud Calendar-Service
 * @param weekdayNum {number}
 * @returns {string} - abbreviation of weekday
 */
const getIsoWeekdayForNumber = (weekdayNum) => {
	const weekdayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'];
	return weekdayNames[weekdayNum];
};

/**
 * Generates the fullcalendar.js weekday index of a given iso weekday label
 * @param weekday {string}
 * @returns {number} - number of weekday
 */
const getNumberForFullCalendarWeekday = (weekday) => {
	const weekdayNames = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
	return weekdayNames.indexOf(weekday);
};

/**
 *  Generates the german weekday label for a given number
 * @param weekdayNum {number}
 * @returns {string} - abbreviation of weekday
 */
const getWeekdayForNumber = (weekdayNum) => {
	const weekdayNames = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'];
	return weekdayNames[weekdayNum];
};

/**
 * Generates the index of a given german weekday label
 * @param weekday {string}
 * @returns {number} - number of weekday
 */
const getNumberForWeekday = (weekday) => {
	const weekdayNames = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'];
	return weekdayNames.indexOf(weekday);
};

/**
 * Creates recurring (weekly) events for a @param recurringEvent definition
 * @param recurringEvent {Event} - the recurring (weekly) event
 * @return recurringEvents [] - new set of events
 */
const createRecurringEvents = (recurringEvent) => {
	const recurringEvents = [];
	let { start, end } = recurringEvent;
	const until = new Date(recurringEvent.included[0].attributes.until).getTime();
	const oneDayIndicator = 24 * 60 * 60 * 1000;
	const oneWeekIndicator = 7 * oneDayIndicator;

	// find first weekday, if the start-event is not a real weekly event itself, because it's just a period of time
	for (let i = 0; start + i * oneDayIndicator <= end + oneWeekIndicator; i = +1) {
		const newStartDate = start + i * oneDayIndicator;
		const newEndDate = end + i * oneDayIndicator;

		// check if it is the given weekday, if so set first date of recurring events
		if (moment(newStartDate).day() == getNumberForFullCalendarWeekday(recurringEvent.included[0].attributes.wkst)) {
			start = newStartDate;
			end = newEndDate;
			break;
		}
	}

	// loop over all new weekdays from startDate to untilDate
	for (let i = 0; start + i * oneWeekIndicator <= until; i = +1) {
		const newStartDate = start + i * oneWeekIndicator;
		const newEndDate = end + i * oneWeekIndicator;

		recurringEvents.push({
			title: recurringEvent.summary,
			summary: recurringEvent.summary,
			location: recurringEvent.location,
			description: recurringEvent.description,
			url: recurringEvent.url,
			color: recurringEvent.color,
			start: newStartDate,
			end: newEndDate,
		});

	}

	return recurringEvents;
};

/**
 * handle recurring events for calendar
 * @param event {Event} - a event which could contain a recurring value
 * @returns events [] - new set of events
 */
const mapRecurringEvent = (event) => {
	if (event.included && event.included[0].attributes.freq === 'WEEKLY') {
		return createRecurringEvents(event);
	}

	return [event];
};

/**
 * maps properties of a event to fit calendar, e.g. url and color
 * If any error is accoured then return course or team with default color.
 * @param event
 */
const mapEventProps = (event, req) => {
	if (event['x-sc-courseId']) {
		return api(req).get(`/courses/${event['x-sc-courseId']}`).then((course) => {
			event.url = event['x-sc-courseTimeId'] ? `/courses/${course._id}` : '';
			event.color = course.color;
			return event;
		}).catch(() => {
			event.url = '';
			event.color = '#ff0000';
			return event;
		});
	}

	if (event['x-sc-teamId']) {
		/*
		// bad fix for ,<id> error in teams, TODO: fix dashboard
		let id = event['x-sc-teamId'];
		if (id.substring(0, 1) === ',') {
			id = id.substr(1);
		} */
		return api(req).get(`/teams/${event['x-sc-teamId']}`).then((team) => {
			event.url = '';
			event.color = team.color;
			return event;
		}).catch(() => {
			event.url = '';
			event.color = '#ff0000';
			return event;
		});
	}

	return event;
};

/**
 * retrieves the next date for given weekly courseTimes
 * @param courseTimes - the times of a course
 * @return {String} - a formatted date string
 */
const getNextEventForCourseTimes = (courseTimes) => {
	if ((courseTimes || []).length <= 0) return;
	const nextDates = _.map(courseTimes, (ct) => {
		let weekDayIdentifier = ct.weekday + 1; // moment starts on sunday

		// if current week's weekday is over, take the one next week
		if (moment().day() > weekDayIdentifier) weekDayIdentifier += 7;

		// return nothing if date has already passed
		const date = moment().day(weekDayIdentifier).hours(0).minutes(0)
			.seconds(0)
			.milliseconds(ct.startTime);
		if (date.isBefore()) return;

		// has to store index, because .indexOf with moment arrays does not work
		// eslint-disable-next-line consistent-return
		return date;
	});

	// find nearest day from now
	const minDate = _.min(nextDates);
	// eslint-disable-next-line consistent-return
	return moment(minDate).format('DD.MM.YYYY HH:mm');
};

function pad(number) {
	if (number < 10) {
		return `0${number}`;
	}
	return number;
}

if (!Date.prototype.toLocalISOString) {
	(function convertDate() {
		// eslint-disable-next-line no-extend-native
		Date.prototype.toLocalISOString = function convert() {
			return `${this.getFullYear()
			}-${pad(this.getMonth() + 1)
			}-${pad(this.getDate())
			}T${pad(this.getHours())
			}:${pad(this.getMinutes())
			}:${pad(this.getSeconds())
			}.${(this.getMilliseconds() / 1000).toFixed(3).slice(2, 5)
			}Z`;
		};
	}());
}

module.exports = {
	getIsoWeekdayForNumber,
	getWeekdayForNumber,
	getNumberForWeekday,
	getNumberForFullCalendarWeekday,
	createRecurringEvents,
	mapRecurringEvent,
	mapEventProps,
	getNextEventForCourseTimes,
};
<commit_msg>bad fix for wrong team ids<commit_after>const moment = require('moment');
const _ = require('lodash');

const api = require('../api');

/**
 * Generates the iso-weekday abbreviation for a given number, e.g. for the Schul-Cloud Calendar-Service
 * @param weekdayNum {number}
 * @returns {string} - abbreviation of weekday
 */
const getIsoWeekdayForNumber = (weekdayNum) => {
	const weekdayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'];
	return weekdayNames[weekdayNum];
};

/**
 * Generates the fullcalendar.js weekday index of a given iso weekday label
 * @param weekday {string}
 * @returns {number} - number of weekday
 */
const getNumberForFullCalendarWeekday = (weekday) => {
	const weekdayNames = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
	return weekdayNames.indexOf(weekday);
};

/**
 *  Generates the german weekday label for a given number
 * @param weekdayNum {number}
 * @returns {string} - abbreviation of weekday
 */
const getWeekdayForNumber = (weekdayNum) => {
	const weekdayNames = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'];
	return weekdayNames[weekdayNum];
};

/**
 * Generates the index of a given german weekday label
 * @param weekday {string}
 * @returns {number} - number of weekday
 */
const getNumberForWeekday = (weekday) => {
	const weekdayNames = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'];
	return weekdayNames.indexOf(weekday);
};

/**
 * Creates recurring (weekly) events for a @param recurringEvent definition
 * @param recurringEvent {Event} - the recurring (weekly) event
 * @return recurringEvents [] - new set of events
 */
const createRecurringEvents = (recurringEvent) => {
	const recurringEvents = [];
	let { start, end } = recurringEvent;
	const until = new Date(recurringEvent.included[0].attributes.until).getTime();
	const oneDayIndicator = 24 * 60 * 60 * 1000;
	const oneWeekIndicator = 7 * oneDayIndicator;

	// find first weekday, if the start-event is not a real weekly event itself, because it's just a period of time
	for (let i = 0; start + i * oneDayIndicator <= end + oneWeekIndicator; i = +1) {
		const newStartDate = start + i * oneDayIndicator;
		const newEndDate = end + i * oneDayIndicator;

		// check if it is the given weekday, if so set first date of recurring events
		if (moment(newStartDate).day() == getNumberForFullCalendarWeekday(recurringEvent.included[0].attributes.wkst)) {
			start = newStartDate;
			end = newEndDate;
			break;
		}
	}

	// loop over all new weekdays from startDate to untilDate
	for (let i = 0; start + i * oneWeekIndicator <= until; i = +1) {
		const newStartDate = start + i * oneWeekIndicator;
		const newEndDate = end + i * oneWeekIndicator;

		recurringEvents.push({
			title: recurringEvent.summary,
			summary: recurringEvent.summary,
			location: recurringEvent.location,
			description: recurringEvent.description,
			url: recurringEvent.url,
			color: recurringEvent.color,
			start: newStartDate,
			end: newEndDate,
		});

	}

	return recurringEvents;
};

/**
 * handle recurring events for calendar
 * @param event {Event} - a event which could contain a recurring value
 * @returns events [] - new set of events
 */
const mapRecurringEvent = (event) => {
	if (event.included && event.included[0].attributes.freq === 'WEEKLY') {
		return createRecurringEvents(event);
	}

	return [event];
};

/**
 * maps properties of a event to fit calendar, e.g. url and color
 * If any error is accoured then return course or team with default color.
 * @param event
 */
const mapEventProps = (event, req) => {
	if (event['x-sc-courseId']) {
		return api(req).get(`/courses/${event['x-sc-courseId']}`).then((course) => {
			event.url = event['x-sc-courseTimeId'] ? `/courses/${course._id}` : '';
			event.color = course.color;
			return event;
		}).catch(() => {
			event.url = '';
			event.color = '#ff0000';
			return event;
		});
	}

	if (event['x-sc-teamId']) {
		// bad fix for ,<id> error in teams, TODO: fix dashboard
		let id = event['x-sc-teamId'];
		if (id.substring(0, 1) === ',') {
			id = id.substr(1);
		}
		return api(req).get(`/teams/${id}`).then((team) => {
			event.url = '';
			event.color = team.color;
			return event;
		}).catch(() => {
			event.url = '';
			event.color = '#ff0000';
			return event;
		});
	}

	return event;
};

/**
 * retrieves the next date for given weekly courseTimes
 * @param courseTimes - the times of a course
 * @return {String} - a formatted date string
 */
const getNextEventForCourseTimes = (courseTimes) => {
	if ((courseTimes || []).length <= 0) return;
	const nextDates = _.map(courseTimes, (ct) => {
		let weekDayIdentifier = ct.weekday + 1; // moment starts on sunday

		// if current week's weekday is over, take the one next week
		if (moment().day() > weekDayIdentifier) weekDayIdentifier += 7;

		// return nothing if date has already passed
		const date = moment().day(weekDayIdentifier).hours(0).minutes(0)
			.seconds(0)
			.milliseconds(ct.startTime);
		if (date.isBefore()) return;

		// has to store index, because .indexOf with moment arrays does not work
		// eslint-disable-next-line consistent-return
		return date;
	});

	// find nearest day from now
	const minDate = _.min(nextDates);
	// eslint-disable-next-line consistent-return
	return moment(minDate).format('DD.MM.YYYY HH:mm');
};

function pad(number) {
	if (number < 10) {
		return `0${number}`;
	}
	return number;
}

if (!Date.prototype.toLocalISOString) {
	(function convertDate() {
		// eslint-disable-next-line no-extend-native
		Date.prototype.toLocalISOString = function convert() {
			return `${this.getFullYear()
			}-${pad(this.getMonth() + 1)
			}-${pad(this.getDate())
			}T${pad(this.getHours())
			}:${pad(this.getMinutes())
			}:${pad(this.getSeconds())
			}.${(this.getMilliseconds() / 1000).toFixed(3).slice(2, 5)
			}Z`;
		};
	}());
}

module.exports = {
	getIsoWeekdayForNumber,
	getWeekdayForNumber,
	getNumberForWeekday,
	getNumberForFullCalendarWeekday,
	createRecurringEvents,
	mapRecurringEvent,
	mapEventProps,
	getNextEventForCourseTimes,
};
<|endoftext|>


<commit_before>import {TextSelection, NodeSelection} from "prosemirror-state"

import browser from "./browser"
import {selectionCollapsed} from "./dom"

// Track the state of the current editor selection. Keeps the editor
// selection in sync with the DOM selection by polling for changes,
// as there is no DOM event for DOM selection changes.
export class SelectionReader {
  constructor(view) {
    this.view = view

    // Track the state of the DOM selection.
    this.lastAnchorNode = this.lastHeadNode = this.lastAnchorOffset = this.lastHeadOffset = null
    this.lastSelection = view.state.selection
    this.ignoreUpdates = false
    this.poller = poller(this)

    view.dom.addEventListener("focus", () => this.poller.start(hasFocusAndSelection(this.view)))
    view.dom.addEventListener("blur", () => this.poller.stop())

    if (!view.editable) this.poller.start(false)
  }

  destroy() { this.poller.stop() }

  poll(origin) { this.poller.poll(origin) }

  editableChanged() {
    if (!this.view.editable) this.poller.start()
    else if (!hasFocusAndSelection(this.view)) this.poller.stop()
  }

  // : () → bool
  // Whether the DOM selection has changed from the last known state.
  domChanged() {
    let sel = this.view.root.getSelection()
    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
      sel.focusNode != this.lastHeadNode || sel.focusOffset != this.lastHeadOffset
  }

  // Store the current state of the DOM selection.
  storeDOMState(selection) {
    let sel = this.view.root.getSelection()
    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
    this.lastHeadNode = sel.focusNode; this.lastHeadOffset = sel.focusOffset
    this.lastSelection = selection
  }

  clearDOMState() {
    this.lastAnchorNode = this.lastSelection = null
  }

  // : (?string) → bool
  // When the DOM selection changes in a notable manner, modify the
  // current selection state to match.
  readFromDOM(origin) {
    if (this.ignoreUpdates || !this.domChanged() || !hasFocusAndSelection(this.view)) return
    if (!this.view.inDOMChange) this.view.domObserver.flush()
    if (this.view.inDOMChange) return

    let domSel = this.view.root.getSelection(), doc = this.view.state.doc
    let nearestDesc = this.view.docView.nearestDesc(domSel.focusNode), inWidget = nearestDesc.size == 0
    let head = this.view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset)
    let $head = doc.resolve(head), $anchor, selection
    if (selectionCollapsed(domSel)) {
      $anchor = $head
      while (nearestDesc && !nearestDesc.node) nearestDesc = nearestDesc.parent
      if (nearestDesc && nearestDesc.node.isAtom && NodeSelection.isSelectable(nearestDesc.node) && nearestDesc.parent) {
        let pos = nearestDesc.posBefore
        selection = new NodeSelection(head == pos ? $head : doc.resolve(pos))
      }
    } else {
      $anchor = doc.resolve(this.view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset))
    }

    if (!selection) {
      let bias = origin == "pointer" || (this.view.state.selection.head < $head.pos && !inWidget) ? 1 : -1
      selection = selectionBetween(this.view, $anchor, $head, bias)
    }
    let preserve = !inWidget && !this.view.cursorWrapper && head == selection.head && $anchor.pos == selection.anchor
    if (preserve) this.storeDOMState(selection)
    if (!this.view.state.selection.eq(selection)) {
      let tr = this.view.state.tr.setSelection(selection)
      if (origin == "pointer") tr.setMeta("pointer", true)
      this.view.dispatch(tr)
    } else if (!preserve) {
      selectionToDOM(this.view)
    }
  }
}

// There's two polling models. On browsers that support the
// selectionchange event (everything except Firefox < 52, basically), we
// register a listener for that whenever the editor is focused.
class SelectionChangePoller {
  constructor(reader) {
    this.listening = false
    this.curOrigin = null
    this.originTime = 0
    this.reader = reader

    this.readFunc = () => reader.readFromDOM(this.originTime > Date.now() - 50 ? this.curOrigin : null)
  }

  poll(origin) {
    this.curOrigin = origin
    this.originTime = Date.now()
  }

  start(andRead) {
    if (!this.listening) {
      let doc = this.reader.view.dom.ownerDocument
      doc.addEventListener("selectionchange", this.readFunc)
      this.listening = true
      if (andRead) this.readFunc()
    }
  }

  stop() {
    if (this.listening) {
      let doc = this.reader.view.dom.ownerDocument
      doc.removeEventListener("selectionchange", this.readFunc)
      this.listening = false
    }
  }
}

// On Browsers that don't support the selectionchange event,
// we use timeout-based polling.
class TimeoutPoller {
  constructor(reader) {
    // The timeout ID for the poller when active.
    this.polling = null
    this.reader = reader
    this.pollFunc = this.doPoll.bind(this, null)
  }

  doPoll(origin) {
    let view = this.reader.view
    if (view.focused || !view.editable) {
      this.reader.readFromDOM(origin)
      this.polling = setTimeout(this.pollFunc, 100)
    } else {
      this.polling = null
    }
  }

  poll(origin) {
    clearTimeout(this.polling)
    this.polling = setTimeout(origin ? this.doPoll.bind(this, origin) : this.pollFunc, 0)
  }

  start() {
    if (this.polling == null) this.poll()
  }

  stop() {
    clearTimeout(this.polling)
    this.polling = null
  }
}

function poller(reader) {
  return new ("onselectionchange" in document ? SelectionChangePoller : TimeoutPoller)(reader)
}

export function selectionToDOM(view, takeFocus) {
  let sel = view.state.selection
  syncNodeSelection(view, sel)

  if (!view.hasFocus()) {
    if (!takeFocus) return
    // See https://bugzilla.mozilla.org/show_bug.cgi?id=921444
    if (browser.gecko && view.editable) {
      view.selectionReader.ignoreUpdates = true
      view.dom.focus()
      view.selectionReader.ignoreUpdates = false
    }
  }

  let reader = view.selectionReader
  if (reader.lastSelection && reader.lastSelection.eq(sel) && !reader.domChanged()) return

  reader.ignoreUpdates = true

  if (view.cursorWrapper) {
    selectCursorWrapper(view)
  } else {
    let {anchor, head} = sel, resetEditableFrom, resetEditableTo
    if (brokenSelectBetweenUneditable && !(sel instanceof TextSelection)) {
      if (!sel.$from.parent.inlineContent)
        resetEditableFrom = temporarilyEditableNear(view, sel.from)
      if (!sel.empty && !sel.$from.parent.inlineContent)
        resetEditableTo = temporarilyEditableNear(view, sel.to)
    }
    view.docView.setSelection(anchor, head, view.root)
    if (brokenSelectBetweenUneditable) {
      if (resetEditableFrom) resetEditableFrom.contentEditable = "false"
      if (resetEditableTo) resetEditableTo.contentEditable = "false"
    }
    if (sel.visible) {
      view.dom.classList.remove("ProseMirror-hideselection")
    } else if (anchor != head) {
      view.dom.classList.add("ProseMirror-hideselection")
      if ("onselectionchange" in document) removeClassOnSelectionChange(view)
    }
  }

  reader.storeDOMState(sel)
  reader.ignoreUpdates = false
}

// Kludge to work around Webkit not allowing a selection to start/end
// between non-editable block nodes. We briefly make something
// editable, set the selection, then set it uneditable again.

const brokenSelectBetweenUneditable = browser.safari || browser.chrome && browser.chrome_version < 63

function temporarilyEditableNear(view, pos) {
  let {node, offset} = view.docView.domFromPos(pos)
  let after = offset < node.childNodes.length ? node.childNodes[offset] : null
  let before = offset ? node.childNodes[offset - 1] : null
  if ((!after || after.contentEditable == "false") && (!before || before.contentEditable == "false")) {
    if (after) {
      after.contentEditable = "true"
      return after
    } else if (before) {
      before.contentEditable = "true"
      return before
    }
  }
}

function removeClassOnSelectionChange(view) {
  let doc = view.dom.ownerDocument
  doc.removeEventListener("selectionchange", view.hideSelectionGuard)
  let domSel = view.root.getSelection()
  let node = domSel.anchorNode, offset = domSel.anchorOffset
  doc.addEventListener("selectionchange", view.hideSelectionGuard = () => {
    if (domSel.anchorNode != node || domSel.anchorOffset != offset) {
      doc.removeEventListener("selectionchange", view.hideSelectionGuard)
      view.dom.classList.remove("ProseMirror-hideselection")
    }
  })
}

function selectCursorWrapper(view) {
  let domSel = view.root.getSelection(), range = document.createRange()
  let node = view.cursorWrapper.type.widget
  range.setEnd(node, node.childNodes.length)
  range.collapse(false)
  domSel.removeAllRanges()
  domSel.addRange(range)
  // Kludge to kill 'control selection' in IE11 when selecting an
  // invisible cursor wrapper, since that would result in those weird
  // resize handles and a selection that considers the absolutely
  // positioned wrapper, rather than the root editable node, the
  // focused element.
  if (!view.state.selection.visible && browser.ie && browser.ie_version <= 11) {
    node.disabled = true
    node.disabled = false
  }
}

function syncNodeSelection(view, sel) {
  if (sel instanceof NodeSelection) {
    let desc = view.docView.descAt(sel.from)
    if (desc != view.lastSelectedViewDesc) {
      clearNodeSelection(view)
      if (desc) desc.selectNode()
      view.lastSelectedViewDesc = desc
    }
  } else {
    clearNodeSelection(view)
  }
}

// Clear all DOM statefulness of the last node selection.
function clearNodeSelection(view) {
  if (view.lastSelectedViewDesc) {
    view.lastSelectedViewDesc.deselectNode()
    view.lastSelectedViewDesc = null
  }
}

export function selectionBetween(view, $anchor, $head, bias) {
  return view.someProp("createSelectionBetween", f => f(view, $anchor, $head))
    || TextSelection.between($anchor, $head, bias)
}

function hasFocusAndSelection(view) {
  if (view.editable && view.root.activeElement != view.dom) return false
  let sel = view.root.getSelection()
  if (!sel.anchorNode) return false
  try {
    // Firefox will raise 'permission denied' errors when accessing
    // properties of `sel.anchorNode` when it's in a generated CSS
    // element.
    return view.dom.contains(sel.anchorNode.nodeType == 3 ? sel.anchorNode.parentNode : sel.anchorNode)
  } catch(_) {
    return false
  }
}
<commit_msg>Fix selection updating for non-editable editor<commit_after>import {TextSelection, NodeSelection} from "prosemirror-state"

import browser from "./browser"
import {selectionCollapsed} from "./dom"

// Track the state of the current editor selection. Keeps the editor
// selection in sync with the DOM selection by polling for changes,
// as there is no DOM event for DOM selection changes.
export class SelectionReader {
  constructor(view) {
    this.view = view

    // Track the state of the DOM selection.
    this.lastAnchorNode = this.lastHeadNode = this.lastAnchorOffset = this.lastHeadOffset = null
    this.lastSelection = view.state.selection
    this.ignoreUpdates = false
    this.poller = poller(this)

    view.dom.addEventListener("focus", () => this.poller.start(hasFocusAndSelection(this.view)))
    view.dom.addEventListener("blur", () => this.poller.stop())

    if (!view.editable) this.poller.start(false)
  }

  destroy() { this.poller.stop() }

  poll(origin) { this.poller.poll(origin) }

  editableChanged() {
    if (!this.view.editable) this.poller.start()
    else if (!hasFocusAndSelection(this.view)) this.poller.stop()
  }

  // : () → bool
  // Whether the DOM selection has changed from the last known state.
  domChanged() {
    let sel = this.view.root.getSelection()
    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
      sel.focusNode != this.lastHeadNode || sel.focusOffset != this.lastHeadOffset
  }

  // Store the current state of the DOM selection.
  storeDOMState(selection) {
    let sel = this.view.root.getSelection()
    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
    this.lastHeadNode = sel.focusNode; this.lastHeadOffset = sel.focusOffset
    this.lastSelection = selection
  }

  clearDOMState() {
    this.lastAnchorNode = this.lastSelection = null
  }

  // : (?string) → bool
  // When the DOM selection changes in a notable manner, modify the
  // current selection state to match.
  readFromDOM(origin) {
    if (this.ignoreUpdates || !this.domChanged() || !hasFocusAndSelection(this.view)) return
    if (!this.view.inDOMChange) this.view.domObserver.flush()
    if (this.view.inDOMChange) return

    let domSel = this.view.root.getSelection(), doc = this.view.state.doc
    let nearestDesc = this.view.docView.nearestDesc(domSel.focusNode), inWidget = nearestDesc.size == 0
    let head = this.view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset)
    let $head = doc.resolve(head), $anchor, selection
    if (selectionCollapsed(domSel)) {
      $anchor = $head
      while (nearestDesc && !nearestDesc.node) nearestDesc = nearestDesc.parent
      if (nearestDesc && nearestDesc.node.isAtom && NodeSelection.isSelectable(nearestDesc.node) && nearestDesc.parent) {
        let pos = nearestDesc.posBefore
        selection = new NodeSelection(head == pos ? $head : doc.resolve(pos))
      }
    } else {
      $anchor = doc.resolve(this.view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset))
    }

    if (!selection) {
      let bias = origin == "pointer" || (this.view.state.selection.head < $head.pos && !inWidget) ? 1 : -1
      selection = selectionBetween(this.view, $anchor, $head, bias)
    }
    let preserve = !inWidget && !this.view.cursorWrapper && head == selection.head && $anchor.pos == selection.anchor
    if (preserve) this.storeDOMState(selection)
    if (!this.view.state.selection.eq(selection)) {
      let tr = this.view.state.tr.setSelection(selection)
      if (origin == "pointer") tr.setMeta("pointer", true)
      this.view.dispatch(tr)
    } else if (!preserve) {
      selectionToDOM(this.view)
    }
  }
}

// There's two polling models. On browsers that support the
// selectionchange event (everything except Firefox < 52, basically), we
// register a listener for that whenever the editor is focused.
class SelectionChangePoller {
  constructor(reader) {
    this.listening = false
    this.curOrigin = null
    this.originTime = 0
    this.reader = reader

    this.readFunc = () => reader.readFromDOM(this.originTime > Date.now() - 50 ? this.curOrigin : null)
  }

  poll(origin) {
    this.curOrigin = origin
    this.originTime = Date.now()
  }

  start(andRead) {
    if (!this.listening) {
      let doc = this.reader.view.dom.ownerDocument
      doc.addEventListener("selectionchange", this.readFunc)
      this.listening = true
      if (andRead) this.readFunc()
    }
  }

  stop() {
    if (this.listening) {
      let doc = this.reader.view.dom.ownerDocument
      doc.removeEventListener("selectionchange", this.readFunc)
      this.listening = false
    }
  }
}

// On Browsers that don't support the selectionchange event,
// we use timeout-based polling.
class TimeoutPoller {
  constructor(reader) {
    // The timeout ID for the poller when active.
    this.polling = null
    this.reader = reader
    this.pollFunc = this.doPoll.bind(this, null)
  }

  doPoll(origin) {
    let view = this.reader.view
    if (view.focused || !view.editable) {
      this.reader.readFromDOM(origin)
      this.polling = setTimeout(this.pollFunc, 100)
    } else {
      this.polling = null
    }
  }

  poll(origin) {
    clearTimeout(this.polling)
    this.polling = setTimeout(origin ? this.doPoll.bind(this, origin) : this.pollFunc, 0)
  }

  start() {
    if (this.polling == null) this.poll()
  }

  stop() {
    clearTimeout(this.polling)
    this.polling = null
  }
}

function poller(reader) {
  return new ("onselectionchange" in document ? SelectionChangePoller : TimeoutPoller)(reader)
}

export function selectionToDOM(view, takeFocus) {
  let sel = view.state.selection
  syncNodeSelection(view, sel)

  if (view.editable && !view.hasFocus()) {
    if (!takeFocus) return
    // See https://bugzilla.mozilla.org/show_bug.cgi?id=921444
    if (browser.gecko && view.editable) {
      view.selectionReader.ignoreUpdates = true
      view.dom.focus()
      view.selectionReader.ignoreUpdates = false
    }
  } else if (!view.editable && !hasSelection(view) && !takeFocus) {
    return
  }

  let reader = view.selectionReader
  if (reader.lastSelection && reader.lastSelection.eq(sel) && !reader.domChanged()) return

  reader.ignoreUpdates = true

  if (view.cursorWrapper) {
    selectCursorWrapper(view)
  } else {
    let {anchor, head} = sel, resetEditableFrom, resetEditableTo
    if (brokenSelectBetweenUneditable && !(sel instanceof TextSelection)) {
      if (!sel.$from.parent.inlineContent)
        resetEditableFrom = temporarilyEditableNear(view, sel.from)
      if (!sel.empty && !sel.$from.parent.inlineContent)
        resetEditableTo = temporarilyEditableNear(view, sel.to)
    }
    view.docView.setSelection(anchor, head, view.root)
    if (brokenSelectBetweenUneditable) {
      if (resetEditableFrom) resetEditableFrom.contentEditable = "false"
      if (resetEditableTo) resetEditableTo.contentEditable = "false"
    }
    if (sel.visible) {
      view.dom.classList.remove("ProseMirror-hideselection")
    } else if (anchor != head) {
      view.dom.classList.add("ProseMirror-hideselection")
      if ("onselectionchange" in document) removeClassOnSelectionChange(view)
    }
  }

  reader.storeDOMState(sel)
  reader.ignoreUpdates = false
}

// Kludge to work around Webkit not allowing a selection to start/end
// between non-editable block nodes. We briefly make something
// editable, set the selection, then set it uneditable again.

const brokenSelectBetweenUneditable = browser.safari || browser.chrome && browser.chrome_version < 63

function temporarilyEditableNear(view, pos) {
  let {node, offset} = view.docView.domFromPos(pos)
  let after = offset < node.childNodes.length ? node.childNodes[offset] : null
  let before = offset ? node.childNodes[offset - 1] : null
  if ((!after || after.contentEditable == "false") && (!before || before.contentEditable == "false")) {
    if (after) {
      after.contentEditable = "true"
      return after
    } else if (before) {
      before.contentEditable = "true"
      return before
    }
  }
}

function removeClassOnSelectionChange(view) {
  let doc = view.dom.ownerDocument
  doc.removeEventListener("selectionchange", view.hideSelectionGuard)
  let domSel = view.root.getSelection()
  let node = domSel.anchorNode, offset = domSel.anchorOffset
  doc.addEventListener("selectionchange", view.hideSelectionGuard = () => {
    if (domSel.anchorNode != node || domSel.anchorOffset != offset) {
      doc.removeEventListener("selectionchange", view.hideSelectionGuard)
      view.dom.classList.remove("ProseMirror-hideselection")
    }
  })
}

function selectCursorWrapper(view) {
  let domSel = view.root.getSelection(), range = document.createRange()
  let node = view.cursorWrapper.type.widget
  range.setEnd(node, node.childNodes.length)
  range.collapse(false)
  domSel.removeAllRanges()
  domSel.addRange(range)
  // Kludge to kill 'control selection' in IE11 when selecting an
  // invisible cursor wrapper, since that would result in those weird
  // resize handles and a selection that considers the absolutely
  // positioned wrapper, rather than the root editable node, the
  // focused element.
  if (!view.state.selection.visible && browser.ie && browser.ie_version <= 11) {
    node.disabled = true
    node.disabled = false
  }
}

function syncNodeSelection(view, sel) {
  if (sel instanceof NodeSelection) {
    let desc = view.docView.descAt(sel.from)
    if (desc != view.lastSelectedViewDesc) {
      clearNodeSelection(view)
      if (desc) desc.selectNode()
      view.lastSelectedViewDesc = desc
    }
  } else {
    clearNodeSelection(view)
  }
}

// Clear all DOM statefulness of the last node selection.
function clearNodeSelection(view) {
  if (view.lastSelectedViewDesc) {
    view.lastSelectedViewDesc.deselectNode()
    view.lastSelectedViewDesc = null
  }
}

export function selectionBetween(view, $anchor, $head, bias) {
  return view.someProp("createSelectionBetween", f => f(view, $anchor, $head))
    || TextSelection.between($anchor, $head, bias)
}

function hasFocusAndSelection(view) {
  if (view.editable && view.root.activeElement != view.dom) return false
  return hasSelection(view)
}

function hasSelection(view) {
  let sel = view.root.getSelection()
  if (!sel.anchorNode) return false
  try {
    // Firefox will raise 'permission denied' errors when accessing
    // properties of `sel.anchorNode` when it's in a generated CSS
    // element.
    return view.dom.contains(sel.anchorNode.nodeType == 3 ? sel.anchorNode.parentNode : sel.anchorNode)
  } catch(_) {
    return false
  }
}
<|endoftext|>


<commit_before>/**
 * Created by VincentBel on 15/7/3.
 */

var MailProxy = require('../proxy').Mail;
var ROLE = require('../models/user').ROLE;
var DISTRIBUTE_STATUS = require('../models/mail').DISTRIBUTE_STATUS;
var validator = require('validator');
var moment = require('moment');

exports.getMailList = function (req, res, next) {
    if (req.user.role !== ROLE.DISTRIBUTOR) {
        res.reply(101, "邮件列表获取失败");
        return;
    }

    var page = req.query.page || 1;
    var type = validator.trim(req.query.type) || DISTRIBUTE_STATUS.NEW;

    var query = {};

    function callback(err, results, pageCount, itemCount) {
        if (err) {
            next(err);
        } else {
            for(var i=0;i<results.length;i++){
                results[i].date=moment(results[i].date).locale('zh-cn').toNow();
                console.log(results[i].date);
            }
            var data = {};
            data.page = pageCount;
            data.pageCount = pageCount;
            data.count = itemCount.length;
            data.items = results;

            res.reply(0, '邮件列表获取成功', data);
        }
    }

    if (type == 'outdated') {
        MailProxy.getDistributorOutDatedMailList(page, callback);
    } else {
        if (!DISTRIBUTE_STATUS.hasOwnProperty(type)) {
            // 若果type不合法，默认获取未分发的邮件
            type = DISTRIBUTE_STATUS.NEW;
        }
        MailProxy.getDistributorMailListByType(type, page, callback);
    }
};

exports.distribute = function (req, res, next) {
    if (req.user.role !== ROLE.DISTRIBUTOR) {
        res.reply(101, "没有权限");
        return;
    }

    var mailId = validator.trim(req.body.mailId);
    var handlerId = validator.trim(req.body.handlerId);
    var readerIds = validator.trim(req.body.readerIds);
    var handleDeadline = validator.trim(req.body.deadline);

    MailProxy.updateMailById(
        mailId,
        {
            handler: handlerId,
            readers: readerIds,
            distributeStatus: DISTRIBUTE_STATUS.DISTRIBUTED,
            handleDeadline: new Date(handleDeadline)
        },
        function (err) {
        if (err) return next(err);
        res.reply(101, "邮件分发成功");

    });

};

<commit_msg>定时收取邮件完成<commit_after>/**
 * Created by VincentBel on 15/7/3.
 */

var MailProxy = require('../proxy').Mail;
var ROLE = require('../models/user').ROLE;
var DISTRIBUTE_STATUS = require('../models/mail').DISTRIBUTE_STATUS;
var validator = require('validator');
var moment = require('moment');

exports.getMailList = function (req, res, next) {
    if (req.user.role !== ROLE.DISTRIBUTOR) {
        res.reply(101, "邮件列表获取失败");
        return;
    }

    var page = req.query.page || 1;
    var type = validator.trim(req.query.type) || DISTRIBUTE_STATUS.NEW;

    var query = {};

    function callback(err, results, pageCount, itemCount) {
        if (err) {
            next(err);
        } else {
            for(var i=0;i<results.length;i++){
                console.log(moment(results[i].date).locale('zh-cn').toNow());
            }
            var data = {};
            data.page = pageCount;
            data.pageCount = pageCount;
            data.count = itemCount.length;
            data.items = results;

            res.reply(0, '邮件列表获取成功', data);
        }
    }

    if (type == 'outdated') {
        MailProxy.getDistributorOutDatedMailList(page, callback);
    } else {
        if (!DISTRIBUTE_STATUS.hasOwnProperty(type)) {
            // 若果type不合法，默认获取未分发的邮件
            type = DISTRIBUTE_STATUS.NEW;
        }
        MailProxy.getDistributorMailListByType(type, page, callback);
    }
};

exports.distribute = function (req, res, next) {
    if (req.user.role !== ROLE.DISTRIBUTOR) {
        res.reply(101, "没有权限");
        return;
    }

    var mailId = validator.trim(req.body.mailId);
    var handlerId = validator.trim(req.body.handlerId);
    var readerIds = validator.trim(req.body.readerIds);
    var handleDeadline = validator.trim(req.body.deadline);

    MailProxy.updateMailById(
        mailId,
        {
            handler: handlerId,
            readers: readerIds,
            distributeStatus: DISTRIBUTE_STATUS.DISTRIBUTED,
            handleDeadline: new Date(handleDeadline)
        },
        function (err) {
        if (err) return next(err);
        res.reply(101, "邮件分发成功");

    });

};

<|endoftext|>