<commit_before>var EXPORTED_SYMBOLS = ["sl3uf"];

const Cc = Components.classes, Ci = Components.interfaces;

// Compatibility with before Thunderbird 17. Ugh.
if (!String.prototype.endsWith) {
  String.prototype.endsWith = function(searchString, position) {
      var subjectString = this.toString();
      if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
        position = subjectString.length;
      }
      position -= searchString.length;
      var lastIndex = subjectString.indexOf(searchString, position);
      return lastIndex !== -1 && lastIndex === position;
  };
}

function octal(num) {
    // Takes an integer and returns the value of that integer as if its base-10
    // digits were actually base-8 digits.
    // For example, an input of 10 will return the base-10 value 8.
    // This is a helper function to cope with the fact that versions of
    // Thunderbird prior to 25 didn't support the 0o syntax, and I want to
    // support back to Thunderbird 20. We can just get rid of this and Use 0o
    // syntax directly once we're only supporting Thunderbird 25 or newer.
    return parseInt(String(num), 8);
}

var sl3uf = {
    list: function() {
        // Return sorted array of function names
        var entries = this.directory().directoryEntries;
        var array = [];
        while (entries.hasMoreElements()) {
            var entry = entries.getNext();
            entry.QueryInterface(Ci.nsIFile);
            if (entry.leafName.endsWith(".slj")) {
                array.push(entry.leafName.slice(0, -4));
            }
        }
        return array.sort();
    },

    load: function(name) {
        // Returns [name, help, body]
        var file = this.directory();
        file.append(name + ".slj");
        return this.import(file);
    },

    save: function(name, help, body) {
        // Returns nothing
        var file = this.file(name);
        this.export(name, help, body, file);
    },

    remove: function(name) {
        // Returns nothing
        var file = this.file(name);
        file.remove(false);
    },

    exists: function(name) {
        // Returns true or false
        return this.file(name).exists();
    },

    import: function(file) {
        // "file" is an nsIFile object
        // Returns [name, help, body]
        var data = "";
        var fstream = Cc["@mozilla.org/network/file-input-stream;1"].
            createInstance(Ci.nsIFileInputStream);
        var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
            createInstance(Ci.nsIConverterInputStream);
        fstream.init(file, -1, 0, 0);
        cstream.init(fstream, "UTF-8", 0, 0);

        var read = 1;
        while (read != 0) {
            var str = {};
            read = cstream.readString(0xffffffff, str);
            data += str.value;
        }
        cstream.close();
        var obj = JSON.parse(data);
        return [obj.name, obj.help, obj.body];
    },

    export: function(name, help, body, file) {
        // "file" is an nsIFile object
        // Returns nothing
        var obj = {
            version: 1,
            name: name,
            help: help,
            body: body
        };
        var data = JSON.stringify(obj);
        var foStream = Cc["@mozilla.org/network/file-output-stream;1"].
            createInstance(Ci.nsIFileOutputStream);
        foStream.init(file, 0x02 | 0x08 | 0x20, octal(666), 0);
        var converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
            createInstance(Ci.nsIConverterOutputStream);
        converter.init(foStream, "UTF-8", 0, 0);
        converter.writeString(data);
        converter.close();
    },

    call: function(name, body, time, args) {
        body = "var next, nextspec, nextargs;" + body +
            "; return([next, nextspec, nextargs]);"
        var f = Function.apply(null, ["specname", "prev", "args", body]);
        var next, nextspec, nextargs;
        [next, nextspec, nextargs] = f("ufunc:" + name, time, args);
        if (! nextspec)
            return next;
        if (! nextargs)
            return [next, nextspec];
        nextargs.splice(0, 0, next, nextspec);
        return nextargs;
    },

    callByName: function(name, time, args) {
        // Returns whatever the function returns
        var help, body;
        [name, help, body] = this.load(name);
        return this.call(name, body, time, args);
    },

    nameOk: function(name) {
        // Returns true or false
        return /^\w+$/.test(name);
    },

    bodyOk: function(body) {
        // Returns true or false
        try {
            return !!Function.apply(null, ["specname", "prev", "args", body]);
        } catch (ex) {
            return false;
        }
    },

    parseArgs: function(argstring) {
        if (argstring === "")
            return [];
        try {
            var args = JSON.parse("[" + argstring + "]");
            this.unparseArgs(args); // throws exception on bad args
            return args;
        }
        catch (ex) {
            return false;
        }
    },

    unparseArgs: function(args) {
        // Convert a list into its string representation, WITHOUT the square
        // braces around the entire list.
        //
        // We stringify the individual elements of the list and then join them
        // because we want spaces after the commas for readability, and JSON.
        // stringify won't do that, and when you try to make it do that by
        // specifying its "space" argument, it inserts newlines as well, which
        // we obviously don't want.
        if (! args.length)
            return "";
        var arglist = [];
        for (var i in args) {
            val = args[i];
            if (val && val.splice)
                arglist.push('[' + this.unparseArgs(val) + ']');
            else if (! (/^(?:number|boolean|string)$/.test(typeof(val)) ||
                        val === null))
                throw new Error("Function arguments can only contain arrays " +
                                "numbers, booleans, strings, and null.");
            arglist.push(JSON.stringify(val));
        }
        return arglist.join(', ');
    },

    directory: function() {
        // Returns an nsIFile for the ufuncs storage directory
        var directoryService =
            Cc["@mozilla.org/file/directory_service;1"].
            getService(Ci.nsIProperties);
        // this is a reference to the profile dir (ProfD) now.
        var localDir = directoryService.get("ProfD", Ci.nsIFile);

        localDir.append("sendlater3");
        if (!localDir.exists() || !localDir.isDirectory()) {
            localDir.create(Ci.nsIFile.DIRECTORY_TYPE, octal(774));
        }

        localDir.append("ufuncs");
        if (!localDir.exists() || !localDir.isDirectory()) {
            localDir.create(Ci.nsIFile.DIRECTORY_TYPE, octal(774));
        }

        return localDir;
    },

    file: function(name) {
        // Returns an nsIFile for a ufunc file within the storage directory
        var file = this.directory();
        file.append(name + ".slj");
        return file;
    }
};
<commit_msg>Put newlines in dynamic scheduling function export files for readability<commit_after>var EXPORTED_SYMBOLS = ["sl3uf"];

const Cc = Components.classes, Ci = Components.interfaces;

// Compatibility with before Thunderbird 17. Ugh.
if (!String.prototype.endsWith) {
  String.prototype.endsWith = function(searchString, position) {
      var subjectString = this.toString();
      if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
        position = subjectString.length;
      }
      position -= searchString.length;
      var lastIndex = subjectString.indexOf(searchString, position);
      return lastIndex !== -1 && lastIndex === position;
  };
}

function octal(num) {
    // Takes an integer and returns the value of that integer as if its base-10
    // digits were actually base-8 digits.
    // For example, an input of 10 will return the base-10 value 8.
    // This is a helper function to cope with the fact that versions of
    // Thunderbird prior to 25 didn't support the 0o syntax, and I want to
    // support back to Thunderbird 20. We can just get rid of this and Use 0o
    // syntax directly once we're only supporting Thunderbird 25 or newer.
    return parseInt(String(num), 8);
}

var sl3uf = {
    list: function() {
        // Return sorted array of function names
        var entries = this.directory().directoryEntries;
        var array = [];
        while (entries.hasMoreElements()) {
            var entry = entries.getNext();
            entry.QueryInterface(Ci.nsIFile);
            if (entry.leafName.endsWith(".slj")) {
                array.push(entry.leafName.slice(0, -4));
            }
        }
        return array.sort();
    },

    load: function(name) {
        // Returns [name, help, body]
        var file = this.directory();
        file.append(name + ".slj");
        return this.import(file);
    },

    save: function(name, help, body) {
        // Returns nothing
        var file = this.file(name);
        this.export(name, help, body, file);
    },

    remove: function(name) {
        // Returns nothing
        var file = this.file(name);
        file.remove(false);
    },

    exists: function(name) {
        // Returns true or false
        return this.file(name).exists();
    },

    import: function(file) {
        // "file" is an nsIFile object
        // Returns [name, help, body]
        var data = "";
        var fstream = Cc["@mozilla.org/network/file-input-stream;1"].
            createInstance(Ci.nsIFileInputStream);
        var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
            createInstance(Ci.nsIConverterInputStream);
        fstream.init(file, -1, 0, 0);
        cstream.init(fstream, "UTF-8", 0, 0);

        var read = 1;
        while (read != 0) {
            var str = {};
            read = cstream.readString(0xffffffff, str);
            data += str.value;
        }
        cstream.close();
        data = data.replace(/\s+$/, "");
        if (! data.match(/"version":1,/)) {
            data = data.replace(/\n/g, "\\n");
        }
        var obj = JSON.parse(data.replace(/\n/g, "\\n"));
        return [obj.name, obj.help, obj.body];
    },

    export: function(name, help, body, file) {
        // "file" is an nsIFile object
        // Returns nothing
        var obj = {
            version: 2,
            name: name,
            help: help,
            body: body
        };
        var data = JSON.stringify(obj).replace(/\\n/g, "\n");
        var foStream = Cc["@mozilla.org/network/file-output-stream;1"].
            createInstance(Ci.nsIFileOutputStream);
        foStream.init(file, 0x02 | 0x08 | 0x20, octal(666), 0);
        var converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
            createInstance(Ci.nsIConverterOutputStream);
        converter.init(foStream, "UTF-8", 0, 0);
        converter.writeString(data);
        converter.close();
    },

    call: function(name, body, time, args) {
        body = "var next, nextspec, nextargs;" + body +
            "; return([next, nextspec, nextargs]);"
        var f = Function.apply(null, ["specname", "prev", "args", body]);
        var next, nextspec, nextargs;
        [next, nextspec, nextargs] = f("ufunc:" + name, time, args);
        if (! nextspec)
            return next;
        if (! nextargs)
            return [next, nextspec];
        nextargs.splice(0, 0, next, nextspec);
        return nextargs;
    },

    callByName: function(name, time, args) {
        // Returns whatever the function returns
        var help, body;
        [name, help, body] = this.load(name);
        return this.call(name, body, time, args);
    },

    nameOk: function(name) {
        // Returns true or false
        return /^\w+$/.test(name);
    },

    bodyOk: function(body) {
        // Returns true or false
        try {
            return !!Function.apply(null, ["specname", "prev", "args", body]);
        } catch (ex) {
            return false;
        }
    },

    parseArgs: function(argstring) {
        if (argstring === "")
            return [];
        try {
            var args = JSON.parse("[" + argstring + "]");
            this.unparseArgs(args); // throws exception on bad args
            return args;
        }
        catch (ex) {
            return false;
        }
    },

    unparseArgs: function(args) {
        // Convert a list into its string representation, WITHOUT the square
        // braces around the entire list.
        //
        // We stringify the individual elements of the list and then join them
        // because we want spaces after the commas for readability, and JSON.
        // stringify won't do that, and when you try to make it do that by
        // specifying its "space" argument, it inserts newlines as well, which
        // we obviously don't want.
        if (! args.length)
            return "";
        var arglist = [];
        for (var i in args) {
            val = args[i];
            if (val && val.splice)
                arglist.push('[' + this.unparseArgs(val) + ']');
            else if (! (/^(?:number|boolean|string)$/.test(typeof(val)) ||
                        val === null))
                throw new Error("Function arguments can only contain arrays " +
                                "numbers, booleans, strings, and null.");
            arglist.push(JSON.stringify(val));
        }
        return arglist.join(', ');
    },

    directory: function() {
        // Returns an nsIFile for the ufuncs storage directory
        var directoryService =
            Cc["@mozilla.org/file/directory_service;1"].
            getService(Ci.nsIProperties);
        // this is a reference to the profile dir (ProfD) now.
        var localDir = directoryService.get("ProfD", Ci.nsIFile);

        localDir.append("sendlater3");
        if (!localDir.exists() || !localDir.isDirectory()) {
            localDir.create(Ci.nsIFile.DIRECTORY_TYPE, octal(774));
        }

        localDir.append("ufuncs");
        if (!localDir.exists() || !localDir.isDirectory()) {
            localDir.create(Ci.nsIFile.DIRECTORY_TYPE, octal(774));
        }

        return localDir;
    },

    file: function(name) {
        // Returns an nsIFile for a ufunc file within the storage directory
        var file = this.directory();
        file.append(name + ".slj");
        return file;
    }
};
<|endoftext|>


<commit_before>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/* Event list at 2018.11.2 (via global search emitEvent)
 *
 * DDNS:Updated
 * DestIPFound
 * DeviceOffline
 * DeviceUpdate
 * IPTABLES_READY
 * IPv6DeviceInfoUpdate
 * NewDevice
 * NewDeviceFound
 * NewDeviceWithMacOnly
 * OldDeviceChangedToNewIP
 * OldDeviceTakenOverOtherDeviceIP
 * PolicyEnforcement
 * PublicIP:Updated
 * RefreshMacBackupName
 * RegularDeviceInfoUpdate
 * ReleaseMonkey
 * ReloadDNSRule
 * StartDHCP
 * StartDNS
 * StopDHCP
 * StopDNS
 * UPDATE_CATEGORY_DOMAIN
 * VPNConnectionAccepted
 * Alarm:NewAlarm
 * Policy:AllInitialized
 * Policy:CountryActivated
 */

'use strict';

let log = require('../net2/logger.js')(__filename);

const EventEmitter = require('events');

const sclient = require('../util/redis_manager.js').getSubscriptionClient()
const pclient = require('../util/redis_manager.js').getPublishClient()

let instance = null;

class SensorEventManager extends EventEmitter {
  constructor() {
    super();
    this.setMaxListeners(0);
    this.subscribeEvent();
  }

  getRemoteChannel(title) {
    return "TO." + title;
  }

  subscribeEvent() {
    sclient.on("message", (channel, message) => {
      if(channel === this.getRemoteChannel(process.title) || channel === "TO.*") {
        log.info(`Got a remote message for channel ${channel}: ${message}`)
        try {
          let m = JSON.parse(message)

          // only process redis events not originated from this process
          // local event will be processed by EventEmitter
          if(m.fromProcess !== process.title) {
            this.emitLocalEvent(m); // never send remote pubsub event back to remote
          }
        } catch (err) {
          log.error("Failed to parse channel message:", err);
        }
      } else {
        log.debug("Ignore channel", channel);
      }
    });

    sclient.subscribe(this.getRemoteChannel(process.title));
    sclient.subscribe(this.getRemoteChannel("*")); // subscribe events for all components
  }

  clearEventType(eventType) {
    this.removeAllListeners(eventType)
  }

  sendEvent(event, target) {
    this.emitEvent(Object.assign({}, event, {
      toProcess: target
    }))
  }

  sendEventToFireApi(event) {
    this.sendEvent(event, "FireApi");
  }

  sendEventToFireMain(event) {
    this.sendEvent(event, "FireMain");
  }

  sendEventToFireMon(event) {
    this.sendEvent(event, "FireMon");
  }

  sendEventToAll(event) {
    this.sendEvent(event, "*");
  }

  emitLocalEvent(event) {
    if(!event.suppressEventLogging) {
      log.info("New Event: " + event.type + " -- " + (event.message || "(no message)"));
    }

    log.debug(event.type, "subscribers: ", this.listenerCount(event.type));
    let count = this.listenerCount(event.type);
    if(count === 0) {
      log.warn("No subscription on event type:", event.type);
    } else if (count > 1) {
      // most of time, only one subscribe on each event type
      log.debug("Subscribers on event type:", event.type, "is more than ONE");
      this.emit(event.type, event);
    } else {
      this.emit(event.type, event);
    }
  }

  emitEvent(event) {
    if(event.toProcess && event.toProcess !== process.title) {
      if(!event.suppressEventLogging) {
        log.info("New Event: " + event.type + " -- " + (event.message || "(no message)"));
      }

      // this event is meant to send to another process
      let channel = this.getRemoteChannel(event.toProcess);
      const eventCopy = JSON.parse(JSON.stringify(event));
      eventCopy.fromProcess = process.title;
      pclient.publish(channel, JSON.stringify(eventCopy));
      return; // local will also be processed in .on(channel, event)..
    }

    this.emitLocalEvent(event);
  }

  on(event, callback) {
    // Error.stack is slow, so expecting subscription calls are not many, use it carefully
    log.debug("Subscribing event", event, "from",
      new Error().stack.split("\n")[2]
        .replace("     at", "")
        .replace(/.*\//, "")
        .replace(/:[^:]*$/,""));
    super.on(event, callback);
  }

  once(event, callback) {
    // Error.stack is slow, so expecting subscription calls are not many, use it carefully
    log.debug("Subscribing event", event, "from",
    new Error().stack.split("\n")[2]
      .replace("     at", "")
      .replace(/.*\//, "")
      .replace(/:[^:]*$/,""));
   super.once(event, callback);
  }

  clearAllSubscriptions() {
    super.removeAllListeners();
  }
}

function getInstance() {
  if(!instance) {
    instance = new SensorEventManager();
  }
  return instance;
}

module.exports = {
  getInstance:getInstance
}
<commit_msg>suppress event subscription warning<commit_after>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/* Event list at 2018.11.2 (via global search emitEvent)
 *
 * DDNS:Updated
 * DestIPFound
 * DeviceOffline
 * DeviceUpdate
 * IPTABLES_READY
 * IPv6DeviceInfoUpdate
 * NewDevice
 * NewDeviceFound
 * NewDeviceWithMacOnly
 * OldDeviceChangedToNewIP
 * OldDeviceTakenOverOtherDeviceIP
 * PolicyEnforcement
 * PublicIP:Updated
 * RefreshMacBackupName
 * RegularDeviceInfoUpdate
 * ReleaseMonkey
 * ReloadDNSRule
 * StartDHCP
 * StartDNS
 * StopDHCP
 * StopDNS
 * UPDATE_CATEGORY_DOMAIN
 * VPNConnectionAccepted
 * Alarm:NewAlarm
 * Policy:AllInitialized
 * Policy:CountryActivated
 */

'use strict';

let log = require('../net2/logger.js')(__filename);

const EventEmitter = require('events');

const sclient = require('../util/redis_manager.js').getSubscriptionClient()
const pclient = require('../util/redis_manager.js').getPublishClient()

const warningRecords = {};

let instance = null;

class SensorEventManager extends EventEmitter {
  constructor() {
    super();
    this.setMaxListeners(0);
    this.subscribeEvent();
  }

  getRemoteChannel(title) {
    return "TO." + title;
  }

  subscribeEvent() {
    sclient.on("message", (channel, message) => {
      if(channel === this.getRemoteChannel(process.title) || channel === "TO.*") {
        log.info(`Got a remote message for channel ${channel}: ${message}`)
        try {
          let m = JSON.parse(message)

          // only process redis events not originated from this process
          // local event will be processed by EventEmitter
          if(m.fromProcess !== process.title) {
            this.emitLocalEvent(m); // never send remote pubsub event back to remote
          }
        } catch (err) {
          log.error("Failed to parse channel message:", err);
        }
      } else {
        log.debug("Ignore channel", channel);
      }
    });

    sclient.subscribe(this.getRemoteChannel(process.title));
    sclient.subscribe(this.getRemoteChannel("*")); // subscribe events for all components
  }

  clearEventType(eventType) {
    this.removeAllListeners(eventType)
  }

  sendEvent(event, target) {
    this.emitEvent(Object.assign({}, event, {
      toProcess: target
    }))
  }

  sendEventToFireApi(event) {
    this.sendEvent(event, "FireApi");
  }

  sendEventToFireMain(event) {
    this.sendEvent(event, "FireMain");
  }

  sendEventToFireMon(event) {
    this.sendEvent(event, "FireMon");
  }

  sendEventToAll(event) {
    this.sendEvent(event, "*");
  }

  emitLocalEvent(event) {
    if(!event.suppressEventLogging) {
      log.info("New Event: " + event.type + " -- " + (event.message || "(no message)"));
    }

    log.debug(event.type, "subscribers: ", this.listenerCount(event.type));
    let count = this.listenerCount(event.type);
    if(count === 0) {
      if(!warningRecords[event.type]) {
        log.warn("No subscription on event type:", event.type);
        warningRecords[event.type] = true;
      }
    } else if (count > 1) {
      // most of time, only one subscribe on each event type
      log.debug("Subscribers on event type:", event.type, "is more than ONE");
      this.emit(event.type, event);
    } else {
      this.emit(event.type, event);
    }

    if(count !== 0) { // clear warnRecords
      if(warningRecords[event.type]) {
        log.info("subscription on event type:", event.type, "is created");
        delete warningRecords[event.type];
      }
    }
  }

  emitEvent(event) {
    if(event.toProcess && event.toProcess !== process.title) {
      if(!event.suppressEventLogging) {
        log.info("New Event: " + event.type + " -- " + (event.message || "(no message)"));
      }

      // this event is meant to send to another process
      let channel = this.getRemoteChannel(event.toProcess);
      const eventCopy = JSON.parse(JSON.stringify(event));
      eventCopy.fromProcess = process.title;
      pclient.publish(channel, JSON.stringify(eventCopy));
      return; // local will also be processed in .on(channel, event)..
    }

    this.emitLocalEvent(event);
  }

  on(event, callback) {
    // Error.stack is slow, so expecting subscription calls are not many, use it carefully
    log.debug("Subscribing event", event, "from",
      new Error().stack.split("\n")[2]
        .replace("     at", "")
        .replace(/.*\//, "")
        .replace(/:[^:]*$/,""));
    super.on(event, callback);
  }

  once(event, callback) {
    // Error.stack is slow, so expecting subscription calls are not many, use it carefully
    log.debug("Subscribing event", event, "from",
    new Error().stack.split("\n")[2]
      .replace("     at", "")
      .replace(/.*\//, "")
      .replace(/:[^:]*$/,""));
   super.once(event, callback);
  }

  clearAllSubscriptions() {
    super.removeAllListeners();
  }
}

function getInstance() {
  if(!instance) {
    instance = new SensorEventManager();
  }
  return instance;
}

module.exports = {
  getInstance:getInstance
}
<|endoftext|>


<commit_before>var _ = require("underscore")
  , routeMethods
  , routes = []

routeMethods = []

$("a").live("click", function(e) {
  var url = $(this).attr("href")
  try {
    navigate(url)
    //prevent the default if router doesnt throw a 404 exception
    e.preventDefault()
  } catch(e) {
    //just swallow the 404 and do the regular link action
  }
})

var zipObject = function(k, v) {
  var arr = _.zip(k, v)
    ,red = function(m, x) {
      m[x[0]] = x[1]
      return m
    }
  return _.reduce(arr, red, {})
}

var normalizePath = function(path, keys) {
  var buildRegex
  buildRegex = function(_, slash, format, key, capture, optional) {
    keys.push(key)
    if (slash == null) slash = ''
    return '' + (optional ? '' : slash) + '(?:' + (optional ? slash : '') + (format || '') + (capture || '([^/]+?)') + ')' + (optional || '')
  }
  path = path.concat('/?').replace(/\/\(/g, '(?:/').replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, buildRegex).replace(/([\/.])/g, '\\$1').replace(/\*/g, '(.+)')
  return new RegExp('^' + path + '$', 'i')
}

window.onpopstate = function(event) {
  event = event = {}
  var state = event.state || {}
  return navigate(document.location.pathname)
}

var matchRoute = function(url, push) {
  var matched = false
  _(routes).each(function(obj) {
    var matches = url.match(obj.regex)
      , params
    if (matches) {
      matched = true
      params = zipObject(obj.keys, matches.slice(1))
      methods = {
        url: url
      }
      // this should be made async with a callback when done
      var stopRoute = false
      _.each(obj.filters.before, function(filter) {
        if (!stopRoute && filter.call(methods, params) === false) {
          return stopRoute = true
        }
      })
      if (stopRoute) {
        return
      }
      if (push) {
        //$("body").attr("class", url.replace(/\//g, " ").trim())
        window.history.pushState({}, "", url)
      }
      obj.callback.call(methods, params)
      _.each(obj.filters.after, function(filter) {
        return filter(params)
      })
    }
  })
  if (!matched) {
    throw new Error("404 Route not found")
  }
}

registerRoute = function(route, filters, callback) {
  var keys
  keys = []
  return routes.push({
    "regex": normalizePath(route, keys),
    "keys": keys,
    "original": route,
    "filters": filters,
    "callback": callback
  })
}
var Controller = function(base, scope) {
  var filters, router
  router = {}
  filters = {
    before: [],
    after: []
  }
  router = function(route, callback) {
    if (typeof route !== "string") {
      callback = route
      route = ""
    }
    registerRoute(base + route, filters, callback)
    return this
  }
  router.get = router
  router.before = function(filter) {
    filters.before.push(filter)
    return this
  }
  router.after = function(filter) {
    filters.after.push(filter)
    return this
  }
  scope(router)
  return { get: router, before: router.before, after: router.after }
}
module.exports.Controller = Controller
var navigate = module.exports.navigate = function(url, opts) {
  var push
    , reload
  if(typeof opts === "object") {
    push = opts.push
    reload = opts.reload
  }
  if(typeof push === "undefined") {
    push = true
  }
  if(reload) {
    document.location = url
  } else {
    return matchRoute(url, {}, push)
  }
}
<commit_msg>slightly better error on 404<commit_after>var _ = require("underscore")
  , routeMethods
  , routes = []

routeMethods = []

$("a").live("click", function(e) {
  var url = $(this).attr("href")
  try {
    navigate(url)
    //prevent the default if router doesnt throw a 404 exception
    e.preventDefault()
  } catch(e) {
    //just swallow the 404 and do the regular link action
  }
})

var zipObject = function(k, v) {
  var arr = _.zip(k, v)
    ,red = function(m, x) {
      m[x[0]] = x[1]
      return m
    }
  return _.reduce(arr, red, {})
}

var normalizePath = function(path, keys) {
  var buildRegex
  buildRegex = function(_, slash, format, key, capture, optional) {
    keys.push(key)
    if (slash == null) slash = ''
    return '' + (optional ? '' : slash) + '(?:' + (optional ? slash : '') + (format || '') + (capture || '([^/]+?)') + ')' + (optional || '')
  }
  path = path.concat('/?').replace(/\/\(/g, '(?:/').replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, buildRegex).replace(/([\/.])/g, '\\$1').replace(/\*/g, '(.+)')
  return new RegExp('^' + path + '$', 'i')
}

window.onpopstate = function(event) {
  event = event = {}
  var state = event.state || {}
  return navigate(document.location.pathname)
}

var matchRoute = function(url, push) {
  var matched = false
  _(routes).each(function(obj) {
    var matches = url.match(obj.regex)
      , params
    if (matches) {
      matched = true
      params = zipObject(obj.keys, matches.slice(1))
      methods = {
        url: url
      }
      // this should be made async with a callback when done
      var stopRoute = false
      _.each(obj.filters.before, function(filter) {
        if (!stopRoute && filter.call(methods, params) === false) {
          return stopRoute = true
        }
      })
      if (stopRoute) {
        return
      }
      if (push) {
        //$("body").attr("class", url.replace(/\//g, " ").trim())
        window.history.pushState({}, "", url)
      }
      obj.callback.call(methods, params)
      _.each(obj.filters.after, function(filter) {
        return filter(params)
      })
    }
  })
  if (!matched) {
    throw new Error(url + "404 Route not found")
  }
}

registerRoute = function(route, filters, callback) {
  var keys
  keys = []
  return routes.push({
    "regex": normalizePath(route, keys),
    "keys": keys,
    "original": route,
    "filters": filters,
    "callback": callback
  })
}
var Controller = function(base, scope) {
  var filters, router
  router = {}
  filters = {
    before: [],
    after: []
  }
  router = function(route, callback) {
    if (typeof route !== "string") {
      callback = route
      route = ""
    }
    registerRoute(base + route, filters, callback)
    return this
  }
  router.get = router
  router.before = function(filter) {
    filters.before.push(filter)
    return this
  }
  router.after = function(filter) {
    filters.after.push(filter)
    return this
  }
  scope(router)
  return { get: router, before: router.before, after: router.after }
}
module.exports.Controller = Controller
var navigate = module.exports.navigate = function(url, opts) {
  var push
    , reload
  if(typeof opts === "object") {
    push = opts.push
    reload = opts.reload
  }
  if(typeof push === "undefined") {
    push = true
  }
  if(reload) {
    document.location = url
  } else {
    return matchRoute(url, {}, push)
  }
}
<|endoftext|>


<commit_before>/* eslint-env jasmine */
import moment from 'moment-mini'
import ganttDb from './ganttDb'

describe('when using the ganttDb', function () {
  beforeEach(function () {
    ganttDb.clear()
  })

  describe('when calling the clear function', function () {
    beforeEach(function () {
      ganttDb.setDateFormat('YYYY-MM-DD')
      ganttDb.enableInclusiveEndDates()
      ganttDb.setExcludes('weekends 2019-02-06,friday')
      ganttDb.addSection('weekends skip test')
      ganttDb.addTask('test1', 'id1,2019-02-01,1d')
      ganttDb.addTask('test2', 'id2,after id1,2d')
      ganttDb.clear()
    })

    it.each`
      fn                        | expected
      ${'getTasks'}             | ${[]}
      ${'getTitle'}             | ${''}
      ${'getDateFormat'}        | ${''}
      ${'getAxisFormat'}        | ${''}
      ${'getExcludes'}          | ${[]}
      ${'getSections'}          | ${[]}
      ${'endDatesAreInclusive'} | ${false}
    `('should clear $fn', ({ fn, expected }) => {
  expect(ganttDb[ fn ]()).toEqual(expected)
})
  })

  it.each`
    testName                                                                             | section     | taskName   | taskData                       | expStartDate            | expEndDate                       | expId      | expTask
    ${'should handle fixed dates'}                                                       | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2013-01-12'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)}         | ${'id1'}   | ${'test1'}
    ${'should handle duration (days) instead of fixed date to determine end date'}       | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2d'}         | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 3)}          | ${'id1'}   | ${'test1'}
    ${'should handle duration (hours) instead of fixed date to determine end date'}      | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2h'}         | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 2)}       | ${'id1'}   | ${'test1'}
    ${'should handle duration (minutes) instead of fixed date to determine end date'}    | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2m'}         | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 0, 2)}    | ${'id1'}   | ${'test1'}
    ${'should handle duration (seconds) instead of fixed date to determine end date'}    | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2s'}         | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 0, 0, 2)} | ${'id1'}   | ${'test1'}
    ${'should handle duration (weeks) instead of fixed date to determine end date'}      | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2w'}         | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 15)}         | ${'id1'}   | ${'test1'}
    ${'should handle fixed dates without id'}                                            | ${'testa1'} | ${'test1'} | ${'2013-01-01,2013-01-12'}     | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)}         | ${'task1'} | ${'test1'}
    ${'should handle duration instead of a fixed date to determine end date without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,4d'}             | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 5)}          | ${'task1'} | ${'test1'}
`('$testName', ({ section, taskName, taskData, expStartDate, expEndDate, expId, expTask }) => {
  ganttDb.setDateFormat('YYYY-MM-DD')
  ganttDb.addSection(section)
  ganttDb.addTask(taskName, taskData)
  const tasks = ganttDb.getTasks()
  expect(tasks[0].startTime).toEqual(expStartDate)
  expect(tasks[0].endTime).toEqual(expEndDate)
  expect(tasks[0].id).toEqual(expId)
  expect(tasks[0].task).toEqual(expTask)
})

  it.each`
  section     | taskName1  | taskName2  | taskData1              | taskData2             | expStartDate2                                  | expEndDate2              | expId2     | expTask2
  ${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id1,1d'} | ${new Date(2013, 0, 15)}                       | ${undefined}                      | ${'id2'}   | ${'test2'}
  ${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id3,1d'} | ${new Date((new Date()).setHours(0, 0, 0, 0))} | ${undefined}                      | ${'id2'}   | ${'test2'}
  ${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'after id1,1d'}     | ${new Date(2013, 0, 15)}                       | ${undefined}                      | ${'task1'} | ${'test2'}
  ${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2013-01-26'}       | ${new Date(2013, 0, 15)}                       | ${new Date(2013, 0, 26)} | ${'task1'} | ${'test2'}
  ${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2d'}               | ${new Date(2013, 0, 15)}                       | ${new Date(2013, 0, 17)} | ${'task1'} | ${'test2'}
`('$testName', ({ section, taskName1, taskName2, taskData1, taskData2, expStartDate2, expEndDate2, expId2, expTask2 }) => {
  ganttDb.setDateFormat('YYYY-MM-DD')
  ganttDb.addSection(section)
  ganttDb.addTask(taskName1, taskData1)
  ganttDb.addTask(taskName2, taskData2)
  const tasks = ganttDb.getTasks()
  expect(tasks[1].startTime).toEqual(expStartDate2)
  if (!expEndDate2 === undefined) {
    expect(tasks[1].endTime).toEqual(expEndDate2)
  }
  expect(tasks[1].id).toEqual(expId2)
  expect(tasks[1].task).toEqual(expTask2)
})

  it('should handle relative start date based on id regardless of sections', function () {
    ganttDb.setDateFormat('YYYY-MM-DD')
    ganttDb.addSection('testa1')
    ganttDb.addTask('test1', 'id1,2013-01-01,2w')
    ganttDb.addTask('test2', 'id2,after id3,1d')
    ganttDb.addSection('testa2')
    ganttDb.addTask('test3', 'id3,after id1,2d')

    const tasks = ganttDb.getTasks()

    expect(tasks[1].startTime).toEqual(new Date(2013, 0, 17))
    expect(tasks[1].endTime).toEqual(new Date(2013, 0, 18))
    expect(tasks[1].id).toEqual('id2')
    expect(tasks[1].task).toEqual('test2')

    expect(tasks[2].id).toEqual('id3')
    expect(tasks[2].task).toEqual('test3')
    expect(tasks[2].startTime).toEqual(new Date(2013, 0, 15))
    expect(tasks[2].endTime).toEqual(new Date(2013, 0, 17))
  })
  it('should ignore weekends', function () {
    ganttDb.setDateFormat('YYYY-MM-DD')
    ganttDb.setExcludes('weekends 2019-02-06,friday')
    ganttDb.addSection('weekends skip test')
    ganttDb.addTask('test1', 'id1,2019-02-01,1d')
    ganttDb.addTask('test2', 'id2,after id1,2d')
    ganttDb.addTask('test3', 'id3,after id2,7d')
    ganttDb.addTask('test4', 'id4,2019-02-01,2019-02-20') // Fixed endTime
    ganttDb.addTask('test5', 'id5,after id4,1d')
    ganttDb.addSection('full ending taks on last day')
    ganttDb.addTask('test6', 'id6,2019-02-13,2d')
    ganttDb.addTask('test7', 'id7,after id6,1d')

    const tasks = ganttDb.getTasks()

    expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
    expect(tasks[0].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
    expect(tasks[0].renderEndTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate())
    expect(tasks[0].id).toEqual('id1')
    expect(tasks[0].task).toEqual('test1')

    expect(tasks[1].startTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
    expect(tasks[1].endTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate())
    expect(tasks[1].renderEndTime).toEqual(moment('2019-02-06', 'YYYY-MM-DD').toDate())
    expect(tasks[1].id).toEqual('id2')
    expect(tasks[1].task).toEqual('test2')

    expect(tasks[2].startTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate())
    expect(tasks[2].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
    expect(tasks[2].renderEndTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
    expect(tasks[2].id).toEqual('id3')
    expect(tasks[2].task).toEqual('test3')

    expect(tasks[3].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
    expect(tasks[3].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
    expect(tasks[3].renderEndTime).toBeNull() // Fixed end
    expect(tasks[3].id).toEqual('id4')
    expect(tasks[3].task).toEqual('test4')

    expect(tasks[4].startTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
    expect(tasks[4].endTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate())
    expect(tasks[4].renderEndTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate())
    expect(tasks[4].id).toEqual('id5')
    expect(tasks[4].task).toEqual('test5')

    expect(tasks[5].startTime).toEqual(moment('2019-02-13', 'YYYY-MM-DD').toDate())
    expect(tasks[5].endTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate())
    expect(tasks[5].renderEndTime).toEqual(moment('2019-02-15', 'YYYY-MM-DD').toDate())
    expect(tasks[5].id).toEqual('id6')
    expect(tasks[5].task).toEqual('test6')

    expect(tasks[6].startTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate())
    expect(tasks[6].endTime).toEqual(moment('2019-02-19', 'YYYY-MM-DD').toDate())
    expect(tasks[6].id).toEqual('id7')
    expect(tasks[6].task).toEqual('test7')
  })

  describe('when setting inclusive end dates', function () {
    beforeEach(function () {
      ganttDb.setDateFormat('YYYY-MM-DD')
      ganttDb.enableInclusiveEndDates()
      ganttDb.addTask('test1', 'id1,2019-02-01,1d')
      ganttDb.addTask('test2', 'id2,2019-02-01,2019-02-03')
    })
    it('should automatically add one day to all end dates', function () {
      const tasks = ganttDb.getTasks()
      expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
      expect(tasks[0].endTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate())
      expect(tasks[0].id).toEqual('id1')
      expect(tasks[0].task).toEqual('test1')

      expect(tasks[1].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
      expect(tasks[1].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
      expect(tasks[1].renderEndTime).toBeNull() // Fixed end
      expect(tasks[1].manualEndTime).toBeTruthy()
      expect(tasks[1].id).toEqual('id2')
      expect(tasks[1].task).toEqual('test2')
    })
  })
})
<commit_msg>test(ganttDb.spec): add simple tests for the durationToDate fn<commit_after>/* eslint-env jasmine */
import moment from 'moment-mini'
import ganttDb from './ganttDb'

describe('when using the ganttDb', function () {
  beforeEach(function () {
    ganttDb.clear()
  })

  describe('when using relative times', function () {
    it.each`
      diff                | date                      | expected
      ${' 1d'}            | ${moment('2019-01-01')}   | ${moment('2019-01-02').toDate()}
      ${' 1w'}            | ${moment('2019-01-01')}   | ${moment('2019-01-08').toDate()}
    `('should add $diff to $date resulting in $expected', ({ diff, date, expected }) => {
  expect(ganttDb.durationToDate(diff, date)).toEqual(expected)
})
  })

  describe('when calling the clear function', function () {
    beforeEach(function () {
      ganttDb.setDateFormat('YYYY-MM-DD')
      ganttDb.enableInclusiveEndDates()
      ganttDb.setExcludes('weekends 2019-02-06,friday')
      ganttDb.addSection('weekends skip test')
      ganttDb.addTask('test1', 'id1,2019-02-01,1d')
      ganttDb.addTask('test2', 'id2,after id1,2d')
      ganttDb.clear()
    })

    it.each`
      fn                        | expected
      ${'getTasks'}             | ${[]}
      ${'getTitle'}             | ${''}
      ${'getDateFormat'}        | ${''}
      ${'getAxisFormat'}        | ${''}
      ${'getExcludes'}          | ${[]}
      ${'getSections'}          | ${[]}
      ${'endDatesAreInclusive'} | ${false}
    `('should clear $fn', ({ fn, expected }) => {
  expect(ganttDb[ fn ]()).toEqual(expected)
})
  })

  it.each`
    testName                                                                             | section     | taskName   | taskData                       | expStartDate            | expEndDate                       | expId      | expTask
    ${'should handle fixed dates'}                                                       | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2013-01-12'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)}         | ${'id1'}   | ${'test1'}
    ${'should handle duration (days) instead of fixed date to determine end date'}       | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2d'}         | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 3)}          | ${'id1'}   | ${'test1'}
    ${'should handle duration (hours) instead of fixed date to determine end date'}      | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2h'}         | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 2)}       | ${'id1'}   | ${'test1'}
    ${'should handle duration (minutes) instead of fixed date to determine end date'}    | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2m'}         | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 0, 2)}    | ${'id1'}   | ${'test1'}
    ${'should handle duration (seconds) instead of fixed date to determine end date'}    | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2s'}         | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 0, 0, 2)} | ${'id1'}   | ${'test1'}
    ${'should handle duration (weeks) instead of fixed date to determine end date'}      | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2w'}         | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 15)}         | ${'id1'}   | ${'test1'}
    ${'should handle fixed dates without id'}                                            | ${'testa1'} | ${'test1'} | ${'2013-01-01,2013-01-12'}     | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)}         | ${'task1'} | ${'test1'}
    ${'should handle duration instead of a fixed date to determine end date without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,4d'}             | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 5)}          | ${'task1'} | ${'test1'}
`('$testName', ({ section, taskName, taskData, expStartDate, expEndDate, expId, expTask }) => {
  ganttDb.setDateFormat('YYYY-MM-DD')
  ganttDb.addSection(section)
  ganttDb.addTask(taskName, taskData)
  const tasks = ganttDb.getTasks()
  expect(tasks[0].startTime).toEqual(expStartDate)
  expect(tasks[0].endTime).toEqual(expEndDate)
  expect(tasks[0].id).toEqual(expId)
  expect(tasks[0].task).toEqual(expTask)
})

  it.each`
  section     | taskName1  | taskName2  | taskData1              | taskData2             | expStartDate2                                  | expEndDate2              | expId2     | expTask2
  ${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id1,1d'} | ${new Date(2013, 0, 15)}                       | ${undefined}                      | ${'id2'}   | ${'test2'}
  ${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id3,1d'} | ${new Date((new Date()).setHours(0, 0, 0, 0))} | ${undefined}                      | ${'id2'}   | ${'test2'}
  ${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'after id1,1d'}     | ${new Date(2013, 0, 15)}                       | ${undefined}                      | ${'task1'} | ${'test2'}
  ${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2013-01-26'}       | ${new Date(2013, 0, 15)}                       | ${new Date(2013, 0, 26)} | ${'task1'} | ${'test2'}
  ${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2d'}               | ${new Date(2013, 0, 15)}                       | ${new Date(2013, 0, 17)} | ${'task1'} | ${'test2'}
`('$testName', ({ section, taskName1, taskName2, taskData1, taskData2, expStartDate2, expEndDate2, expId2, expTask2 }) => {
  ganttDb.setDateFormat('YYYY-MM-DD')
  ganttDb.addSection(section)
  ganttDb.addTask(taskName1, taskData1)
  ganttDb.addTask(taskName2, taskData2)
  const tasks = ganttDb.getTasks()
  expect(tasks[1].startTime).toEqual(expStartDate2)
  if (!expEndDate2 === undefined) {
    expect(tasks[1].endTime).toEqual(expEndDate2)
  }
  expect(tasks[1].id).toEqual(expId2)
  expect(tasks[1].task).toEqual(expTask2)
})

  it('should handle relative start date based on id regardless of sections', function () {
    ganttDb.setDateFormat('YYYY-MM-DD')
    ganttDb.addSection('testa1')
    ganttDb.addTask('test1', 'id1,2013-01-01,2w')
    ganttDb.addTask('test2', 'id2,after id3,1d')
    ganttDb.addSection('testa2')
    ganttDb.addTask('test3', 'id3,after id1,2d')

    const tasks = ganttDb.getTasks()

    expect(tasks[1].startTime).toEqual(new Date(2013, 0, 17))
    expect(tasks[1].endTime).toEqual(new Date(2013, 0, 18))
    expect(tasks[1].id).toEqual('id2')
    expect(tasks[1].task).toEqual('test2')

    expect(tasks[2].id).toEqual('id3')
    expect(tasks[2].task).toEqual('test3')
    expect(tasks[2].startTime).toEqual(new Date(2013, 0, 15))
    expect(tasks[2].endTime).toEqual(new Date(2013, 0, 17))
  })
  it('should ignore weekends', function () {
    ganttDb.setDateFormat('YYYY-MM-DD')
    ganttDb.setExcludes('weekends 2019-02-06,friday')
    ganttDb.addSection('weekends skip test')
    ganttDb.addTask('test1', 'id1,2019-02-01,1d')
    ganttDb.addTask('test2', 'id2,after id1,2d')
    ganttDb.addTask('test3', 'id3,after id2,7d')
    ganttDb.addTask('test4', 'id4,2019-02-01,2019-02-20') // Fixed endTime
    ganttDb.addTask('test5', 'id5,after id4,1d')
    ganttDb.addSection('full ending taks on last day')
    ganttDb.addTask('test6', 'id6,2019-02-13,2d')
    ganttDb.addTask('test7', 'id7,after id6,1d')

    const tasks = ganttDb.getTasks()

    expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
    expect(tasks[0].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
    expect(tasks[0].renderEndTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate())
    expect(tasks[0].id).toEqual('id1')
    expect(tasks[0].task).toEqual('test1')

    expect(tasks[1].startTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
    expect(tasks[1].endTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate())
    expect(tasks[1].renderEndTime).toEqual(moment('2019-02-06', 'YYYY-MM-DD').toDate())
    expect(tasks[1].id).toEqual('id2')
    expect(tasks[1].task).toEqual('test2')

    expect(tasks[2].startTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate())
    expect(tasks[2].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
    expect(tasks[2].renderEndTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
    expect(tasks[2].id).toEqual('id3')
    expect(tasks[2].task).toEqual('test3')

    expect(tasks[3].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
    expect(tasks[3].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
    expect(tasks[3].renderEndTime).toBeNull() // Fixed end
    expect(tasks[3].id).toEqual('id4')
    expect(tasks[3].task).toEqual('test4')

    expect(tasks[4].startTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
    expect(tasks[4].endTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate())
    expect(tasks[4].renderEndTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate())
    expect(tasks[4].id).toEqual('id5')
    expect(tasks[4].task).toEqual('test5')

    expect(tasks[5].startTime).toEqual(moment('2019-02-13', 'YYYY-MM-DD').toDate())
    expect(tasks[5].endTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate())
    expect(tasks[5].renderEndTime).toEqual(moment('2019-02-15', 'YYYY-MM-DD').toDate())
    expect(tasks[5].id).toEqual('id6')
    expect(tasks[5].task).toEqual('test6')

    expect(tasks[6].startTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate())
    expect(tasks[6].endTime).toEqual(moment('2019-02-19', 'YYYY-MM-DD').toDate())
    expect(tasks[6].id).toEqual('id7')
    expect(tasks[6].task).toEqual('test7')
  })

  describe('when setting inclusive end dates', function () {
    beforeEach(function () {
      ganttDb.setDateFormat('YYYY-MM-DD')
      ganttDb.enableInclusiveEndDates()
      ganttDb.addTask('test1', 'id1,2019-02-01,1d')
      ganttDb.addTask('test2', 'id2,2019-02-01,2019-02-03')
    })
    it('should automatically add one day to all end dates', function () {
      const tasks = ganttDb.getTasks()
      expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
      expect(tasks[0].endTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate())
      expect(tasks[0].id).toEqual('id1')
      expect(tasks[0].task).toEqual('test1')

      expect(tasks[1].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
      expect(tasks[1].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
      expect(tasks[1].renderEndTime).toBeNull() // Fixed end
      expect(tasks[1].manualEndTime).toBeTruthy()
      expect(tasks[1].id).toEqual('id2')
      expect(tasks[1].task).toEqual('test2')
    })
  })
})
<|endoftext|>


<commit_before>$(function() {
    'use strict';

	var defaultTranslation = 'en',
		translations = ['en','zh','es'];

	window.getLocale = function () {
        if (!localStorage) {
            return defaultTranslation;
        }
        if (location.search === '?locale=zh') {
            localStorage.locale = 'zh';
        }
        return localStorage.locale;
    };

	function main() {
        $(window).scroll(showGotoTop);
        $(window).resize(showGotoTop);
        $('.goto-top').click(function() {
            document.body.scrollTop = 0;
            document.documentElement.scrollTop = 0;
            return false;
        });
        initLocale();
        initScrollspy();
        showGotoTop();
        $('#bulletin').bulletin();
    }

    function initLocale() {
        var $locale = $('#locale');

        if (!localStorage) {
            $locale.hide();
            return;
        }

		switch (getLocale()) {
			case 'zh':
				setGlobalLanguage($locale, '简体中文', 'zh');
			break;

			case 'es':
				setGlobalLanguage($locale, 'Español', 'es');
			break;

			default:
				setGlobalLanguage($locale, 'English', 'en');
			break;
		};

        $('[data-locale]').click(function () {
            localStorage.locale = $(this).data('locale');
            location.reload(true);
        });
    }

	function setGlobalLanguage($locale, text, languageCode) {
		for(i = 0; i < translations.length; i++) {
			if(translations[i] !== languageCode) {
				$locale.find('[data-locale="'+ translations[i] + '"]').removeClass('active').end()
				.find('.dropdown-toggle img').removeClass('flag-'+ translations[i] +'').end();
			}
		}

		$locale.find('.language').text(text).end()
			.find('[data-locale="'+languageCode + '"]').addClass('active').end()
			.find('.dropdown-toggle img').addClass('flag-'+ languageCode +'').end();

		$('[data-'+ languageCode + ']').each(function () {
			$(this).html($(this).data(languageCode));
		});
	}

    function initScrollspy() {
        var $window = $(window),
            $body = $(document.body),
            html = [];

        $('.page-header').find('h1, h2').each(function (i) {
            var $this = $(this),
                parent = $this.is('h1'),
                link = '' + $.trim($this.text()) + '';

            if (parent) {
                if (i > 0) {
                    html.push('

');
                }
                html.push('
', link, '
');
            } else {
                html.push('
', link, '
');
            }
        });
        html.push('

');
        $('.bs-sidenav').html(html.join(''));

        $body.scrollspy({
            target: '.bs-sidebar',
            offset: $('.navbar').outerHeight(true) + 10
        });
        $body.scrollspy('refresh');

        // affix
        setTimeout(function () {
            var $sideBar = $('.bs-sidebar');

            $sideBar.affix({
                offset: {
                    top: function () {
                        var offsetTop = $sideBar.offset().top;
                        var sideBarMargin = parseInt($sideBar.children(0).css('margin-top'), 10);
                        var navOuterHeight = $('.bs-docs-nav').height();

                        return (this.top = offsetTop - navOuterHeight - sideBarMargin);
                    },
                    bottom: function () {
                        return (this.bottom = $('.bs-footer').outerHeight(true));
                    }
                }
            });
        }, 100);
    }

    function showGotoTop() {
        var $gotoTop = $('.goto-top'),
            $bdshare = $('#bdshare');

        if ($(document).scrollTop() > 0) {
            $gotoTop.fadeIn('slow');
            $bdshare.fadeOut('slow');
        } else {
            $gotoTop.fadeOut('slow');
            $bdshare.fadeIn('slow');
        }
    }

    main();
});<commit_msg>Improve the language translation<commit_after>$(function() {
    'use strict';

	var defaultTranslation = 'en',
		translations = {'en': {title: 'English', code: 'en'},'zh': {title: '简体中文', code: 'zh'},'es': {title: 'Español', code: 'es'}};

	window.getLocale = function () {
        if (!localStorage) {
            return defaultTranslation;
        }

		if (location.search !== '') {
            localStorage.locale = translations[location.search.replace('?locale=', '')].code;
        }

        return localStorage.locale === undefined ? defaultTranslation : localStorage.locale;
    };

	function main() {
        $(window).scroll(showGotoTop);
        $(window).resize(showGotoTop);
        $('.goto-top').click(function() {
            document.body.scrollTop = 0;
            document.documentElement.scrollTop = 0;
            return false;
        });
        initLocale();
        initScrollspy();
        showGotoTop();
        $('#bulletin').bulletin();
    }

    function initLocale() {
        var $locale = $('#locale'),
			translation = {};

        if (!localStorage) {
            $locale.hide();
            return;
        }

		translation = translations[getLocale()];
		setGlobalLanguage($locale, translation.title, translation.code);

        $('[data-locale]').click(function () {
            localStorage.locale = $(this).data('locale');
            location.reload(true);
        });
    }

	function setGlobalLanguage($locale, text, languageCode) {
		var translationsArray = $.map(translations, function(value, index) {
			return [value];
		});

		for(var i = 0; i < translationsArray.length; i++) {
			if(translationsArray[i].code !== languageCode) {
				$locale.find('[data-locale="'+ translationsArray[i].code + '"]').removeClass('active').end()
				.find('.dropdown-toggle img').removeClass('flag-'+ translationsArray[i].code +'').end();
			}
		}

		$locale.find('.language').text(text).end()
			.find('[data-locale="'+languageCode + '"]').addClass('active').end()
			.find('.dropdown-toggle img').addClass('flag-'+ languageCode +'').end();

		$('[data-'+ languageCode + ']').each(function () {
			$(this).html($(this).data(languageCode));
		});
	}

    function initScrollspy() {
        var $window = $(window),
            $body = $(document.body),
            html = [];

        $('.page-header').find('h1, h2').each(function (i) {
            var $this = $(this),
                parent = $this.is('h1'),
                link = '' + $.trim($this.text()) + '';

            if (parent) {
                if (i > 0) {
                    html.push('

');
                }
                html.push('
', link, '
');
            } else {
                html.push('
', link, '
');
            }
        });
        html.push('

');
        $('.bs-sidenav').html(html.join(''));

        $body.scrollspy({
            target: '.bs-sidebar',
            offset: $('.navbar').outerHeight(true) + 10
        });
        $body.scrollspy('refresh');

        // affix
        setTimeout(function () {
            var $sideBar = $('.bs-sidebar');

            $sideBar.affix({
                offset: {
                    top: function () {
                        var offsetTop = $sideBar.offset().top;
                        var sideBarMargin = parseInt($sideBar.children(0).css('margin-top'), 10);
                        var navOuterHeight = $('.bs-docs-nav').height();

                        return (this.top = offsetTop - navOuterHeight - sideBarMargin);
                    },
                    bottom: function () {
                        return (this.bottom = $('.bs-footer').outerHeight(true));
                    }
                }
            });
        }, 100);
    }

    function showGotoTop() {
        var $gotoTop = $('.goto-top'),
            $bdshare = $('#bdshare');

        if ($(document).scrollTop() > 0) {
            $gotoTop.fadeIn('slow');
            $bdshare.fadeOut('slow');
        } else {
            $gotoTop.fadeOut('slow');
            $bdshare.fadeIn('slow');
        }
    }

    main();
});<|endoftext|>


<commit_before>'use strict';

var net = require('net');

var server = net.createServer(function(connection) {
	console.log('Connection open');
	connection.write('Hello?\r\n');
	connection.on('data', function(data) {
		if (String(data).trim() != 'hello') {
			connection.write('ERROR\r\n');
		} else {
			connection.write('world\r\n');
			console.log('Connection closed');
		}
	});
});
server.listen(port);
console.log('Server listening on port %s', port);

<commit_msg>Removed line breaks.<commit_after>'use strict';

var net = require('net');

var server = net.createServer(function(connection) {
	console.log('Connection open');
	connection.write('Hello?');
	connection.on('data', function(data) {
		if (String(data).trim() != 'hello') {
			connection.write('ERROR');
		} else {
			connection.write('world');
			console.log('Connection closed');
		}
	});
});
server.listen(port);
console.log('Server listening on port %s', port);

<|endoftext|>


<commit_before>var CharityApp = (function() {

  var getProducts = function() {
    $.ajax({
      type: 'GET',
      url: '../products.json',
      success: function(data, status, xhr) {
        var source = $('#product-template').html(),
              template = Handlebars.compile(source),
              self = this;
        _.each(data.products, function(product) {
          console.log(product)
          var context = {
              headline: product.headline,
              productImage: product.productImage,
              currentBid: product.currentBid,
              hoursLeft: product.hoursLeft,
              minutesLeft: product.minutesLeft
            },
            html = template(context);
          $('.auction-columns').append(html);
        })
      },
      error: function(xhr, errorType, error) {
        console.log(['now there was error', xhr, errorType, error]);
      }
    });
  }

  return {
    getProducts: getProducts
  };
})();<commit_msg>Get item listings from the API<commit_after>var CharityApp = (function() {

  // FIXME: Maybe not the best place to put this, but it'll do for
  // now.
  // var UserId = 3333014;
  var UserId = 2023663

  var getProducts = function() {
    $.ajax({
      type: 'GET',
      url: '/users/' + UserId + '/items',
      success: function(data, status, xhr) {
        var source = $('#product-template').html(),
              template = Handlebars.compile(source),
              self = this;
        _.each(data.products, function(product) {
          console.log(product)
          var context = {
              headline: product.headline,
              productImage: product.productImage,
              currentBid: product.currentBid,
              hoursLeft: product.hoursLeft,
              minutesLeft: product.minutesLeft
            },
            html = template(context);
          $('.auction-columns').append(html);
        })
      },
      error: function(xhr, errorType, error) {
        console.log(['now there was error', xhr, errorType, error]);
      }
    });
  }

  return {
    getProducts: getProducts
  };
})();<|endoftext|>


<commit_before>import * as util from "./util";

var objectId = 0, changedObjects = {}, delayCallbacks = [], flushTimer;

function BasisObject() {
  Object.defineProperty(this, 'objectId', {value: ++objectId});
  this.init.apply(this, arguments);
};

BasisObject.displayName = 'Basis.Object';

// Internal: Processes a property change by traversing the property dependency graph and forwarding
// changes to proxy objects.
function didChange(object, name) {
  (object.__changedProps__ = object.__changedProps__ || {})[name] = true;
  changedObjects[object.objectId] = object;
  uncache.call(object, name);

  if (object.__deps__) {
    let deps = object.__deps__[name];

    if (deps) {
      for (let i = 0, n = deps.length; i < n; i++) {
        didChange(object, deps[i]);
      }
    }
  }

  if (object.__proxies__ && name.indexOf('.') === -1) {
    for (let k in object.__proxies__) {
      util.detectRecursion(object, function() {
        didChange(object.__proxies__[k].object, `${object.__proxies__[k].name}.${name}`);
      });
    }
  }
}

// Internal: Flushes the current change queue. This notifies observers of the changed props as well
// as observers of any props that depend on the changed props. Observers are only invoked once per
// flush, regardless of how many of their dependent props have changed. Additionaly, cached values
// are cleared where appropriate.
function flush() {
  var ids = Object.keys(changedObjects), f;

  for (let j = 0, m = ids.length; j < m; j++) {
    let object  = changedObjects[ids[j]];
    let changes = Object.keys(object.__changedProps__);

    for (let i = 0, n = changes.length; i < n; i++) {
      didChange(object, changes[i]);
    }
  }

  for (let id in changedObjects) {
    let object  = changedObjects[id];
    let changes = Object.keys(object.__changedProps__);
    let star    = false;

    object.__changedProps__ = {};

    for (let i = 0, n = changes.length; i < n; i++) {
      if (changes[i].indexOf('.') === -1) { star = true; }
      object.notify(changes[i]);
    }

    if (star) { object.notify('*'); }
  }

  changedObjects = {};

  flushTimer = null;

  while (f = delayCallbacks.shift()) { f(); }
}

// Internal: Caches the given name/value pair on the receiver.
function cache(name, value) { (this.__cache__ = this.__cache__ || {})[name] = value; }

// Internal: Removes the given name from the cache.
function uncache(name) { if (this.__cache__) { delete this.__cache__[name]; } }

// Internal: Indicates whether the current name has a value cached.
function isCached(name) { return this.__cache__ ? this.__cache__.hasOwnProperty(name) : false; }

// Internal: Returns the cached value for the given name.
function getCached(name) { return this.__cache__ ? this.__cache__[name] : undefined; }

// Internal: Defines a property on the given object. See the docs for `Basis.prop`.
//
// Returns nothing.
function defineProp(object, name, opts = {}) {
  var descriptor = Object.assign({
    name: name,
    get: null,
    set: null,
    default: undefined,
    on: [],
    cache: false,
    pure: !!(opts.get && opts.on && !opts.set),
  }, opts, {readonly: opts.get && !opts.set});

  if (!object.hasOwnProperty('__props__')) {
    object.__props__ = Object.create(object.__props__ || null);
  }

  object.__props__[name] = descriptor;

  if (!object.hasOwnProperty('__deps__')) {
    object.__deps__ = Object.create(object.__deps__ || null);
  }

  descriptor.on.forEach(function(prop) {
    (object.__deps__[prop] = object.__deps__[prop] || []).push(name);

    if (prop.indexOf('.') !== -1) {
      let segments = prop.split('.'), first = segments[0];
      if (segments.length > 2) {
        throw new Error(`Basis.Object.defineProp: dependent property paths of more than two segments are not allowed: \`${prop}\``);
      }
      (object.__deps__[first] = object.__deps__[first] || []).push(name);
    }
  });

  Object.defineProperty(object, name, {
    get: function() { return this._getProp(name); },
    set: descriptor.readonly ? undefined : function(value) { this._setProp(name, value); },
    configurable: false,
    enumerable: true
  });
}

// Public: Flush the pending change queue. This should only be used in specs.
BasisObject.flush = function() {
  clearTimeout(flushTimer);
  flush();
  return this;
};

// Public: Register a callback to be invoked immediately after the next flush cycle completes.
//
// f - A function.
//
// Returns the receiver;
BasisObject.delay = function(f) {
  delayCallbacks.push(f);
  return this;
};

// Public: Creates a subclass of `Basis.Object`.
BasisObject.extend = function(f) {
  var subclass = function() { BasisObject.apply(this, arguments); };

  for (let k in this) { if (this.hasOwnProperty(k)) { subclass[k] = this[k]; } }

  subclass.prototype = Object.create(this.prototype);
  subclass.prototype.constructor = subclass;
  subclass.__super__ = this.prototype;

  if (typeof f === 'function') { f.call(subclass); }

  return subclass;
};

// Public: Defines a property on the class's prototype. Properties defined with this method are
// observable using the `Basis.Object#on` method.
//
// name - A string containing the name of the property.
// opts - An object containing one or more of the following keys:
//   get       - A custom property getter function.
//   set       - A custom property setter function.
//   default   - Specify a default value for the property.
//   on        - An array of dependent path names. Observers of the prop are notified when any of
//               these props change. Paths may be at most two segments long.
//   pure      - Boolean indicating whether the computed prop's `get` function is pure or not. When
//               accessed, a pure prop's `get` function has its dependencies passed in and is called
//               with `this` set to null. If you have a computed prop with dependencies and need to
//               access `this`, then `pure` must be set to `false`.
//   cache     - Set this to true to enable property caching. This is useful with computed
//               properties that have their dependent events defined. If dependent events aren't
//               defined, then the initially cached value will never be cleared.
//
// Returns the receiver.
BasisObject.prop = function(name, opts = {}) {
  defineProp(this.prototype, name, opts);
  return this;
};

// Public: Creates multiple props at once from the given object.
//
// props - An object mapping prop names to their options. See `Basis.Object#prop` for the available
//         options.
//
// Returns the receiver.
BasisObject.props = function(props) {
  for (let name in props) { this.prop(name, props[name]); }
  return this;
};

// Public: Returns a string containing the class's name.
BasisObject.toString = function() { return this.displayName || this.name || '(Unknown)'; };

// Public: The `Basis.Object` initializer. Sets the given props and begins observing dependent
// property events. This method should never be called directly, its called by the `Basis.Object`
// constructor function.
//
// props - An object containing properties to set. Only properties defined via `Basis.Object.prop`
//         are settable.
BasisObject.prototype.init = function(props) { if (props) { this.set(props); } };

// Public: Defines an observable property directly on the receiver. See the `Basis.Object.prop`
// method for available options.
BasisObject.prototype.prop = function(name, opts = {}) {
  defineProp(this, name, opts);
  return this;
};

// Public: Creates multiple props at once from the given object directly on the receiver.
BasisObject.prototype.props = BasisObject.props;

// Public: Set multiple props at once. This method take special care to only set props that are
// defined with `Basis.Object.prop` and are not readonly.
//
// props - An object containing props to set.
//
// Returns nothing.
BasisObject.prototype.set = function(props) {
  for (let k in props) {
    if (this.__props__ && this.__props__[k] && !this.__props__[k].readonly) {
      this[k] = props[k];
    }
  }
};

// Public: Register a callback to be invoked when the given prop changes. Callbacks are invoked
// asynchronously whenever the property is changed directly or one of its dependent properties is
// changed.
//
// prop     - The name of the property to observe.
// callback - A function to invoke when the prop changes.
//
// Returns the receiver.
BasisObject.prototype.on = function(prop, callback) {
  this.__observers__ = this.__observers__ || {};
  this.__observers__[prop] = this.__observers__[prop] || [];
  this.__observers__[prop].push(callback);
  return this;
};

// Public: Remove a prop observer.
//
// prop     - The name of the property to stop observing.
// callback - The function passed to `Basis.Object#on`.
//
// Returns the receiver.
BasisObject.prototype.off = function(prop, callback) {
  if (this.__observers__ && this.__observers__[prop]) {
    for (let i = this.__observers__[prop].length - 1; i >= 0; i--) {
      if (this.__observers__[prop][i] === callback) {
        this.__observers__[prop].splice(i, 1);
      }
    }
  }
  return this;
};

// Public: Notifies observers of an event. Observers are invoked with the name of the event and
// any additional arguments passed to `notify`.
//
// event   - A string containing the name of the event.
// ...args - Zero or more additional arguments to pass along to observers.
//
// Returns the receiver.
BasisObject.prototype.notify = function(event, ...args) {
  if (this.__observers__ && this.__observers__[event]) {
    for (let i = 0, n = this.__observers__[event].length; i < n; i++) {
      if (this.__observers__[event][i]) {
        try {
          this.__observers__[event][i](event, ...args);
        }
        catch (e) {
          console.error('Basis.Object#notify: exception caught in observer:', e);
        }
      }
    }
  }

  return this;
};

// Public: Registers a property change and asynchronously triggers property observers. This is
// called automatically when a prop is set. This should only be used when the state of a prop is
// managed by external code.
//
// name - The name of the prop that has changed.
//
// Returns the receiver.
BasisObject.prototype.didChange = function(name) {
  (this.__changedProps__ = this.__changedProps__ || {})[name] = true;
  changedObjects[this.objectId] = this;

  // FIXME: The double setTimeout here is to work around an issue we've been seeing in Firefox
  // with promises. In Chrome the flush is getting run after promises callbacks are invoked but
  // in firefox its running before which is causing some timing issues. The double setTimeout
  // ensures that the flush gets run after promise callbacks.
  if (!flushTimer) { flushTimer = setTimeout(function() { setTimeout(flush); }); }

  return this;
};

// Public: Returns a string representation of the object.
BasisObject.prototype.toString = function() {
  return `#<${this.constructor}:${this.objectId}>`;
};

// Public: Indicates whether the receiver is equal to the given object. The default implementation
// simply does an identity comparison using the `===` operator. You'll likely want to override
// this method in your sub-types in order to perform a more meaningful comparison.
//
// o - An object to compare against the receiver.
//
// Returns a `true` if the objects are equal and `false` otherwise.
BasisObject.prototype.eq = function(other) { return this === other; };

BasisObject.prototype.getPath = function(path) { return util.getPath(this, path); };

// Internal: Returns the current value of the given property or the default value if it is not
// defined.
//
// Returns the value of the property.
// Throws `Error` if there is no property with the given name.
BasisObject.prototype._getProp = function(name) {
  var desc = this.__props__ && this.__props__[name], value;

  if (!desc) {
    throw new Error(`Basis.Object#_getProp: unknown prop name \`${name}\``);
  }

  if (desc.cache && isCached.call(this, name)) { return getCached.call(this, name); }

  if (desc.get && desc.pure) {
    value = desc.get.apply(null, desc.on.map((path) => this.getPath(path)));
  }
  else if (desc.get) {
    value = desc.get.call(this);
  }
  else {
    value = this[`__${name}`];
  }

  value = (value === undefined) ? desc.default : value;

  if (desc.cache) { cache.call(this, name, value); }

  return value;
};

// Internal: Sets the value of the given property.
//
// name - The name of the prop to change.
// value - The new value of the prop.
//
// Returns the previous value.
// Throws `Error` if there is no property with the given name.
// Throws `TypeError` if the property is readonly.
BasisObject.prototype._setProp = function(name, value) {
  var descriptor = this.__props__ && this.__props__[name],
      key        = `__${name}`,
      old        = this._getProp(name);

  if (!descriptor) {
    throw new Error(`Basis.Object#_setProp: unknown prop name \`${name}\``);
  }

  if (descriptor.readonly) {
    throw new TypeError(`Basis.Object#_setProp: cannot set readonly property \`${name}\` of ${this}`);
  }

  if (descriptor.set) { descriptor.set.call(this, value); }
  else { this[key] = value; }

  this.didChange(name);

  return old;
};

// Internal: Registers a proxy object. All prop changes on the receiver will be proxied to the
// given proxy object with the given name as a prefix for the property name.
BasisObject.prototype._registerProxy = function(object, name) {
  this.__proxies__ = this.__proxies__ || {};
  this.__proxies__[`${object.objectId},${name}`] = {object, name};
  return this;
};

// Internal: Deregisters a proxy object previously registered with `#_registerProxy`.
BasisObject.prototype._deregisterProxy = function(object, name) {
  if (this.__proxies__) { delete this.__proxies__[`${object.objectId},${name}`]; }
  return this;
};

BasisObject.displayName = 'Basis.Object';

export default BasisObject;
<commit_msg>Adds docs for Object#getPath.<commit_after>import * as util from "./util";

var objectId = 0, changedObjects = {}, delayCallbacks = [], flushTimer;

function BasisObject() {
  Object.defineProperty(this, 'objectId', {value: ++objectId});
  this.init.apply(this, arguments);
};

BasisObject.displayName = 'Basis.Object';

// Internal: Processes a property change by traversing the property dependency graph and forwarding
// changes to proxy objects.
function didChange(object, name) {
  (object.__changedProps__ = object.__changedProps__ || {})[name] = true;
  changedObjects[object.objectId] = object;
  uncache.call(object, name);

  if (object.__deps__) {
    let deps = object.__deps__[name];

    if (deps) {
      for (let i = 0, n = deps.length; i < n; i++) {
        didChange(object, deps[i]);
      }
    }
  }

  if (object.__proxies__ && name.indexOf('.') === -1) {
    for (let k in object.__proxies__) {
      util.detectRecursion(object, function() {
        didChange(object.__proxies__[k].object, `${object.__proxies__[k].name}.${name}`);
      });
    }
  }
}

// Internal: Flushes the current change queue. This notifies observers of the changed props as well
// as observers of any props that depend on the changed props. Observers are only invoked once per
// flush, regardless of how many of their dependent props have changed. Additionaly, cached values
// are cleared where appropriate.
function flush() {
  var ids = Object.keys(changedObjects), f;

  for (let j = 0, m = ids.length; j < m; j++) {
    let object  = changedObjects[ids[j]];
    let changes = Object.keys(object.__changedProps__);

    for (let i = 0, n = changes.length; i < n; i++) {
      didChange(object, changes[i]);
    }
  }

  for (let id in changedObjects) {
    let object  = changedObjects[id];
    let changes = Object.keys(object.__changedProps__);
    let star    = false;

    object.__changedProps__ = {};

    for (let i = 0, n = changes.length; i < n; i++) {
      if (changes[i].indexOf('.') === -1) { star = true; }
      object.notify(changes[i]);
    }

    if (star) { object.notify('*'); }
  }

  changedObjects = {};

  flushTimer = null;

  while (f = delayCallbacks.shift()) { f(); }
}

// Internal: Caches the given name/value pair on the receiver.
function cache(name, value) { (this.__cache__ = this.__cache__ || {})[name] = value; }

// Internal: Removes the given name from the cache.
function uncache(name) { if (this.__cache__) { delete this.__cache__[name]; } }

// Internal: Indicates whether the current name has a value cached.
function isCached(name) { return this.__cache__ ? this.__cache__.hasOwnProperty(name) : false; }

// Internal: Returns the cached value for the given name.
function getCached(name) { return this.__cache__ ? this.__cache__[name] : undefined; }

// Internal: Defines a property on the given object. See the docs for `Basis.prop`.
//
// Returns nothing.
function defineProp(object, name, opts = {}) {
  var descriptor = Object.assign({
    name: name,
    get: null,
    set: null,
    default: undefined,
    on: [],
    cache: false,
    pure: !!(opts.get && opts.on && !opts.set),
  }, opts, {readonly: opts.get && !opts.set});

  if (!object.hasOwnProperty('__props__')) {
    object.__props__ = Object.create(object.__props__ || null);
  }

  object.__props__[name] = descriptor;

  if (!object.hasOwnProperty('__deps__')) {
    object.__deps__ = Object.create(object.__deps__ || null);
  }

  descriptor.on.forEach(function(prop) {
    (object.__deps__[prop] = object.__deps__[prop] || []).push(name);

    if (prop.indexOf('.') !== -1) {
      let segments = prop.split('.'), first = segments[0];
      if (segments.length > 2) {
        throw new Error(`Basis.Object.defineProp: dependent property paths of more than two segments are not allowed: \`${prop}\``);
      }
      (object.__deps__[first] = object.__deps__[first] || []).push(name);
    }
  });

  Object.defineProperty(object, name, {
    get: function() { return this._getProp(name); },
    set: descriptor.readonly ? undefined : function(value) { this._setProp(name, value); },
    configurable: false,
    enumerable: true
  });
}

// Public: Flush the pending change queue. This should only be used in specs.
BasisObject.flush = function() {
  clearTimeout(flushTimer);
  flush();
  return this;
};

// Public: Register a callback to be invoked immediately after the next flush cycle completes.
//
// f - A function.
//
// Returns the receiver;
BasisObject.delay = function(f) {
  delayCallbacks.push(f);
  return this;
};

// Public: Creates a subclass of `Basis.Object`.
BasisObject.extend = function(f) {
  var subclass = function() { BasisObject.apply(this, arguments); };

  for (let k in this) { if (this.hasOwnProperty(k)) { subclass[k] = this[k]; } }

  subclass.prototype = Object.create(this.prototype);
  subclass.prototype.constructor = subclass;
  subclass.__super__ = this.prototype;

  if (typeof f === 'function') { f.call(subclass); }

  return subclass;
};

// Public: Defines a property on the class's prototype. Properties defined with this method are
// observable using the `Basis.Object#on` method.
//
// name - A string containing the name of the property.
// opts - An object containing one or more of the following keys:
//   get       - A custom property getter function.
//   set       - A custom property setter function.
//   default   - Specify a default value for the property.
//   on        - An array of dependent path names. Observers of the prop are notified when any of
//               these props change. Paths may be at most two segments long.
//   pure      - Boolean indicating whether the computed prop's `get` function is pure or not. When
//               accessed, a pure prop's `get` function has its dependencies passed in and is called
//               with `this` set to null. If you have a computed prop with dependencies and need to
//               access `this`, then `pure` must be set to `false`.
//   cache     - Set this to true to enable property caching. This is useful with computed
//               properties that have their dependent events defined. If dependent events aren't
//               defined, then the initially cached value will never be cleared.
//
// Returns the receiver.
BasisObject.prop = function(name, opts = {}) {
  defineProp(this.prototype, name, opts);
  return this;
};

// Public: Creates multiple props at once from the given object.
//
// props - An object mapping prop names to their options. See `Basis.Object#prop` for the available
//         options.
//
// Returns the receiver.
BasisObject.props = function(props) {
  for (let name in props) { this.prop(name, props[name]); }
  return this;
};

// Public: Returns a string containing the class's name.
BasisObject.toString = function() { return this.displayName || this.name || '(Unknown)'; };

// Public: The `Basis.Object` initializer. Sets the given props and begins observing dependent
// property events. This method should never be called directly, its called by the `Basis.Object`
// constructor function.
//
// props - An object containing properties to set. Only properties defined via `Basis.Object.prop`
//         are settable.
BasisObject.prototype.init = function(props) { if (props) { this.set(props); } };

// Public: Defines an observable property directly on the receiver. See the `Basis.Object.prop`
// method for available options.
BasisObject.prototype.prop = function(name, opts = {}) {
  defineProp(this, name, opts);
  return this;
};

// Public: Creates multiple props at once from the given object directly on the receiver.
BasisObject.prototype.props = BasisObject.props;

// Public: Set multiple props at once. This method take special care to only set props that are
// defined with `Basis.Object.prop` and are not readonly.
//
// props - An object containing props to set.
//
// Returns nothing.
BasisObject.prototype.set = function(props) {
  for (let k in props) {
    if (this.__props__ && this.__props__[k] && !this.__props__[k].readonly) {
      this[k] = props[k];
    }
  }
};

// Public: Register a callback to be invoked when the given prop changes. Callbacks are invoked
// asynchronously whenever the property is changed directly or one of its dependent properties is
// changed.
//
// prop     - The name of the property to observe.
// callback - A function to invoke when the prop changes.
//
// Returns the receiver.
BasisObject.prototype.on = function(prop, callback) {
  this.__observers__ = this.__observers__ || {};
  this.__observers__[prop] = this.__observers__[prop] || [];
  this.__observers__[prop].push(callback);
  return this;
};

// Public: Remove a prop observer.
//
// prop     - The name of the property to stop observing.
// callback - The function passed to `Basis.Object#on`.
//
// Returns the receiver.
BasisObject.prototype.off = function(prop, callback) {
  if (this.__observers__ && this.__observers__[prop]) {
    for (let i = this.__observers__[prop].length - 1; i >= 0; i--) {
      if (this.__observers__[prop][i] === callback) {
        this.__observers__[prop].splice(i, 1);
      }
    }
  }
  return this;
};

// Public: Notifies observers of an event. Observers are invoked with the name of the event and
// any additional arguments passed to `notify`.
//
// event   - A string containing the name of the event.
// ...args - Zero or more additional arguments to pass along to observers.
//
// Returns the receiver.
BasisObject.prototype.notify = function(event, ...args) {
  if (this.__observers__ && this.__observers__[event]) {
    for (let i = 0, n = this.__observers__[event].length; i < n; i++) {
      if (this.__observers__[event][i]) {
        try {
          this.__observers__[event][i](event, ...args);
        }
        catch (e) {
          console.error('Basis.Object#notify: exception caught in observer:', e);
        }
      }
    }
  }

  return this;
};

// Public: Registers a property change and asynchronously triggers property observers. This is
// called automatically when a prop is set. This should only be used when the state of a prop is
// managed by external code.
//
// name - The name of the prop that has changed.
//
// Returns the receiver.
BasisObject.prototype.didChange = function(name) {
  (this.__changedProps__ = this.__changedProps__ || {})[name] = true;
  changedObjects[this.objectId] = this;

  // FIXME: The double setTimeout here is to work around an issue we've been seeing in Firefox
  // with promises. In Chrome the flush is getting run after promises callbacks are invoked but
  // in firefox its running before which is causing some timing issues. The double setTimeout
  // ensures that the flush gets run after promise callbacks.
  if (!flushTimer) { flushTimer = setTimeout(function() { setTimeout(flush); }); }

  return this;
};

// Public: Returns a string representation of the object.
BasisObject.prototype.toString = function() {
  return `#<${this.constructor}:${this.objectId}>`;
};

// Public: Indicates whether the receiver is equal to the given object. The default implementation
// simply does an identity comparison using the `===` operator. You'll likely want to override
// this method in your sub-types in order to perform a more meaningful comparison.
//
// o - An object to compare against the receiver.
//
// Returns a `true` if the objects are equal and `false` otherwise.
BasisObject.prototype.eq = function(other) { return this === other; };

// Public: Resolves the given path into a value, relative to the receiver. See `util.getPath`.
BasisObject.prototype.getPath = function(path) { return util.getPath(this, path); };

// Internal: Returns the current value of the given property or the default value if it is not
// defined.
//
// Returns the value of the property.
// Throws `Error` if there is no property with the given name.
BasisObject.prototype._getProp = function(name) {
  var desc = this.__props__ && this.__props__[name], value;

  if (!desc) {
    throw new Error(`Basis.Object#_getProp: unknown prop name \`${name}\``);
  }

  if (desc.cache && isCached.call(this, name)) { return getCached.call(this, name); }

  if (desc.get && desc.pure) {
    value = desc.get.apply(null, desc.on.map((path) => this.getPath(path)));
  }
  else if (desc.get) {
    value = desc.get.call(this);
  }
  else {
    value = this[`__${name}`];
  }

  value = (value === undefined) ? desc.default : value;

  if (desc.cache) { cache.call(this, name, value); }

  return value;
};

// Internal: Sets the value of the given property.
//
// name - The name of the prop to change.
// value - The new value of the prop.
//
// Returns the previous value.
// Throws `Error` if there is no property with the given name.
// Throws `TypeError` if the property is readonly.
BasisObject.prototype._setProp = function(name, value) {
  var descriptor = this.__props__ && this.__props__[name],
      key        = `__${name}`,
      old        = this._getProp(name);

  if (!descriptor) {
    throw new Error(`Basis.Object#_setProp: unknown prop name \`${name}\``);
  }

  if (descriptor.readonly) {
    throw new TypeError(`Basis.Object#_setProp: cannot set readonly property \`${name}\` of ${this}`);
  }

  if (descriptor.set) { descriptor.set.call(this, value); }
  else { this[key] = value; }

  this.didChange(name);

  return old;
};

// Internal: Registers a proxy object. All prop changes on the receiver will be proxied to the
// given proxy object with the given name as a prefix for the property name.
BasisObject.prototype._registerProxy = function(object, name) {
  this.__proxies__ = this.__proxies__ || {};
  this.__proxies__[`${object.objectId},${name}`] = {object, name};
  return this;
};

// Internal: Deregisters a proxy object previously registered with `#_registerProxy`.
BasisObject.prototype._deregisterProxy = function(object, name) {
  if (this.__proxies__) { delete this.__proxies__[`${object.objectId},${name}`]; }
  return this;
};

BasisObject.displayName = 'Basis.Object';

export default BasisObject;
<|endoftext|>


<commit_before>var m = require('mithril');
var chessground = require('chessground');
var partial = chessground.util.partial;
var ground = require('./ground');
var xhr = require('./xhr');

var promoting = false;

function start(ctrl, orig, dest, isPremove) {
  var piece = ctrl.chessground.data.pieces[dest];
  if (piece && piece.role == 'pawn' && (
    (dest[1] == 8 && ctrl.data.player.color == 'white') ||
    (dest[1] == 1 && ctrl.data.player.color == 'black'))) {
    if (ctrl.data.pref.autoQueen == 3 || (ctrl.data.pref.autoQueen == 2 && isPremove)) return false;
    m.startComputation();
    promoting = [orig, dest];
    m.endComputation();
    return true;
  }
  return false;
}

function finish(ctrl, role) {
  if (promoting) {
    ground.promote(ctrl.chessground, promoting[1], role);
    ctrl.sendMove(promoting[0], promoting[1], role);
  }
  promoting = false;
}

function cancel(ctrl) {
  if (promoting) xhr.reload(ctrl).then(ctrl.reload);
  promoting = false;
}

module.exports = {

  start: start,

  view: function(ctrl) {
    var pieces = ctrl.data.game.variant.key != "antichess" ?
     ['queen', 'knight', 'rook', 'bishop'] : ['queen', 'knight', 'rook', 'bishop', 'king']

    return promoting ? m('div#promotion_choice', {
      onclick: partial(cancel, ctrl)
    }, pieces.map(function(serverRole) {

      return m('div.cg-piece.' + serverRole + '.' + ctrl.data.player.color, {
        onclick: function(e) {
          e.stopPropagation();
          finish(ctrl, serverRole);
        }
      });
    })) : null
  }
};
<commit_msg>tweak round promotion view<commit_after>var m = require('mithril');
var chessground = require('chessground');
var partial = chessground.util.partial;
var ground = require('./ground');
var xhr = require('./xhr');

var promoting = false;

function start(ctrl, orig, dest, isPremove) {
  var piece = ctrl.chessground.data.pieces[dest];
  if (piece && piece.role == 'pawn' && (
    (dest[1] == 8 && ctrl.data.player.color == 'white') ||
    (dest[1] == 1 && ctrl.data.player.color == 'black'))) {
    if (ctrl.data.pref.autoQueen == 3 || (ctrl.data.pref.autoQueen == 2 && isPremove)) return false;
    m.startComputation();
    promoting = [orig, dest];
    m.endComputation();
    return true;
  }
  return false;
}

function finish(ctrl, role) {
  if (promoting) {
    ground.promote(ctrl.chessground, promoting[1], role);
    ctrl.sendMove(promoting[0], promoting[1], role);
  }
  promoting = false;
}

function cancel(ctrl) {
  if (promoting) xhr.reload(ctrl).then(ctrl.reload);
  promoting = false;
}

module.exports = {

  start: start,

  view: function(ctrl) {
    if (!promoting) return;
    var pieces = ['queen', 'knight', 'rook', 'bishop'];
    if (ctrl.data.game.variant.key === "antichess") pieces.push('king');
    return m('div#promotion_choice', {
      onclick: partial(cancel, ctrl)
    }, pieces.map(function(serverRole) {

      return m('div.cg-piece.' + serverRole + '.' + ctrl.data.player.color, {
        onclick: function(e) {
          e.stopPropagation();
          finish(ctrl, serverRole);
        }
      });
    }));
  }
};
<|endoftext|>