<commit_before>let DOMImage, Canvas, getImageData, getCanvasArray, isDifferentOrigin, env;

if (typeof self !== 'undefined') { // Browser

    env = 'browser';
    let origin = self.location.href;
    isDifferentOrigin = function (url) {
        try {
            let parsedURL = new self.URL(url);
            return parsedURL.origin !== origin;
        } catch (e) {
            // may be a relative URL. In this case, it cannot be parsed but is effectively from same origin
            return false;
        }
    };

    let ImageData = self.ImageData;

    DOMImage = self.Image;
    Canvas = function Canvas(width, height) {
        let canvas = self.document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        return canvas;
    };
    getImageData = function (data, width, height) {
        let validData = data;
        // TODO for now we always copy the array because we don't know if it can be modified after we put it in the canvas
        //if (data.constructor.name !== 'Uint8ClampedArray') {
        validData = new Uint8ClampedArray(data.length);
        for (let i = 0; i < data.length; i++) {
            validData[i] = data[i];
        }
        //}
        return new ImageData(validData, width, height);
    };
    getCanvasArray = function (width, height) {
        return new Uint8ClampedArray(width * height * 4);
    };

} else if (typeof module !== 'undefined' && module.exports) { // Node.js

    env = 'node';
    isDifferentOrigin = function (url) {
        return false;
    };

    let canvas = require('canvas');
    let ImageData = require('canvas/lib/bindings').ImageData;

    DOMImage = canvas.Image;
    Canvas = canvas;
    getImageData = function (data, width, height) {
        let validData = data;
        if (!(data instanceof canvas.PixelArray)) {
            validData = new canvas.PixelArray(width, height);
            for (let i = 0; i < data.length; i++) {
                validData[i] = data[i];
            }
        }
        return new ImageData(validData, width, height);
    };
    getCanvasArray = function (width, height) {
        return new canvas.PixelArray(width, height);
    };
}

export {DOMImage, Canvas, getImageData, getCanvasArray, isDifferentOrigin, env};
<commit_msg>fix cross-origin check<commit_after>let DOMImage, Canvas, getImageData, getCanvasArray, isDifferentOrigin, env;

if (typeof self !== 'undefined') { // Browser

    env = 'browser';
    let origin = self.location.origin;
    isDifferentOrigin = function (url) {
        try {
            let parsedURL = new self.URL(url);
            return parsedURL.origin !== origin;
        } catch (e) {
            // may be a relative URL. In this case, it cannot be parsed but is effectively from same origin
            return false;
        }
    };

    let ImageData = self.ImageData;

    DOMImage = self.Image;
    Canvas = function Canvas(width, height) {
        let canvas = self.document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        return canvas;
    };
    getImageData = function (data, width, height) {
        let validData = data;
        // TODO for now we always copy the array because we don't know if it can be modified after we put it in the canvas
        //if (data.constructor.name !== 'Uint8ClampedArray') {
        validData = new Uint8ClampedArray(data.length);
        for (let i = 0; i < data.length; i++) {
            validData[i] = data[i];
        }
        //}
        return new ImageData(validData, width, height);
    };
    getCanvasArray = function (width, height) {
        return new Uint8ClampedArray(width * height * 4);
    };

} else if (typeof module !== 'undefined' && module.exports) { // Node.js

    env = 'node';
    isDifferentOrigin = function (url) {
        return false;
    };

    let canvas = require('canvas');
    let ImageData = require('canvas/lib/bindings').ImageData;

    DOMImage = canvas.Image;
    Canvas = canvas;
    getImageData = function (data, width, height) {
        let validData = data;
        if (!(data instanceof canvas.PixelArray)) {
            validData = new canvas.PixelArray(width, height);
            for (let i = 0; i < data.length; i++) {
                validData[i] = data[i];
            }
        }
        return new ImageData(validData, width, height);
    };
    getCanvasArray = function (width, height) {
        return new canvas.PixelArray(width, height);
    };
}

export {DOMImage, Canvas, getImageData, getCanvasArray, isDifferentOrigin, env};
<|endoftext|>


<commit_before>// HumanizeDuration.js - http://git.io/j0HgmQ

(function() {

  var UNITS = {
    year: 31557600000,
    month: 2629800000,
    week: 604800000,
    day: 86400000,
    hour: 3600000,
    minute: 60000,
    second: 1000,
    millisecond: 1
  };

  var languages = {
    ca: {
      year: function(c) { return "any" + ((c !== 1) ? "s" : ""); },
      month: function(c) { return "mes" + ((c !== 1) ? "os" : ""); },
      week: function(c) { return "setman" + ((c !== 1) ? "es" :"a"); },
      day: function(c) { return "di" + ((c !== 1) ? "es" :"a"); },
      hour: function(c) { return "hor" + ((c !== 1) ? "es" :"a"); },
      minute: function(c) { return "minut" + ((c !== 1) ? "s" : ""); },
      second: function(c) { return "segon" + ((c !== 1) ? "s" : ""); },
      millisecond: function(c) { return "milisegon" + ((c !== 1) ? "s" : "" ); }
    },
    da: {
      year: "år",
      month: function(c) { return "måned" + ((c !== 1) ? "er" : ""); },
      week: function(c) { return "uge" + ((c !== 1) ? "r" : ""); },
      day: function(c) { return "dag" + ((c !== 1) ? "e" : ""); },
      hour: function(c) { return "time" + ((c !== 1) ? "r" : ""); },
      minute: function(c) { return "minut" + ((c !== 1) ? "ter" : ""); },
      second: function(c) { return "sekund" + ((c !== 1) ? "er" : ""); },
      millisecond: function(c) { return "millisekund" + ((c !== 1) ? "er" : ""); }
    },
    de: {
      year: function(c) { return "jahr" + ((c !== 1) ? "e" : ""); },
      month: function(c) { return "monat" + ((c !== 1) ? "e" : ""); },
      week: function(c) { return "woche" + ((c !== 1) ? "n" : ""); },
      day: function(c) { return "tag" + ((c !== 1) ? "e" : ""); },
      hour: function(c) { return "stunde" + ((c !== 1) ? "n" : ""); },
      minute: function(c) { return "minute" + ((c !== 1) ? "n" : ""); },
      second: function(c) { return "sekunde" + ((c !== 1) ? "n" : ""); },
      millisecond: function(c) { return "millisekunde" + ((c !== 1) ? "n" : ""); }
    },
    en: {
      year: function(c) { return "year" + ((c !== 1) ? "s" : ""); },
      month: function(c) { return "month" + ((c !== 1) ? "s" : ""); },
      week: function(c) { return "week" + ((c !== 1) ? "s" : ""); },
      day: function(c) { return "day" + ((c !== 1) ? "s" : ""); },
      hour: function(c) { return "hour" + ((c !== 1) ? "s" : ""); },
      minute: function(c) { return "minute" + ((c !== 1) ? "s" : ""); },
      second: function(c) { return "second" + ((c !== 1) ? "s" : ""); },
      millisecond: function(c) { return "millisecond" + ((c !== 1) ? "s" : ""); }
    },
    nl: {
      year: "jaar",
      month: function(c) { return (c === 1) ? "maand" : "maanden"; },
      week: function(c) { return (c === 1) ? "week" : "weken"; },
      day: function(c) { return (c === 1) ? "dag" : "dagen"; },
      hour: "uur",
      minute: function(c) { return (c === 1) ? "minuut" : "minuten"; },
      second: function(c) { return (c === 1) ? "seconde" : "seconden"; },
      millisecond: function(c) { return (c === 1) ? "milliseconde" : "milliseconden"; }
    },
    es: {
      year: function(c) { return "año" + ((c !== 1) ? "s" : ""); },
      month: function(c) { return "mes" + ((c !== 1) ? "es" : ""); },
      week: function(c) { return "semana" + ((c !== 1) ? "s" : ""); },
      day: function(c) { return "día" + ((c !== 1) ? "s" : ""); },
      hour: function(c) { return "hora" + ((c !== 1) ? "s" : ""); },
      minute: function(c) { return "minuto" + ((c !== 1) ? "s" : ""); },
      second: function(c) { return "segundo" + ((c !== 1) ? "s" : ""); },
      millisecond: function(c) { return "milisegundo" + ((c !== 1) ? "s" : "" ); }
    },
    fr: {
      year: function(c) { return "an" + ((c !== 1) ? "s" : ""); },
      month: "mois",
      week: function(c) { return "semaine" + ((c !== 1) ? "s" : ""); },
      day: function(c) { return "jour" + ((c !== 1) ? "s" : ""); },
      hour: function(c) { return "heure" + ((c !== 1) ? "s" : ""); },
      minute: function(c) { return "minute" + ((c !== 1) ? "s" : ""); },
      second: function(c) { return "seconde" + ((c !== 1) ? "s" : ""); },
      millisecond: function(c) { return "milliseconde" + ((c !== 1) ? "s" : ""); }
    },
    ko: {
      year: "년",
      month: "개월",
      week: "주일",
      day: "일",
      hour: "시간",
      minute: "분",
      second: "초",
      millisecond: "밀리 초"
    },
    nob: {
      year: "år",
      month: function(c) { return "måned" + ((c !== 1) ? "er" : ""); },
      week: function(c) { return "uke" + ((c !== 1) ? "r" : ""); },
      day: function(c) { return "dag" + ((c !== 1) ? "er" : ""); },
      hour: function(c) { return "time" + ((c !== 1) ? "r" : ""); },
      minute: function(c) { return "minutt" + ((c !== 1) ? "er" : ""); },
      second: function(c) { return "sekund" + ((c !== 1) ? "er" : ""); },
      millisecond: function(c) { return "millisekund" + ((c !== 1) ? "er" : ""); }
    },
    pl: {
      year: function(c) { return ["rok", "roku", "lata", "lat"][getPolishForm(c)]; },
      month: function(c) { return ["miesiąc", "miesiąca", "miesiące", "miesięcy"][getPolishForm(c)]; },
      week: function(c) { return ["tydzień", "tygodnia", "tygodnie", "tygodni"][getPolishForm(c)]; },
      day: function(c) { return ["dzień", "dnia", "dni", "dni"][getPolishForm(c)]; },
      hour: function(c) { return ["godzina", "godziny", "godziny", "godzin"][getPolishForm(c)]; },
      minute: function(c) { return ["minuta", "minuty", "minuty", "minut"][getPolishForm(c)]; },
      second: function(c) { return ["sekunda", "sekundy", "sekundy", "sekund"][getPolishForm(c)]; },
      millisecond: function(c) { return ["milisekunda", "milisekundy", "milisekundy", "milisekund"][getPolishForm(c)]; }
    },
    pt: {
      year: function(c) { return "ano" + ((c !== 1) ? "s" : ""); },
      month: function(c) { return (c !== 1) ? "meses" : "mês"; },
      week: function(c) { return "semana" + ((c !== 1) ? "s" : ""); },
      day: function(c) { return "dia" + ((c !== 1) ? "s" : ""); },
      hour: function(c) { return "hora" + ((c !== 1) ? "s" : ""); },
      minute: function(c) { return "minuto" + ((c !== 1) ? "s" : ""); },
      second: function(c) { return "segundo" + ((c !== 1) ? "s" : ""); },
      millisecond: function(c) { return "milissegundo" + ((c !== 1) ? "s" : ""); }
    },
    ru: {
      year: function(c) { return ["лет", "год", "года"][getRussianForm(c)]; },
      month: function(c) { return ["месяцев", "месяц", "месяца"][getRussianForm(c)]; },
      week: function(c) { return ["недель", "неделя", "недели"][getRussianForm(c)]; },
      day: function(c) { return ["дней", "день", "дня"][getRussianForm(c)]; },
      hour: function(c) { return ["часов", "час", "часа"][getRussianForm(c)]; },
      minute: function(c) { return ["минут", "минута", "минуты"][getRussianForm(c)]; },
      second: function(c) { return ["секунд", "секунда", "секунды"][getRussianForm(c)]; },
      millisecond: function(c) { return ["миллисекунд", "миллисекунда", "миллисекунды"][getRussianForm(c)]; }
    },
    tr: {
      year: "yıl",
      month: "ay",
      week: "hafta",
      day: "gün",
      hour: "saat",
      minute: "dakika",
      second: "saniye",
      millisecond: "milisaniye"
    },
    "zh-CN": {
      year: "年",
      month: "个月",
      week: "周",
      day: "天",
      hour: "小时",
      minute: "分钟",
      second: "秒",
      millisecond: "毫秒"
    },
    "zh-TW": {
      year: "年",
      month: "個月",
      week: "周",
      day: "天",
      hour: "小時",
      minute: "分鐘",
      second: "秒",
      millisecond: "毫秒"
    },
    ja: {
      year: "年",
      month: "月",
      week: "週",
      day: "日",
      hour: "時間",
      minute: "分",
      second: "秒",
      millisecond: "ミリ秒"
    }
  };

  // You can create a humanizer, which returns a function with defaults
  // parameters.
  function humanizer(passedOptions) {

    var result = function humanizer(ms, passedOptions) {
      var options = extend({}, result, passedOptions || {});
      return doHumanization(ms, options);
    };

    extend(result, {
      language: "en",
      delimiter: ", ",
      units: [
        "year",
        "month",
        "week",
        "day",
        "hour",
        "minute",
        "second"
      ],
      languages: {}
    }, passedOptions);

    return result;

  }

  // The main function is just a wrapper around a default humanizer.
  var defaultHumanizer = humanizer({});
  function humanizeDuration(ms, passedOptions) {
    return defaultHumanizer(ms, passedOptions);
  }

  // doHumanization does the bulk of the work.
  function doHumanization(ms, options) {

    // Make sure we have a positive number.
    // Has the nice sideffect of turning Number objects into primitives.
    ms = Math.abs(ms);

    if (ms === 0) {
      return "0";
    }

    var dictionary = options.languages[options.language] || languages[options.language];
    if (!dictionary) {
      throw new Error("No language " + dictionary + ".");
    }

    var result = [];

    // Start at the top and keep removing units, bit by bit.
    var unitName, unitMS, unitCount, mightBeHalfUnit;
    for (var i = 0, len = options.units.length; i < len; i ++) {

      unitName = options.units[i];
      if (unitName[unitName.length - 1] === "s") { // strip plurals
        unitName = unitName.substring(0, unitName.length - 1);
      }
      unitMS = UNITS[unitName];

      // If it's a half-unit interval, we're done.
      if (result.length === 0) {
        mightBeHalfUnit = (ms / unitMS) * 2;
        if (mightBeHalfUnit === Math.floor(mightBeHalfUnit)) {
          return render(mightBeHalfUnit / 2, unitName, dictionary);
        }
      }

      // What's the number of full units we can fit?
      if ((i + 1) === len) {
        unitCount = ms / unitMS;
      } else {
        unitCount = Math.floor(ms / unitMS);
      }

      // Add the string.
      if (unitCount) {
        result.push(render(unitCount, unitName, dictionary));
      }

      // Remove what we just figured out.
      ms -= unitCount * unitMS;

    }

    return result.join(options.delimiter);

  }

  humanizeDuration.humanizer = humanizer;
  if (typeof module !== "undefined") {
    module.exports = humanizeDuration;
  } else {
    this.humanizeDuration = humanizeDuration;
  }

  function render(count, type, dictionary) {
    var dictionaryValue = dictionary[type];
    var word;
    if (typeof dictionaryValue === "function") {
      word = dictionaryValue(count);
    } else {
      word = dictionaryValue;
    }
    return count + " " + word;
  }

  function extend(destination) {
    var source;
    for (var i = 1; i < arguments.length; i ++) {
      source = arguments[i];
      for (var prop in source) {
        destination[prop] = source[prop];
      }
    }
    return destination;
  }

  // Internal helper function for Polish language.
  function getPolishForm(c) {
    if (c === 1) {
      return 0;
    } else if (Math.floor(c) !== c) {
      return 1;
    } else if (2 <= c % 10 && c % 10 <= 4 && !(10 < c % 100 && c % 100 < 20)) {
      return 2;
    } else {
      return 3;
    }
  }

  // Internal helper function for Russian language.
  function getRussianForm(c) {
    if (Math.floor(c) !== c) {
      return 2;
    } else if (c === 0 || (c >= 5 && c <= 20) || (c % 10 >= 5 && c % 10 <= 9) || (c % 10 === 0) ) {
      return 0;
    } else if (c === 1 || c % 10 === 1) {
      return 1;
    } else if (c > 1) {
      return 2;
    } else {
      return 0;
    }
  }

})();
<commit_msg>Minor: shorten several lines to one<commit_after>// HumanizeDuration.js - http://git.io/j0HgmQ

(function() {

  var UNITS = {
    year: 31557600000,
    month: 2629800000,
    week: 604800000,
    day: 86400000,
    hour: 3600000,
    minute: 60000,
    second: 1000,
    millisecond: 1
  };

  var languages = {
    ca: {
      year: function(c) { return "any" + ((c !== 1) ? "s" : ""); },
      month: function(c) { return "mes" + ((c !== 1) ? "os" : ""); },
      week: function(c) { return "setman" + ((c !== 1) ? "es" :"a"); },
      day: function(c) { return "di" + ((c !== 1) ? "es" :"a"); },
      hour: function(c) { return "hor" + ((c !== 1) ? "es" :"a"); },
      minute: function(c) { return "minut" + ((c !== 1) ? "s" : ""); },
      second: function(c) { return "segon" + ((c !== 1) ? "s" : ""); },
      millisecond: function(c) { return "milisegon" + ((c !== 1) ? "s" : "" ); }
    },
    da: {
      year: "år",
      month: function(c) { return "måned" + ((c !== 1) ? "er" : ""); },
      week: function(c) { return "uge" + ((c !== 1) ? "r" : ""); },
      day: function(c) { return "dag" + ((c !== 1) ? "e" : ""); },
      hour: function(c) { return "time" + ((c !== 1) ? "r" : ""); },
      minute: function(c) { return "minut" + ((c !== 1) ? "ter" : ""); },
      second: function(c) { return "sekund" + ((c !== 1) ? "er" : ""); },
      millisecond: function(c) { return "millisekund" + ((c !== 1) ? "er" : ""); }
    },
    de: {
      year: function(c) { return "jahr" + ((c !== 1) ? "e" : ""); },
      month: function(c) { return "monat" + ((c !== 1) ? "e" : ""); },
      week: function(c) { return "woche" + ((c !== 1) ? "n" : ""); },
      day: function(c) { return "tag" + ((c !== 1) ? "e" : ""); },
      hour: function(c) { return "stunde" + ((c !== 1) ? "n" : ""); },
      minute: function(c) { return "minute" + ((c !== 1) ? "n" : ""); },
      second: function(c) { return "sekunde" + ((c !== 1) ? "n" : ""); },
      millisecond: function(c) { return "millisekunde" + ((c !== 1) ? "n" : ""); }
    },
    en: {
      year: function(c) { return "year" + ((c !== 1) ? "s" : ""); },
      month: function(c) { return "month" + ((c !== 1) ? "s" : ""); },
      week: function(c) { return "week" + ((c !== 1) ? "s" : ""); },
      day: function(c) { return "day" + ((c !== 1) ? "s" : ""); },
      hour: function(c) { return "hour" + ((c !== 1) ? "s" : ""); },
      minute: function(c) { return "minute" + ((c !== 1) ? "s" : ""); },
      second: function(c) { return "second" + ((c !== 1) ? "s" : ""); },
      millisecond: function(c) { return "millisecond" + ((c !== 1) ? "s" : ""); }
    },
    nl: {
      year: "jaar",
      month: function(c) { return (c === 1) ? "maand" : "maanden"; },
      week: function(c) { return (c === 1) ? "week" : "weken"; },
      day: function(c) { return (c === 1) ? "dag" : "dagen"; },
      hour: "uur",
      minute: function(c) { return (c === 1) ? "minuut" : "minuten"; },
      second: function(c) { return (c === 1) ? "seconde" : "seconden"; },
      millisecond: function(c) { return (c === 1) ? "milliseconde" : "milliseconden"; }
    },
    es: {
      year: function(c) { return "año" + ((c !== 1) ? "s" : ""); },
      month: function(c) { return "mes" + ((c !== 1) ? "es" : ""); },
      week: function(c) { return "semana" + ((c !== 1) ? "s" : ""); },
      day: function(c) { return "día" + ((c !== 1) ? "s" : ""); },
      hour: function(c) { return "hora" + ((c !== 1) ? "s" : ""); },
      minute: function(c) { return "minuto" + ((c !== 1) ? "s" : ""); },
      second: function(c) { return "segundo" + ((c !== 1) ? "s" : ""); },
      millisecond: function(c) { return "milisegundo" + ((c !== 1) ? "s" : "" ); }
    },
    fr: {
      year: function(c) { return "an" + ((c !== 1) ? "s" : ""); },
      month: "mois",
      week: function(c) { return "semaine" + ((c !== 1) ? "s" : ""); },
      day: function(c) { return "jour" + ((c !== 1) ? "s" : ""); },
      hour: function(c) { return "heure" + ((c !== 1) ? "s" : ""); },
      minute: function(c) { return "minute" + ((c !== 1) ? "s" : ""); },
      second: function(c) { return "seconde" + ((c !== 1) ? "s" : ""); },
      millisecond: function(c) { return "milliseconde" + ((c !== 1) ? "s" : ""); }
    },
    ko: {
      year: "년",
      month: "개월",
      week: "주일",
      day: "일",
      hour: "시간",
      minute: "분",
      second: "초",
      millisecond: "밀리 초"
    },
    nob: {
      year: "år",
      month: function(c) { return "måned" + ((c !== 1) ? "er" : ""); },
      week: function(c) { return "uke" + ((c !== 1) ? "r" : ""); },
      day: function(c) { return "dag" + ((c !== 1) ? "er" : ""); },
      hour: function(c) { return "time" + ((c !== 1) ? "r" : ""); },
      minute: function(c) { return "minutt" + ((c !== 1) ? "er" : ""); },
      second: function(c) { return "sekund" + ((c !== 1) ? "er" : ""); },
      millisecond: function(c) { return "millisekund" + ((c !== 1) ? "er" : ""); }
    },
    pl: {
      year: function(c) { return ["rok", "roku", "lata", "lat"][getPolishForm(c)]; },
      month: function(c) { return ["miesiąc", "miesiąca", "miesiące", "miesięcy"][getPolishForm(c)]; },
      week: function(c) { return ["tydzień", "tygodnia", "tygodnie", "tygodni"][getPolishForm(c)]; },
      day: function(c) { return ["dzień", "dnia", "dni", "dni"][getPolishForm(c)]; },
      hour: function(c) { return ["godzina", "godziny", "godziny", "godzin"][getPolishForm(c)]; },
      minute: function(c) { return ["minuta", "minuty", "minuty", "minut"][getPolishForm(c)]; },
      second: function(c) { return ["sekunda", "sekundy", "sekundy", "sekund"][getPolishForm(c)]; },
      millisecond: function(c) { return ["milisekunda", "milisekundy", "milisekundy", "milisekund"][getPolishForm(c)]; }
    },
    pt: {
      year: function(c) { return "ano" + ((c !== 1) ? "s" : ""); },
      month: function(c) { return (c !== 1) ? "meses" : "mês"; },
      week: function(c) { return "semana" + ((c !== 1) ? "s" : ""); },
      day: function(c) { return "dia" + ((c !== 1) ? "s" : ""); },
      hour: function(c) { return "hora" + ((c !== 1) ? "s" : ""); },
      minute: function(c) { return "minuto" + ((c !== 1) ? "s" : ""); },
      second: function(c) { return "segundo" + ((c !== 1) ? "s" : ""); },
      millisecond: function(c) { return "milissegundo" + ((c !== 1) ? "s" : ""); }
    },
    ru: {
      year: function(c) { return ["лет", "год", "года"][getRussianForm(c)]; },
      month: function(c) { return ["месяцев", "месяц", "месяца"][getRussianForm(c)]; },
      week: function(c) { return ["недель", "неделя", "недели"][getRussianForm(c)]; },
      day: function(c) { return ["дней", "день", "дня"][getRussianForm(c)]; },
      hour: function(c) { return ["часов", "час", "часа"][getRussianForm(c)]; },
      minute: function(c) { return ["минут", "минута", "минуты"][getRussianForm(c)]; },
      second: function(c) { return ["секунд", "секунда", "секунды"][getRussianForm(c)]; },
      millisecond: function(c) { return ["миллисекунд", "миллисекунда", "миллисекунды"][getRussianForm(c)]; }
    },
    tr: {
      year: "yıl",
      month: "ay",
      week: "hafta",
      day: "gün",
      hour: "saat",
      minute: "dakika",
      second: "saniye",
      millisecond: "milisaniye"
    },
    "zh-CN": {
      year: "年",
      month: "个月",
      week: "周",
      day: "天",
      hour: "小时",
      minute: "分钟",
      second: "秒",
      millisecond: "毫秒"
    },
    "zh-TW": {
      year: "年",
      month: "個月",
      week: "周",
      day: "天",
      hour: "小時",
      minute: "分鐘",
      second: "秒",
      millisecond: "毫秒"
    },
    ja: {
      year: "年",
      month: "月",
      week: "週",
      day: "日",
      hour: "時間",
      minute: "分",
      second: "秒",
      millisecond: "ミリ秒"
    }
  };

  // You can create a humanizer, which returns a function with defaults
  // parameters.
  function humanizer(passedOptions) {

    var result = function humanizer(ms, passedOptions) {
      var options = extend({}, result, passedOptions || {});
      return doHumanization(ms, options);
    };

    extend(result, {
      language: "en",
      delimiter: ", ",
      units: ["year", "month", "week", "day", "hour", "minute", "second"],
      languages: {}
    }, passedOptions);

    return result;

  }

  // The main function is just a wrapper around a default humanizer.
  var defaultHumanizer = humanizer({});
  function humanizeDuration(ms, passedOptions) {
    return defaultHumanizer(ms, passedOptions);
  }

  // doHumanization does the bulk of the work.
  function doHumanization(ms, options) {

    // Make sure we have a positive number.
    // Has the nice sideffect of turning Number objects into primitives.
    ms = Math.abs(ms);

    if (ms === 0) {
      return "0";
    }

    var dictionary = options.languages[options.language] || languages[options.language];
    if (!dictionary) {
      throw new Error("No language " + dictionary + ".");
    }

    var result = [];

    // Start at the top and keep removing units, bit by bit.
    var unitName, unitMS, unitCount, mightBeHalfUnit;
    for (var i = 0, len = options.units.length; i < len; i ++) {

      unitName = options.units[i];
      if (unitName[unitName.length - 1] === "s") { // strip plurals
        unitName = unitName.substring(0, unitName.length - 1);
      }
      unitMS = UNITS[unitName];

      // If it's a half-unit interval, we're done.
      if (result.length === 0) {
        mightBeHalfUnit = (ms / unitMS) * 2;
        if (mightBeHalfUnit === Math.floor(mightBeHalfUnit)) {
          return render(mightBeHalfUnit / 2, unitName, dictionary);
        }
      }

      // What's the number of full units we can fit?
      if ((i + 1) === len) {
        unitCount = ms / unitMS;
      } else {
        unitCount = Math.floor(ms / unitMS);
      }

      // Add the string.
      if (unitCount) {
        result.push(render(unitCount, unitName, dictionary));
      }

      // Remove what we just figured out.
      ms -= unitCount * unitMS;

    }

    return result.join(options.delimiter);

  }

  humanizeDuration.humanizer = humanizer;
  if (typeof module !== "undefined") {
    module.exports = humanizeDuration;
  } else {
    this.humanizeDuration = humanizeDuration;
  }

  function render(count, type, dictionary) {
    var dictionaryValue = dictionary[type];
    var word;
    if (typeof dictionaryValue === "function") {
      word = dictionaryValue(count);
    } else {
      word = dictionaryValue;
    }
    return count + " " + word;
  }

  function extend(destination) {
    var source;
    for (var i = 1; i < arguments.length; i ++) {
      source = arguments[i];
      for (var prop in source) {
        destination[prop] = source[prop];
      }
    }
    return destination;
  }

  // Internal helper function for Polish language.
  function getPolishForm(c) {
    if (c === 1) {
      return 0;
    } else if (Math.floor(c) !== c) {
      return 1;
    } else if (2 <= c % 10 && c % 10 <= 4 && !(10 < c % 100 && c % 100 < 20)) {
      return 2;
    } else {
      return 3;
    }
  }

  // Internal helper function for Russian language.
  function getRussianForm(c) {
    if (Math.floor(c) !== c) {
      return 2;
    } else if (c === 0 || (c >= 5 && c <= 20) || (c % 10 >= 5 && c % 10 <= 9) || (c % 10 === 0) ) {
      return 0;
    } else if (c === 1 || c % 10 === 1) {
      return 1;
    } else if (c > 1) {
      return 2;
    } else {
      return 0;
    }
  }

})();
<|endoftext|>


<commit_before>/**
 * @fileoverview Executes find and sends the results through a stream.
 */
let Readable = require('stream').Readable;
let exec = require('mz/child_process').exec;
let exists = require('fs').existsSync;
let inherits = require('util').inherits;
let path = require('path');

let defaultOptions = Object.freeze({ exclude: [] });

/**
 * Options:
 *
 *   (Array) exclude - directories to omit from fs scan.
 */
function FindStream(dir, options={}) {
  Readable.call(this, { objectMode: true });
  options = Object.assign({}, defaultOptions, options);
  this.buffer = null;
  this.dir = dir;
  if (!path.isAbsolute(this.dir)) {
    this.dir = path.resolve(process.cwd(), this.dir);
  }

  let cmd = 'find . -type d';
  let exclude = options.exclude;
  if (Array.isArray(exclude) && exclude.length) {
    cmd += formatFindPrune(exclude);
  }

  exec(cmd, {
    cwd: this.dir,
    maxBuffer: 8 * Math.pow(10, 6)  // 8M
  })
  .then(result => {
    let stdout = result[0];
    this.buffer = stdout.split(/(\n|\r)/)
      .filter(line => !/^\s*$/.test(line))
      .map(line => path.resolve(this.dir, line))
      .filter(dir => exists(`${dir}/configure.js`))
      .map(dir => `${dir}/configure.js`);
  });
}
inherits(FindStream, Readable);
module.exports = FindStream;

FindStream.prototype._read = function() {
  if (!this.buffer) {
    // We haven't read anything yet so we should poll... ew.
    return setTimeout(this._read.bind(this), 10);
  }

  if (this.buffer.length) {
    let next = path.normalize(path.resolve(this.dir, this.buffer.shift()));
    return this.push(next);
  }

  // All done.
  this.push(null);
};

function formatFindPrune(ignore) {
  return ' \\\( ' +
         ignore.map(dir => `-name ${dir}`).join(' -o ') +
         ' \\\) -prune -o -print';
}
<commit_msg>Cleanup FindStream impl<commit_after>/**
 * @fileoverview Executes find and sends the results through a stream.
 */
let Readable = require('stream').Readable;
let exec = require('mz/child_process').exec;
let exists = require('fs').existsSync;
let inherits = require('util').inherits;
let path = require('path');

let defaultOptions = Object.freeze({ exclude: [] });

/**
 * Options:
 *
 *   (Array) exclude - directories to omit from fs scan.
 */
function FindStream(dir, options={}) {
  Readable.call(this, { objectMode: true });
  options = Object.assign({}, defaultOptions, options);

  this.buffer = [];
  this.finished = false;

  if (path.isAbsolute(dir)) {
    this.dir = dir;
  } else {
    this.dir = path.resolve(process.cwd(), dir);
  }

  let exclude = options.exclude;
  let cmd;
  if (!exclude.length) {
    cmd = 'find . -type d';
  } else {
    cmd = 'find . -type d \\( ' +
          exclude.map(dir => `-name ${dir}`).join(' -o ') +
          ' \\) -prune -o -print';
  }

  exec(cmd, { cwd: this.dir, maxBuffer: 8 * Math.pow(10, 6) }).then(result => {
    let stdout = result[0];
    let dirs = stdout.split(/(\n|\r)/).filter(line => !/^\s*$/.test(line));
    let files = dirs
      .map(dir => path.resolve(this.dir, dir, 'configure.js'))
      .filter(exists);
    files.forEach(file => this.buffer.push(file));
    this.finished = true;
  });
}
inherits(FindStream, Readable);
module.exports = FindStream;

FindStream.prototype._read = function() {
  if (this.buffer.length) {
    return this.push(this.buffer.shift());
  }

  if (this.finished && !this.buffer.length) {
    this.push(null);
  }

  // We haven't read anything yet so we should poll... ew.
  return setTimeout(this._read.bind(this), 10);
};
<|endoftext|>


<commit_before>var InternalCallTree = require('./src/decoder/internalCallTree')
var SolidityProxy = require('./src/decoder/solidityProxy')
var localDecoder = require('./src/decoder/localDecoder')
var stateDecoder = require('./src/decoder/stateDecoder')
var CodeAnalysis = require('./src/analysis/staticAnalysisRunner')
var Compiler = require('./src/compiler/compiler')
var CompilerInput = require('./src/compiler/compiler-input')

module.exports = {
  InternalCallTree: InternalCallTree,
  SolidityProxy: SolidityProxy,
  localDecoder: localDecoder,
  stateDecoder: stateDecoder,
  CodeAnalysis: CodeAnalysis,
  Compiler: Compiler,
  CompilerInput: CompilerInput
}
<commit_msg>remove unneeded imports from remix-solidity<commit_after>var CodeAnalysis = require('./src/analysis/staticAnalysisRunner')
var Compiler = require('./src/compiler/compiler')
var CompilerInput = require('./src/compiler/compiler-input')

module.exports = {
  CodeAnalysis: CodeAnalysis,
  Compiler: Compiler,
  CompilerInput: CompilerInput
}
<|endoftext|>


<commit_before>var JCSDLGui = function($container, config) {
	var self = this;

	this.$container = $($container); // ensure jQuery object
	if (this.$container.length == 0) return false; // break if no such element in DOM

	/** @var {JCSDL} The actual JCSDL "parser". */
	var jcsdl = new JCSDL(this);

	this.config = $.extend(true, {
		animationSpeed : 200,
		onSave : function(code) {console.log(code);}
	}, config);

	this.$editor = $();

	this.logic = 'AND';
	this.filters = [];

	/*
	this.$currentFilterView = $();
	this.filterSteps = [];

	// current choice data
	this.currentFilterTarget = null;
	this.currentFilterFieldsPath = [];
	*/

	// all templates are defined elsewhere
	this.templates = JCSDLGuiTemplates;

	/*
	 * INITIALIZE THE EDITOR
	 */
	/**
	 * Initializes (or reinitializes) the JCSDL GUI Editor.
	 */
	this.init = function() {
		// reset everything in case this is a reinitialization
		self.logic = 'AND';
		self.filters = [];

		//self.$currentFilterView = $();
		//self.filterSteps = [];
		//self.currentFilterTarget = null;
		//self.currentFilterFieldsPath = [];

		// and insert the editor into the container
		self.$editor = self.getTemplate('editor');
		self.$container.html(self.$editor);

		self.$editor.find('.filters-logic input[name="logic"]').change(function(ev) {
			self.logic = $(this).val();
		});

		self.$editor.find('.jcsdl-editor-save').bind('click.jcsdl', function(ev) {
			self.returnJCSDL();
		});
	};

	/**
	 * Loads the given JCSDL string and builds the whole editor based on it.
	 * @param  {String} code JCSDL string.
	 */
	this.loadJCSDL = function(code) {
		this.init(); // just in case we're loading JCSDL for a 2nd time

		var parsed = jcsdl.parseJCSDL(code);
		if (parsed === false) {
			self.showError('Invalid JCSDL input!', code);
			return;
		}

		self.logic = (parsed.logic == 'AND') ? 'AND' : 'OR';
		self.filters = parsed.filters;

		// mark the logic
		self.$editor.find('.filters-logic input[name="logic"][value="' + self.logic + '"]').click();

		// display the filters
		// target the filters list
		var $filtersList = self.$editor.find('.filters-list');

		// add each filter to the list
		$.each(self.filters, function(i, filter) {
			var $filterRow = createFilterRow(filter);
			$filterRow.appendTo($filtersList);
		});

		/*
		// select target
		self.$currentFilterView.find('select[name="target"]').val(filter.target);
		self.didSelectTarget(filter.target);

		// select all fields and subfields
		$.each(filter.fieldPath, function(i, field) {
			var $fieldView = self.$currentFilterView.find('.filter-target-field:last');
			$fieldView.find('select[name="field[]"]').val(field);
			self.didSelectField(field, $fieldView);
		});

		// select operator
		self.$currentFilterView.find('#operator-' + filter.operator).click();

		// fill in the value
		self.$currentFilterView.find('#filter-value-input-field input').val(filter.value);
		*/
	};

	/**
	 * Returns the JCSDL
	 * @return {[type]}
	 */
	this.returnJCSDL = function() {
		var code = jcsdl.getCSDLFromFilters(self.filters, self.logic);
		self.config.onSave.apply(self, [code]);
	};

	/**
	 * Initializes the filter editor.
	 */
	this.initFilterEditor = function() {
		// prepare the filter editor
		self.$currentFilterView = self.getTemplate('filter');

		// by default always prepare the selection of targets first
		var $targetSelectView = self.getTemplate('target');
		var $targetSelect = $targetSelectView.find('select');

		// create a select option for every possible target
		$.each(JCSDLConfig.targets, function(name, target) {
			// delegate creation of the option to a function
			var $targetView = createOptionForTarget(name, target);

			// append the option to the select
			$targetView.appendTo($targetSelect);
		});

		// register listener for change event
		$targetSelect.change(function(ev) {
			self.didSelectTarget($(this).val());
		});

		// append the select view to the filter view
		this.addFilterStep('target', $targetSelectView);

		// and finally add the editor to the DOM
		self.$container.html(self.$currentFilterView);
	};

	/* ##########################
	 * FILTER EDITOR BUILDING
	 * ########################## */
	/**
	 * Adds a filter step with proper numbering/position.
	 * @param {String} stepName Name of the step.
	 * @param {jQuery} $view    View of the step.
	 */
	this.addFilterStep = function(stepName, $view) {
		var $stepView = self.getTemplate('step');
		$stepView.html($view);

		var stepNumber = self.filterSteps.length;

		// attach some data and classes (for easy selectors)
		$stepView.data('number', stepNumber);
		$stepView.addClass('filter-step-number-' + stepNumber);
		$stepView.data('name', stepName);
		$stepView.addClass('filter-step-name-' + stepName);

		// add to the DOM
		$stepView.appendTo(self.$currentFilterView);
		$stepView.hide().fadeIn(self.config.animationSpeed);

		// add to the steps pool
		self.filterSteps.push($stepView);
	};

	/**
	 * Removes all filter steps after the given position.
	 * Also clears the resulting CSDL if there was any before.
	 * @param  {Integer} position
	 */
	this.removeFilterStepsAfterPosition = function(position) {
		var steps = self.filterSteps.splice(position + 1, self.filterSteps.length - position);
		$.each(steps, function(i, $step) {
			$($step).remove(); // ensure jQuery
		});

		$('#filter-csdl').val('');
	};

	/*
	 * EVENT HANDLERS
	 */
	/**
	 * When a target is selected, the user needs to select a field.
	 * @param  {String} targetName Name of the selected target.
	 */
	this.didSelectTarget = function(targetName) {
		if (typeof(JCSDLConfig.targets[targetName]) == 'undefined') return false;

		self.currentFilterTarget = targetName;
		self.currentFilterFieldsPath = [];

		// remove all steps regarding selection of fields in case another target was selected first
		self.removeFilterStepsAfterPosition(0); // target is always position 0

		// now need to select a field, so build the selection view
		var target = JCSDLConfig.targets[targetName];
		var $fieldView = createFieldSelectionView(target.fields);

		self.addFilterStep('field', $fieldView);
	};

	/**
	 * When a field is selected then the user either needs to input a value or select a subfield if such exists.
	 * @param  {String} fieldName  Name of the selected field.
	 * @param  {jQuery} $fieldView The field selection view that was used.
	 */
	this.didSelectField = function(fieldName, $fieldView) {
		// remove any steps that are farther then this one, just in case
		var fieldPosition = $fieldView.data('position');
		self.currentFilterFieldsPath.splice(fieldPosition, self.currentFilterFieldsPath.length - fieldPosition);

		var field = getFieldInfoAtCurrentPath(fieldName);
		if (field == false) return;

		// from DOM as well
		self.removeFilterStepsAfterPosition($fieldView.closest('.filter-step').data('number'));

		// now proceed with adding the current one
		self.currentFilterFieldsPath.push(fieldName);

		// if this fields has some more subfields then add them now
		if (typeof(field.fields) !== 'undefined') {
			var $fieldView = createFieldSelectionView(field.fields);
			self.addFilterStep('field', $fieldView);
			return;
		}

		// this is a "final" field, so now the user needs to input desired value(s)
		var $valueView = createValueInputView(field);
		self.addFilterStep('value', $valueView);

		// also show the submit button
		var $submitView = self.getTemplate('submit');
		self.addFilterStep('submit', $submitView);

		$submitView.click(function(ev) {
			self.didSubmitFilter($valueView);
		});
	};

	this.didSubmitFilter = function($valueView) {
		// first check if the operator is selected
		var $selectedOperator = $valueView.find('[name="operator"]:checked');
		if ($selectedOperator.length == 0) {
			self.showError('You need to select an operator!');
			return;
		}

		// then check if there is a value specified
		var value = $valueView.find('#filter-value-input-field input').val();
		if (value.length == 0) {
			self.showError('You need to specify a value!');
			return;
		}

		jcsdl.addFilter(self.currentFilterTarget, self.currentFilterFieldsPath, $selectedOperator.val(), value);
		var theCode = jcsdl.getCSDL();
		jcsdl.clearFilters();

		$(config.outputTo).val(theCode);
	};

	/* ##########################
	 * HELPERS
	 * ########################## */
	/**
	 * Shows an error.
	 * @param  {String} message Error message to be displayed.
	 * @param  {String} code    Code that caused the error.
	 */
	this.showError = function(message, code) {
		alert(message + "\n\n##################\n\n" + code + "\n\n#####\n\n See console for more info.");
		console.log(message, arguments);
	};

	/**
	 * Creates a filter row element for the specified filter.
	 * @param  {Object} filter Filter object.
	 * @return {jQuery}
	 */
	var createFilterRow = function(filter) {
		var $filterRow = self.getTemplate('filter');

		$filterRow.find('.j-target').html(filter.target);
		$filterRow.find('.j-field').html(filter.fieldPath.join('.'));
		$filterRow.find('.j-operator').html(filter.operator);
		$filterRow.find('.j-value').html(filter.value);

		// also attach the filter data to the row
		$filterRow.data('filter', filter);

		return $filterRow;
	};

	/**
	 * Creates a select option for the given target with the specified name.
	 * @param  {String} name   Unique name of the target, matching one from JCSDLConfig.
	 * @param  {Object} target Definition of the target from JCSDLConfig.
	 * @return {jQuery}
	 */
	var createOptionForTarget = function(name, target) {
		var $option = self.getTemplate('targetOption');
		$option.val(name);
		$option.html(target.name);
		return $option;
	};

	/**
	 * Creates a field selection view for the given collection of possible fields.
	 * @param  {Object} fields Collection of possible fields.
	 * @return {jQuery}
	 */
	var createFieldSelectionView = function(fields) {
		var $fieldView = self.getTemplate('field');

		// attach some data and classes (for easy selectors)
		var fieldPosition = self.currentFilterFieldsPath.length;
		$fieldView.data('position', fieldPosition);
		$fieldView.addClass('field-select-' + fieldPosition);

		// add all possible selections
		var $fieldSelect = $fieldView.find('select');
		$.each(fields, function(name, field) {
			var $fieldView = createOptionForField(name, field);
			$fieldView.appendTo($fieldSelect);
		});

		// bind event listener on change
		$fieldSelect.change(function(ev) {
			self.didSelectField($(this).val(), $fieldView);
		});

		return $fieldView;
	};

	/**
	 * Creates a select option for the given field with the specified name.
	 * @param  {String} name  Unique name of the field in the current target, matching one from JCSDLConfig.
	 * @param  {Object} field Definition of the field from JCSDLConfig.
	 * @return {jQuery}
	 */
	var createOptionForField = function(name, field) {
		var $option = self.getTemplate('fieldOption');
		$option.val(name);
		$option.html(field.name);
		return $option;
	};

	var createValueInputView = function(field) {
		var $valueView = self.getTemplate('valueInput');

		// create the actual input based on the field definition
		var $inputView = self.getTemplate('valueInput_' + field.input);
		$inputView.addClass('type-' + field.type);

		// add the input in proper area of the value view
		$valueView.find('#filter-value-input-field').html($inputView);

		// now take care of possible operators
		$.each(field.operators, function(i, operator) {
			var $operatorView = createOperatorSelectView(operator);
			$valueView.find('#filter-value-input-operators').append($operatorView);
		});

		return $valueView;
	};

	/**
	 * Creates a single operator select view for the specified operator.
	 * @param  {String} operatorName Name of the operator.
	 * @return {jQuery}
	 */
	var createOperatorSelectView = function(operatorName) {
		var operator = JCSDLConfig.operators[operatorName];
		if (typeof(operator) == 'undefined') return $(); // return empty jquery object if no such operator defined

		var $operatorView = self.getTemplate('operatorSelect');
		var operatorId = 'operator-' + operatorName;
		$operatorView.find('input').val(operatorName);
		$operatorView.find('input').attr('id', operatorId);
		$operatorView.find('label').html(operator.description);
		$operatorView.find('label').attr('for', operatorId);

		return $operatorView;
	};

	/**
	 * Returns definition of the field found under the current root path and specified field name.
	 * @param  {String} newFieldName
	 * @return mixed Either Object or bool false if no such field was found.
	 */
	var getFieldInfoAtCurrentPath = function(newFieldName) {
		// starting field is naturally the current target
		var field = JCSDLConfig.targets[self.currentFilterTarget];

		// get to the end of the path
		$.each(self.currentFilterFieldsPath, function(i, fieldName) {
			if (typeof(field.fields) !== 'undefined') {
				// get the next field definition in line
				field = field.fields[fieldName];

			} else {
				field = false;
				return false; // break the $.each
			}
		});

		if (typeof(field.fields) !== 'undefined') {
			field = (typeof(field.fields[newFieldName]) !== 'undefined') ? field.fields[newFieldName] : false;
		}

		return field;
	};

	/* ##########################
	 * SETTERS AND GETTERS
	 * ########################## */
	/**
	 * Returns a template (jQuery object) of the given name.
	 * @param  {String} name Name of the template to fetch.
	 * @return {jQuery}
	 */
	this.getTemplate = function(name) {
		if (typeof(self.templates[name]) !== 'undefined') {
			return self.templates[name].clone();
		}

		return $();
	};

	// automatically call the initialization after everything has been defined
	this.init();
};<commit_msg>Removing filters is now possible.<commit_after>var JCSDLGui = function($container, config) {
	var self = this;

	this.$container = $($container); // ensure jQuery object
	if (this.$container.length == 0) return false; // break if no such element in DOM

	/** @var {JCSDL} The actual JCSDL "parser". */
	var jcsdl = new JCSDL(this);

	this.config = $.extend(true, {
		animationSpeed : 200,
		onSave : function(code) {console.log(code);}
	}, config);

	this.$editor = $();

	this.logic = 'AND';
	this.filters = [];

	/*
	this.$currentFilterView = $();
	this.filterSteps = [];

	// current choice data
	this.currentFilterTarget = null;
	this.currentFilterFieldsPath = [];
	*/

	// all templates are defined elsewhere
	this.templates = JCSDLGuiTemplates;

	/*
	 * INITIALIZE THE EDITOR
	 */
	/**
	 * Initializes (or reinitializes) the JCSDL GUI Editor.
	 */
	this.init = function() {
		// reset everything in case this is a reinitialization
		self.logic = 'AND';
		self.filters = [];

		//self.$currentFilterView = $();
		//self.filterSteps = [];
		//self.currentFilterTarget = null;
		//self.currentFilterFieldsPath = [];

		// and insert the editor into the container
		self.$editor = self.getTemplate('editor');
		self.$container.html(self.$editor);

		self.$editor.find('.filters-logic input[name="logic"]').change(function(ev) {
			self.logic = $(this).val();
		});

		self.$editor.find('.jcsdl-editor-save').bind('click.jcsdl', function(ev) {
			self.returnJCSDL();
		});
	};

	/**
	 * Loads the given JCSDL string and builds the whole editor based on it.
	 * @param  {String} code JCSDL string.
	 */
	this.loadJCSDL = function(code) {
		this.init(); // just in case we're loading JCSDL for a 2nd time

		var parsed = jcsdl.parseJCSDL(code);
		if (parsed === false) {
			self.showError('Invalid JCSDL input!', code);
			return;
		}

		self.logic = (parsed.logic == 'AND') ? 'AND' : 'OR';
		self.filters = parsed.filters;

		// mark the logic
		self.$editor.find('.filters-logic input[name="logic"][value="' + self.logic + '"]').click();

		// display the filters
		// target the filters list
		var $filtersList = self.$editor.find('.filters-list');

		// add each filter to the list
		$.each(self.filters, function(i, filter) {
			var $filterRow = createFilterRow(filter);
			$filterRow.appendTo($filtersList);
		});

		/*
		// select target
		self.$currentFilterView.find('select[name="target"]').val(filter.target);
		self.didSelectTarget(filter.target);

		// select all fields and subfields
		$.each(filter.fieldPath, function(i, field) {
			var $fieldView = self.$currentFilterView.find('.filter-target-field:last');
			$fieldView.find('select[name="field[]"]').val(field);
			self.didSelectField(field, $fieldView);
		});

		// select operator
		self.$currentFilterView.find('#operator-' + filter.operator).click();

		// fill in the value
		self.$currentFilterView.find('#filter-value-input-field input').val(filter.value);
		*/
	};

	/**
	 * Returns the JCSDL
	 * @return {[type]}
	 */
	this.returnJCSDL = function() {
		var code = jcsdl.getCSDLFromFilters(self.filters, self.logic);
		self.config.onSave.apply(self, [code]);
	};

	/**
	 * Initializes the filter editor.
	 */
	this.initFilterEditor = function() {
		// prepare the filter editor
		self.$currentFilterView = self.getTemplate('filter');

		// by default always prepare the selection of targets first
		var $targetSelectView = self.getTemplate('target');
		var $targetSelect = $targetSelectView.find('select');

		// create a select option for every possible target
		$.each(JCSDLConfig.targets, function(name, target) {
			// delegate creation of the option to a function
			var $targetView = createOptionForTarget(name, target);

			// append the option to the select
			$targetView.appendTo($targetSelect);
		});

		// register listener for change event
		$targetSelect.change(function(ev) {
			self.didSelectTarget($(this).val());
		});

		// append the select view to the filter view
		this.addFilterStep('target', $targetSelectView);

		// and finally add the editor to the DOM
		self.$container.html(self.$currentFilterView);
	};

	/* ##########################
	 * FILTER EDITOR BUILDING
	 * ########################## */
	/**
	 * Adds a filter step with proper numbering/position.
	 * @param {String} stepName Name of the step.
	 * @param {jQuery} $view    View of the step.
	 */
	this.addFilterStep = function(stepName, $view) {
		var $stepView = self.getTemplate('step');
		$stepView.html($view);

		var stepNumber = self.filterSteps.length;

		// attach some data and classes (for easy selectors)
		$stepView.data('number', stepNumber);
		$stepView.addClass('filter-step-number-' + stepNumber);
		$stepView.data('name', stepName);
		$stepView.addClass('filter-step-name-' + stepName);

		// add to the DOM
		$stepView.appendTo(self.$currentFilterView);
		$stepView.hide().fadeIn(self.config.animationSpeed);

		// add to the steps pool
		self.filterSteps.push($stepView);
	};

	/**
	 * Removes all filter steps after the given position.
	 * Also clears the resulting CSDL if there was any before.
	 * @param  {Integer} position
	 */
	this.removeFilterStepsAfterPosition = function(position) {
		var steps = self.filterSteps.splice(position + 1, self.filterSteps.length - position);
		$.each(steps, function(i, $step) {
			$($step).remove(); // ensure jQuery
		});

		$('#filter-csdl').val('');
	};

	/*
	 * EVENT HANDLERS
	 */
	/**
	 * When a target is selected, the user needs to select a field.
	 * @param  {String} targetName Name of the selected target.
	 */
	this.didSelectTarget = function(targetName) {
		if (typeof(JCSDLConfig.targets[targetName]) == 'undefined') return false;

		self.currentFilterTarget = targetName;
		self.currentFilterFieldsPath = [];

		// remove all steps regarding selection of fields in case another target was selected first
		self.removeFilterStepsAfterPosition(0); // target is always position 0

		// now need to select a field, so build the selection view
		var target = JCSDLConfig.targets[targetName];
		var $fieldView = createFieldSelectionView(target.fields);

		self.addFilterStep('field', $fieldView);
	};

	/**
	 * When a field is selected then the user either needs to input a value or select a subfield if such exists.
	 * @param  {String} fieldName  Name of the selected field.
	 * @param  {jQuery} $fieldView The field selection view that was used.
	 */
	this.didSelectField = function(fieldName, $fieldView) {
		// remove any steps that are farther then this one, just in case
		var fieldPosition = $fieldView.data('position');
		self.currentFilterFieldsPath.splice(fieldPosition, self.currentFilterFieldsPath.length - fieldPosition);

		var field = getFieldInfoAtCurrentPath(fieldName);
		if (field == false) return;

		// from DOM as well
		self.removeFilterStepsAfterPosition($fieldView.closest('.filter-step').data('number'));

		// now proceed with adding the current one
		self.currentFilterFieldsPath.push(fieldName);

		// if this fields has some more subfields then add them now
		if (typeof(field.fields) !== 'undefined') {
			var $fieldView = createFieldSelectionView(field.fields);
			self.addFilterStep('field', $fieldView);
			return;
		}

		// this is a "final" field, so now the user needs to input desired value(s)
		var $valueView = createValueInputView(field);
		self.addFilterStep('value', $valueView);

		// also show the submit button
		var $submitView = self.getTemplate('submit');
		self.addFilterStep('submit', $submitView);

		$submitView.click(function(ev) {
			self.didSubmitFilter($valueView);
		});
	};

	this.didSubmitFilter = function($valueView) {
		// first check if the operator is selected
		var $selectedOperator = $valueView.find('[name="operator"]:checked');
		if ($selectedOperator.length == 0) {
			self.showError('You need to select an operator!');
			return;
		}

		// then check if there is a value specified
		var value = $valueView.find('#filter-value-input-field input').val();
		if (value.length == 0) {
			self.showError('You need to specify a value!');
			return;
		}

		jcsdl.addFilter(self.currentFilterTarget, self.currentFilterFieldsPath, $selectedOperator.val(), value);
		var theCode = jcsdl.getCSDL();
		jcsdl.clearFilters();

		$(config.outputTo).val(theCode);
	};

	/**
	 * Removes the filter at the given index.
	 * @param  {Number} index
	 */
	this.deleteFilter = function(index) {
		// remove from the DOM
		self.$editor.find('.filters-list .filter').eq(index).remove();

		// remove from the filters list
		self.filters.splice(index, 1);
	};

	/* ##########################
	 * HELPERS
	 * ########################## */
	/**
	 * Shows an error.
	 * @param  {String} message Error message to be displayed.
	 * @param  {String} code    Code that caused the error.
	 */
	this.showError = function(message, code) {
		alert(message + "\n\n##################\n\n" + code + "\n\n#####\n\n See console for more info.");
		console.log(message, arguments);
	};

	/**
	 * Creates a filter row element for the specified filter.
	 * @param  {Object} filter Filter object.
	 * @return {jQuery}
	 */
	var createFilterRow = function(filter) {
		var $filterRow = self.getTemplate('filter');

		$filterRow.find('.j-target').html(filter.target);
		$filterRow.find('.j-field').html(filter.fieldPath.join('.'));
		$filterRow.find('.j-operator').html(filter.operator);
		$filterRow.find('.j-value').html(filter.value);

		// also attach the filter data to the row
		$filterRow.data('filter', filter);

		// bind the delete event
		$filterRow.find('.j-delete').click(function(ev) {
			ev.preventDefault();
			ev.target.blur();

			var index = self.getFilterIndexByElement($filterRow);
			self.deleteFilter(index);
		});

		return $filterRow;
	};

	/**
	 * Creates a select option for the given target with the specified name.
	 * @param  {String} name   Unique name of the target, matching one from JCSDLConfig.
	 * @param  {Object} target Definition of the target from JCSDLConfig.
	 * @return {jQuery}
	 */
	var createOptionForTarget = function(name, target) {
		var $option = self.getTemplate('targetOption');
		$option.val(name);
		$option.html(target.name);
		return $option;
	};

	/**
	 * Creates a field selection view for the given collection of possible fields.
	 * @param  {Object} fields Collection of possible fields.
	 * @return {jQuery}
	 */
	var createFieldSelectionView = function(fields) {
		var $fieldView = self.getTemplate('field');

		// attach some data and classes (for easy selectors)
		var fieldPosition = self.currentFilterFieldsPath.length;
		$fieldView.data('position', fieldPosition);
		$fieldView.addClass('field-select-' + fieldPosition);

		// add all possible selections
		var $fieldSelect = $fieldView.find('select');
		$.each(fields, function(name, field) {
			var $fieldView = createOptionForField(name, field);
			$fieldView.appendTo($fieldSelect);
		});

		// bind event listener on change
		$fieldSelect.change(function(ev) {
			self.didSelectField($(this).val(), $fieldView);
		});

		return $fieldView;
	};

	/**
	 * Creates a select option for the given field with the specified name.
	 * @param  {String} name  Unique name of the field in the current target, matching one from JCSDLConfig.
	 * @param  {Object} field Definition of the field from JCSDLConfig.
	 * @return {jQuery}
	 */
	var createOptionForField = function(name, field) {
		var $option = self.getTemplate('fieldOption');
		$option.val(name);
		$option.html(field.name);
		return $option;
	};

	var createValueInputView = function(field) {
		var $valueView = self.getTemplate('valueInput');

		// create the actual input based on the field definition
		var $inputView = self.getTemplate('valueInput_' + field.input);
		$inputView.addClass('type-' + field.type);

		// add the input in proper area of the value view
		$valueView.find('#filter-value-input-field').html($inputView);

		// now take care of possible operators
		$.each(field.operators, function(i, operator) {
			var $operatorView = createOperatorSelectView(operator);
			$valueView.find('#filter-value-input-operators').append($operatorView);
		});

		return $valueView;
	};

	/**
	 * Creates a single operator select view for the specified operator.
	 * @param  {String} operatorName Name of the operator.
	 * @return {jQuery}
	 */
	var createOperatorSelectView = function(operatorName) {
		var operator = JCSDLConfig.operators[operatorName];
		if (typeof(operator) == 'undefined') return $(); // return empty jquery object if no such operator defined

		var $operatorView = self.getTemplate('operatorSelect');
		var operatorId = 'operator-' + operatorName;
		$operatorView.find('input').val(operatorName);
		$operatorView.find('input').attr('id', operatorId);
		$operatorView.find('label').html(operator.description);
		$operatorView.find('label').attr('for', operatorId);

		return $operatorView;
	};

	/**
	 * Returns definition of the field found under the current root path and specified field name.
	 * @param  {String} newFieldName
	 * @return mixed Either Object or bool false if no such field was found.
	 */
	var getFieldInfoAtCurrentPath = function(newFieldName) {
		// starting field is naturally the current target
		var field = JCSDLConfig.targets[self.currentFilterTarget];

		// get to the end of the path
		$.each(self.currentFilterFieldsPath, function(i, fieldName) {
			if (typeof(field.fields) !== 'undefined') {
				// get the next field definition in line
				field = field.fields[fieldName];

			} else {
				field = false;
				return false; // break the $.each
			}
		});

		if (typeof(field.fields) !== 'undefined') {
			field = (typeof(field.fields[newFieldName]) !== 'undefined') ? field.fields[newFieldName] : false;
		}

		return field;
	};

	/* ##########################
	 * SETTERS AND GETTERS
	 * ########################## */
	/**
	 * Returns a template (jQuery object) of the given name.
	 * @param  {String} name Name of the template to fetch.
	 * @return {jQuery}
	 */
	this.getTemplate = function(name) {
		if (typeof(self.templates[name]) !== 'undefined') {
			return self.templates[name].clone();
		}

		return $();
	};

	/**
	 * Returns the filter index of the filter to which the given DOM representation belongs.
	 * @param  {jQuery} $filterRow
	 * @return {Number}
	 */
	this.getFilterIndexByElement = function($filterRow) {
		return self.$editor.find('.filters-list .filter').index($filterRow);
	};

	// automatically call the initialization after everything has been defined
	this.init();
};<|endoftext|>


<commit_before>import FingerPrint from './FingerPrint.js';

class AmpCollection {
    constructor(data) {
        this.FingerPrint = new FingerPrint();

        this.setData = data || [];
        this.processed = [];
        this.__isProcessed = false;

        return this;
    }

    /**
     * Getter for the processed or standard data.
     * @return {Array}     Returns the data
     */
    get __data() {
        if(this.__isProcessed) {
            return this.processed;
        }

        return this.data;
    }

    /**
     * Setter for processed data.
     * @param  {Array} processed Items of data that have been ran through a filter.
     * @return {null}           Returns nothing.
     */
    set __processed(processed) {
        // We're creating a filter so make sure we're aware.
        this.__isProcessed = true;
        // Add passed array to processed object.
        this.processed = processed;
    }

    /**
     * Setter for the data to be used in the collection.
     * @param {(String|Array)} data - data to be added
     */
    set setData(data) {
        if(Array.isArray(data)) {
            this.data = data;
        } else {
            this.data = [];
            this.data.push(data);
        }

        this.data = this.FingerPrint.addPrints(this.data);
    }

    /**
     * Create the schema for the current Collection.
     * @param {Array} keys - a list of keys to match
     * @param {Bool} strict - sets strict mode
     * @returns {Object} Returns self.
     */
    schema(keys, strict) {
        this.schema = keys.sort();
        this.schemaStrict = strict || false;

        return this;
    }

    /**
     * Checks whether the data passed to the Collection
     * matches the set schema.
     * @param  {Array} data Array of the data to check
     * @return {Array}      The data being checked.
     */
    checkSchema(data) {
        if(!Array.isArray(this.schema)) {
            return data;
        }

        if(this.schemaStrict) {
            return this.__strictSchema(data);
        }

        return this.__nonStrictSchema(data);
    }

    /**
     * Strictly checks the schema and throws
     * and stop if check doesn't pass.
     * @param  {Array} data Object of data to process
     * @return {Null}       Return nothing
     */
    __strictSchema(data) {
        let dataKeys = Object.keys(data).sort();

        if(JSON.stringify(dataKeys) !== JSON.stringify(this.schema)) {
            console.log(
                'Collection:',
                'Schema in strict mode -- keys do not match. Expecting:',
                this.schema,
                'given;',
                dataKeys
            );
            window.stop();
        }

        return data;
    }

    /**
     * Checks the schema in a non strict fashion
     * and outputs any negative results to console.
     * @param  {Array} data Object of data to process
     * @return {Object}     Returns the data passed
     */
    __nonStrictSchema(data) {
        for(let key = 0; key < this.schema.length; key++) {
            if(!data.hasOwnProperty(this.schema[key])) {
                console.log(
                    'Collection:',
                    'key "' + this.schema[key] + '" missing from collection.'
                );
            }
        }

        return data;
    }

    /**
     * Get all the data or processed data.
     * @returns {Array} The data in the Collection
     */
    all() {
        return this.__data;
    }

    /**
     * First item in the data or processed data.
     * @returns {Object} The first item in the Collection
     */
    first() {
        return this.__data[0];
    }

    /**
     * Get the specified item from the data or processed data.
     * @param  {Number} item Number of item to get
     * @return {Object|Null}   Null if no data found or object defined.
     */
    get(item) {
        if(typeof this.__data[item] === 'undefined') {
            return null;
        }

        return this.__data[item];
    }

    /**
     * Add an item to the list of data.
     * @param {(Object|String)} value Item to add
     * @returns {Object}              Returns self
     */
    addItem(value) {
        this.data.push(this.checkSchema(value));
        this.data = this.FingerPrint.addPrints(this.data);

        return this;
    }

    /**
     * Checks if Collection has a matching key.
     * @param  {String}  key Key to seek
     * @return {(Boolean|Object)} Returns false or self if match.
     */
    has(key) {
        for(let item = 0; item < this.data.length; item++) {
            if(typeof this.data[item][key] === 'undefined') {
                return false;
            }
        }

        return this;
    }

    /**
     * Check if data or processed data has any items.
     * @return {Boolean}    Returns boolean
     */
    isEmpty() {
        let values = this.__data;

        if(values.length > 0) {
            return false;
        }

        return true;
    }

    /**
     * Iterate through each item and run through
     * a passed filter function/callback.
     * @param  {Function} filter Callback filter
     * @return {Object}          Returns self.
     */
    filter(filter) {
        for(let item = 0; item < this.__data.length; item++) {
            this.__data[item] = filter(item, this.get(item));
        }

        return this;
    }

    /**
     * Alias for doWhere to create list of processed items.
     * @param  {String} key         Key to seek
     * @param  {String} operator    Operator to be used in comparison
     * @param  {String} value       Value to match
     * @return {Object}             Returns result of doWhere
     */
    where(key, operator, value) {
        if(arguments.length === 2) {
            value       = operator
            operator    = '=';
        }

        return this.doWhere([], key, operator, value);
    }

    /**
     * Alias for doWhere on processed items as an orWhere,
     * similar to SQL queries.
     * @param  {String} key         Key to seek
     * @param  {String} operator    Operator to be used in comparison
     * @param  {String} value       Value to match
     * @return {Object}             Returns result of doWhere
     */
    orWhere(key, operator, value) {
        if(arguments.length === 2) {
            value       = operator
            operator    = '=';
        }

        return this.doWhere(this.processed, key, operator, value);
    }

    /**
     * Gets items that are unique in the collection. Uses the item FingerPrint
     * to find and remove duplicates. By using a key you can specify
     * finding duplicates by an alternative match.
     * @param   {String} key=__hash__   Key to find uniques by
     * @return  {object}                Return self
     */
    unique(key) {
        key = key || '__hash__';
        let stored = [];

        for(let item = 0; item < this.data.length; item++) {
            let value = this.data[item][key];

            // console.log('value ', this.data[item]);

            this.doWhere([], key, '=', value);

            if(this.processed.length === 1) {
                stored.push(this.processed[0]);
            }
        }

        this.processed = stored;

        return this;
    }

    /**
     * Loops through items and builds or extends processed
     * Array with items that match key and value.
     * @param  {Array}  processed   The current processed items
     * @param  {String} key         Key to seek
     * @param  {String} operator    Operator to be used in comparison
     * @param  {String} value       Value to match
     * @return {Object}             Return self
     */
    doWhere(processed, key, operator, value) {
        this.__processed = processed;

        for(let item = 0; item < this.data.length; item++) {
            if(typeof this.data[item][key] === 'undefined') {
                return this;
            }

            let where = this.__whereOperators(this.data[item][key], operator, value);

            if(where) {
                this.processed.push(this.data[item]);
            }
        }

        return this;
    }

    reset() {
        this.processed = this.data;

        return this;
    }

    __whereOperators(key, operator, value) {
        switch(operator) {
            default:
            case '=':
            case '==':
                return key == value;
            case '===':
                return key === value;
            case '>':
                return key > value;
            case '>=':
                return key >= value;
            case '<':
                return key < value;
            case '<=':
                return key <= value;
        }
    }

    /**
     * Remove specified items from the data or processed data.
     * @param  {Array}  except A list of items to exclude
     * @return {Object}        Return self
     */
    except(except) {
        this.__processed = this.__data;
        let removed = [];

        for(let item = 0; item < this.processed.length; item++) {
            let items = this.processed[item];

            removed[item] = {};

            for(let name in items) {
                if(except.indexOf(name) === -1) {
                    removed[item][name] = items[name];
                }
            }
        }

        this.processed = removed;

        return this;
    }
}

export default AmpCollection;
<commit_msg>Complete docblocks<commit_after>import FingerPrint from './FingerPrint.js';

class AmpCollection {
    /**
     * Construct the class
     * @param  {Array}  data=[]    The given data to add to a collection.
     * @return {Object}            Returns self
     */
    constructor(data) {
        this.FingerPrint = new FingerPrint();

        this.setData = data || [];
        this.processed = [];
        this.__isProcessed = false;

        return this;
    }

    /**
     * Getter for the processed or standard data.
     * @return {Array}     Returns the data
     */
    get __data() {
        if(this.__isProcessed) {
            return this.processed;
        }

        return this.data;
    }

    /**
     * Setter for processed data.
     * @param  {Array} processed Items of data that have been ran through a filter.
     * @return {void}            Returns nothing.
     */
    set __processed(processed) {
        // We're creating a filter so make sure we're aware.
        this.__isProcessed = true;
        // Add passed array to processed object.
        this.processed = processed;
    }

    /**
     * Setter for the data to be used in the collection.
     * @param {(String|Array)} data Data to be added
     */
    set setData(data) {
        if(Array.isArray(data)) {
            this.data = data;
        } else {
            this.data = [];
            this.data.push(data);
        }

        this.data = this.FingerPrint.addPrints(this.data);
    }

    /**
     * Create the schema for the current Collection.
     * @param {Array}   keys            A list of keys to match
     * @param {Bool}    strict=false    Sets strict mode
     * @returns {Object}                Returns self.
     */
    schema(keys, strict) {
        this.schema = keys.sort();
        this.schemaStrict = strict || false;

        return this;
    }

    /**
     * Checks whether the data passed to the Collection
     * matches the set schema.
     * @param  {Array} data Array of the data to check
     * @return {Array}      The data being checked.
     */
    checkSchema(data) {
        if(!Array.isArray(this.schema)) {
            return data;
        }

        if(this.schemaStrict) {
            return this.__strictSchema(data);
        }

        return this.__nonStrictSchema(data);
    }

    /**
     * Strictly checks the schema and throws
     * and stop if check doesn't pass.
     * @param  {Array} data Object of data to process
     * @return {Null}       Return nothing
     */
    __strictSchema(data) {
        let dataKeys = Object.keys(data).sort();

        if(JSON.stringify(dataKeys) !== JSON.stringify(this.schema)) {
            console.log(
                'Collection:',
                'Schema in strict mode -- keys do not match. Expecting:',
                this.schema,
                'given;',
                dataKeys
            );
            window.stop();
        }

        return data;
    }

    /**
     * Checks the schema in a non strict fashion
     * and outputs any negative results to console.
     * @param  {Array} data Object of data to process
     * @return {Object}     Returns the data passed
     */
    __nonStrictSchema(data) {
        for(let key = 0; key < this.schema.length; key++) {
            if(!data.hasOwnProperty(this.schema[key])) {
                console.log(
                    'Collection:',
                    'key "' + this.schema[key] + '" missing from collection.'
                );
            }
        }

        return data;
    }

    /**
     * Get all the data or processed data.
     * @returns {Array} The data in the Collection
     */
    all() {
        return this.__data;
    }

    /**
     * First item in the data or processed data.
     * @returns {Object} The first item in the Collection
     */
    first() {
        return this.__data[0];
    }

    /**
     * Get the specified item from the data or processed data.
     * @param  {Number}     item Number of item to get
     * @return {Object|Null}     Null if no data found or object defined.
     */
    get(item) {
        if(typeof this.__data[item] === 'undefined') {
            return null;
        }

        return this.__data[item];
    }

    /**
     * Add an item to the list of data.
     * @param {(Object|String)} value Item to add
     * @returns {Object}              Returns self
     */
    addItem(value) {
        this.data.push(this.checkSchema(value));
        this.data = this.FingerPrint.addPrints(this.data);

        return this;
    }

    /**
     * Checks if Collection has a matching key.
     * @param  {String}  key Key to seek
     * @return {(Boolean|Object)} Returns false or self if match.
     */
    has(key) {
        for(let item = 0; item < this.data.length; item++) {
            if(typeof this.data[item][key] === 'undefined') {
                return false;
            }
        }

        return this;
    }

    /**
     * Check if data or processed data has any items.
     * @return {Boolean}    Returns boolean
     */
    isEmpty() {
        let values = this.__data;

        if(values.length > 0) {
            return false;
        }

        return true;
    }

    /**
     * Iterate through each item and run through
     * a passed filter function/callback.
     * @param  {Function} filter Callback filter
     * @return {Object}          Returns self.
     */
    filter(filter) {
        for(let item = 0; item < this.__data.length; item++) {
            this.__data[item] = filter(item, this.get(item));
        }

        return this;
    }

    /**
     * Alias for doWhere to create list of processed items.
     * @param  {String} key         Key to seek
     * @param  {String} operator    Operator to be used in comparison
     * @param  {String} value       Value to match
     * @return {Object}             Returns result of doWhere
     */
    where(key, operator, value) {
        if(arguments.length === 2) {
            value       = operator
            operator    = '=';
        }

        return this.doWhere([], key, operator, value);
    }

    /**
     * Alias for doWhere on processed items as an orWhere,
     * similar to SQL queries.
     * @param  {String} key         Key to seek
     * @param  {String} operator    Operator to be used in comparison
     * @param  {String} value       Value to match
     * @return {Object}             Returns result of doWhere
     */
    orWhere(key, operator, value) {
        if(arguments.length === 2) {
            value       = operator
            operator    = '=';
        }

        return this.doWhere(this.processed, key, operator, value);
    }

    /**
     * Gets items that are unique in the collection. Uses the item FingerPrint
     * to find and remove duplicates. By using a key you can specify
     * finding duplicates by an alternative match.
     * @param   {String} key=__hash__   Key to find uniques by
     * @return  {object}                Return self
     */
    unique(key) {
        key = key || '__hash__';
        let stored = [];

        for(let item = 0; item < this.data.length; item++) {
            let value = this.data[item][key];

            // console.log('value ', this.data[item]);

            this.doWhere([], key, '=', value);

            if(this.processed.length === 1) {
                stored.push(this.processed[0]);
            }
        }

        this.processed = stored;

        return this;
    }

    /**
     * Loops through items and builds or extends processed
     * Array with items that match key and value.
     * @param  {Array}  processed   The current processed items
     * @param  {String} key         Key to seek
     * @param  {String} operator    Operator to be used in comparison
     * @param  {String} value       Value to match
     * @return {Object}             Return self
     */
    doWhere(processed, key, operator, value) {
        this.__processed = processed;

        for(let item = 0; item < this.data.length; item++) {
            if(typeof this.data[item][key] === 'undefined') {
                return this;
            }

            let where = this.__whereOperators(this.data[item][key], operator, value);

            if(where) {
                this.processed.push(this.data[item]);
            }
        }

        return this;
    }

    /**
     * Resets the processed data collection back to
     * its original state
     * @return {Object}             Return self
     */
    reset() {
        this.processed = this.data;

        return this;
    }

    /**
     * Does the comparison logic based on the given
     * operator.
     * @param  {String} key         Key to seek
     * @param  {String} operator    Operator to be used in comparison
     * @param  {String} value       Value to match
     * @return {Boolean}            Returns whether matches or not.
     */
    __whereOperators(key, operator, value) {
        switch(operator) {
            default:
            case '=':
            case '==':
                return key == value;
            case '===':
                return key === value;
            case '>':
                return key > value;
            case '>=':
                return key >= value;
            case '<':
                return key < value;
            case '<=':
                return key <= value;
        }
    }

    /**
     * Remove specified items from the data or processed data.
     * @param  {Array}  except A list of items to exclude
     * @return {Object}        Return self
     */
    except(except) {
        this.__processed = this.__data;
        let removed = [];

        for(let item = 0; item < this.processed.length; item++) {
            let items = this.processed[item];

            removed[item] = {};

            for(let name in items) {
                if(except.indexOf(name) === -1) {
                    removed[item][name] = items[name];
                }
            }
        }

        this.processed = removed;

        return this;
    }
}

export default AmpCollection;
<|endoftext|>


<commit_before>
import _ from 'lodash';

const debug = false;

const notifyUID = makeUID(true);

const localProps = ['Notifying', 'Alerted', 'Timer', 'TimerId', 'Evaluator', 'ReactNotify', 'userState'];

function makeUID(notify){
  /*jshint bitwise:false */
  var i, random;
  var uuid = '';
  for (i = 0; i < 32; i++) {
    random = Math.random() * 16 | 0;
    if (i === 8 || i === 12 || i === 16 || i === 20) uuid += '-';
    uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)).toString(16);
  }
  return (!notify? 'uid-': 'ntf-') + uuid;
}

function difference(a, b) {
  function changes(a, b) {
    return _.transform(b, function(result, value, key) {
      if (!_.isEqual(value, a[key])) {
        result[key] = (_.isObject(value) && _.isObject(a[key])) ? changes(value, a[key]) : value;
      }
    });
  }
  return changes(a, b);
}

let persistence = null;
let network = null;

function setPersistence(p){ persistence = p; }

function setNetwork(n){ network = n; }

const objects = {};

function getCachedObject(u){
  return objects[toUID(u)]
}

function getObject(u){
  const uid=toUID(u);
  const o = objects[uid]
  if(o || !(persistence && persistence.fetch)) return Promise.resolve(o)
  return persistence.fetch(uid).then(o=>{
    objects[uid] = o;
    return o
  })
}

function cacheAndPersist(o){
  objects[toUID(o.UID)]=o;
  if(persistence && persistence.persist) persistence.persist(o);
}

function dumpCache(){
  console.log("---------cache-------");
  Object.keys(objects).map(k => console.log(objects[k]));
  console.log("---------------------");
}

function spawnObject(o){
  const UID = o.UID || makeUID();
  const Notify = o.Notify || [];
  cacheAndPersist(Object.assign({ UID, Notify }, o));
  doEvaluate(UID);
  return UID;
}

function storeObject(o){
  if(!o.UID)    return;
  if(!o.Notify) o.Notify = [];
  cacheAndPersist(o);
  notifyObservers(o);
}

function cacheObjects(list){
  return list.map(o => spawnObject(o));
}

var fetching = {};

function ensureObjectState(u, obsuid){
  const o = getCachedObject(u);
  if(o){
    // if(isURL(u) && o.timeSinceFetched < ..){ // and below after getObject
    setNotify(o,obsuid);
    return o;
  }
  getObject(u).then(o=>{
    if(o){
      setNotify(o,obsuid);
      //notifyObservers(o);
    }
    else if(isURL(u)){
      const url=u;
      cacheAndPersist({ UID: url, Notify: [ obsuid ] });
      if(!fetching[url]){
        fetching[url]=true;
        network && network.doGet(url)
         .then(json => { fetching[url]=false; setObjectState(url, json) });
      }
    }
  })
  return null;
}

function setNotify(o,uid){
  if(o.Notify.indexOf(uid) === -1){
    o.Notify.push(uid);
    cacheAndPersist(o)
  }
}

function notifyObservers(o){
  if(o.Notifying){
    setNotify(o, o.Notifying);
  }
  o.Notify.map(u => getObject(u).then(n=>{
    if(!n){
      if(isURL(u) || isNotify(u)){
        network && network.doPost(o, u);
      }
    }
    else {
      if(n.Remote){
        network && network.doPost(o, n.Remote);
      }
      else
      if(isURL(n.UID)){
        network && network.doPost(o, n.UID);
      }
      else {
        n.Alerted=o.UID;
        doEvaluate(n.UID);
        delete n.Alerted;
      }
    }})
  );
}

function setObjectState(uid, update){
  if(!uid) return null
  const o=getCachedObject(uid)
  if(!o) return null;
  const p = Object.assign({}, o, update);
  const changed = !_.isEqual(o, p);
  if(!changed) return null;
  if(debug) console.log(uid, 'changed: ', difference(o, p))
  cacheAndPersist(p);
  notifyObservers(p);
  return p;
}

function isUID(u){
  return /^uid-/.test(u);
}

function isURL(u){
  return /^https?:\/\//.test(u);
}

function isNotify(u){
  return /^ntf-/.test(u);
}

function toUID(u){
  if(!isURL(u)) return u;
  const s=u.indexOf('uid-');
  if(s=== -1) return u;
  return u.substring(s);
}

const isQueryableCacheListLabels = ['queryable', 'cache', 'list'];

function cacheQuery(o, path, query){
  if(!persistence) return new Promise();
  const scope = o.list;
  if(scope.includes('local') || scope.includes('remote')){
    return persistence.query(o.is.filter(s => !isQueryableCacheListLabels.includes(s)), scope, query);
  }
  return new Promise();
}

function object(u,p,q) { const r = ((uid, path, query)=>{
  if(!uid || !path) return null;
  const o=getCachedObject(uid)
  if(!o) return null;
  const isQueryableCacheList = o.is && o.is.constructor===Array && isQueryableCacheListLabels.every(s => o.is.includes(s));
  const hasMatch = query && query.constructor===Object && query.match
  if(path==='list' && isQueryableCacheList && hasMatch) return cacheQuery(o, path, query);
  if(path==='.') return o;
  const pathbits = path.split('.');
  let c=o;
  for(var i=0; i<pathbits.length; i++){
    if(pathbits[i]==='') return c;
    if(pathbits[i]==='Timer') return c.Timer || 0;
    const val = c[pathbits[i]];
    if(val == null) return null;
    if(i==pathbits.length-1){
      if(!hasMatch) return val;
      if(val.constructor === Array){
        if(query.match.constructor===Array){
          return (query.match.length <= val.length && query.match.every(q => val.find(v=>valMatch(q,v))) && val) || null;
        }
        const r = val.filter(v => {
          if(valMatch(v, query.match)) return true;
          if(v.constructor === String){
            // TODO: ensureObjectState?
            const o=getCachedObject(v)
            if(!o) return false;
            setNotify(o,uid);
            return Object.keys(query.match).every(k => valMatch(o[k], query.match[k]));
          }
          if(v.constructor===Object){
            return Object.keys(query.match).every(k => valMatch(v[k],query.match[k]));
          }
          return false;
        });
        return (r.length && r) || null;
      }
      return valMatch(val,query.match)? val: null;
    }
    if(val.constructor === Object){ c = val; continue; }
    if(val.constructor === String){
      c = ensureObjectState(val, uid);
      if(!c) return null;
    }
  }
  })(u,p,q);
  // if(debug) console.log('object',getCachedObject(u),'path',p,'query',q,'=>',r);
  return r;
}

function valMatch(a, b){
  return a == b || ((isURL(a) || isURL(b)) && toUID(a) === toUID(b));
}

function checkTimer(o,time){
  if(time && time > 0 && !o.TimerId){
    o.TimerId = setTimeout(() => {
      getCachedObject(o.UID).TimerId = null;
      setObjectState(o.UID, { Timer: 0 });
      doEvaluate(o.UID);
    }, time);
  }
}

function setPromiseState(uid, o, p){
  p.then(newState => {
    if(debug) console.log('<<<<<<<<<<<<< new state bits: ', newState);
    checkTimer(o,newState.Timer);
    setObjectState(uid, newState);
  });
  return {};
}

function doEvaluate(uid, params) {
  var o = getCachedObject(uid);
  if(!o || !o.Evaluator || typeof o.Evaluator !== 'function') return o;
  const reactnotify = o.ReactNotify;
  for(var i=0; i<4; i++){
    if(debug) console.log(i, '>>>>>>>>>>>>> ', object(uid, '.'));
    if(debug) console.log(i, '>>>>>>>>>>>>> ', object(uid, 'userState.'));
    const evalout = o.Evaluator(object.bind(null, uid), params);
    if(!evalout){ console.error('no eval output for', uid, o); return o; }
    let newState;
    if(evalout.constructor === Array){
      newState = Object.assign({}, ...(evalout.map(x => (x && x.constructor === Promise)? setPromiseState(uid,o,x): (x || {}))))
    }
    else newState = evalout;
    if(debug) console.log(i, '<<<<<<<<<<<<< new state bits: ', newState);
    checkTimer(o,newState.Timer);
    o = setObjectState(uid, newState);
    if(!o) break;
  }
  if(reactnotify) reactnotify();
  return o;
}

function runEvaluator(uid, params){
  return getObject(uid).then(()=>doEvaluate(uid, params));
}

export default {
  notifyUID,
  toUID,
  spawnObject,
  storeObject,
  cacheObjects,
  setNotify,
  setObjectState,
  object,
  runEvaluator,
  getObject,
  setPersistence,
  setNetwork,
  localProps,
  isURL,
  isNotify,
}

<commit_msg>use includes<commit_after>
import _ from 'lodash';

const debug = false;

const notifyUID = makeUID(true);

const localProps = ['Notifying', 'Alerted', 'Timer', 'TimerId', 'Evaluator', 'ReactNotify', 'userState'];

function makeUID(notify){
  /*jshint bitwise:false */
  var i, random;
  var uuid = '';
  for (i = 0; i < 32; i++) {
    random = Math.random() * 16 | 0;
    if (i === 8 || i === 12 || i === 16 || i === 20) uuid += '-';
    uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)).toString(16);
  }
  return (!notify? 'uid-': 'ntf-') + uuid;
}

function difference(a, b) {
  function changes(a, b) {
    return _.transform(b, function(result, value, key) {
      if (!_.isEqual(value, a[key])) {
        result[key] = (_.isObject(value) && _.isObject(a[key])) ? changes(value, a[key]) : value;
      }
    });
  }
  return changes(a, b);
}

let persistence = null;
let network = null;

function setPersistence(p){ persistence = p; }

function setNetwork(n){ network = n; }

const objects = {};

function getCachedObject(u){
  return objects[toUID(u)]
}

function getObject(u){
  const uid=toUID(u);
  const o = objects[uid]
  if(o || !(persistence && persistence.fetch)) return Promise.resolve(o)
  return persistence.fetch(uid).then(o=>{
    objects[uid] = o;
    return o
  })
}

function cacheAndPersist(o){
  objects[toUID(o.UID)]=o;
  if(persistence && persistence.persist) persistence.persist(o);
}

function dumpCache(){
  console.log("---------cache-------");
  Object.keys(objects).map(k => console.log(objects[k]));
  console.log("---------------------");
}

function spawnObject(o){
  const UID = o.UID || makeUID();
  const Notify = o.Notify || [];
  cacheAndPersist(Object.assign({ UID, Notify }, o));
  doEvaluate(UID);
  return UID;
}

function storeObject(o){
  if(!o.UID)    return;
  if(!o.Notify) o.Notify = [];
  cacheAndPersist(o);
  notifyObservers(o);
}

function cacheObjects(list){
  return list.map(o => spawnObject(o));
}

var fetching = {};

function ensureObjectState(u, obsuid){
  const o = getCachedObject(u);
  if(o){
    // if(isURL(u) && o.timeSinceFetched < ..){ // and below after getObject
    setNotify(o,obsuid);
    return o;
  }
  getObject(u).then(o=>{
    if(o){
      setNotify(o,obsuid);
      //notifyObservers(o);
    }
    else if(isURL(u)){
      const url=u;
      cacheAndPersist({ UID: url, Notify: [ obsuid ] });
      if(!fetching[url]){
        fetching[url]=true;
        network && network.doGet(url)
         .then(json => { fetching[url]=false; setObjectState(url, json) });
      }
    }
  })
  return null;
}

function setNotify(o,uid){
  if(!o.Notify.includes(uid)){
    o.Notify.push(uid);
    cacheAndPersist(o)
  }
}

function notifyObservers(o){
  if(o.Notifying){
    setNotify(o, o.Notifying);
  }
  o.Notify.map(u => getObject(u).then(n=>{
    if(!n){
      if(isURL(u) || isNotify(u)){
        network && network.doPost(o, u);
      }
    }
    else {
      if(n.Remote){
        network && network.doPost(o, n.Remote);
      }
      else
      if(isURL(n.UID)){
        network && network.doPost(o, n.UID);
      }
      else {
        n.Alerted=o.UID;
        doEvaluate(n.UID);
        delete n.Alerted;
      }
    }})
  );
}

function setObjectState(uid, update){
  if(!uid) return null
  const o=getCachedObject(uid)
  if(!o) return null;
  const p = Object.assign({}, o, update);
  const changed = !_.isEqual(o, p);
  if(!changed) return null;
  if(debug) console.log(uid, 'changed: ', difference(o, p))
  cacheAndPersist(p);
  notifyObservers(p);
  return p;
}

function isUID(u){
  return /^uid-/.test(u);
}

function isURL(u){
  return /^https?:\/\//.test(u);
}

function isNotify(u){
  return /^ntf-/.test(u);
}

function toUID(u){
  if(!isURL(u)) return u;
  const s=u.indexOf('uid-');
  if(s=== -1) return u;
  return u.substring(s);
}

const isQueryableCacheListLabels = ['queryable', 'cache', 'list'];

function cacheQuery(o, path, query){
  if(!persistence) return new Promise();
  const scope = o.list;
  if(scope.includes('local') || scope.includes('remote')){
    return persistence.query(o.is.filter(s => !isQueryableCacheListLabels.includes(s)), scope, query);
  }
  return new Promise();
}

function object(u,p,q) { const r = ((uid, path, query)=>{
  if(!uid || !path) return null;
  const o=getCachedObject(uid)
  if(!o) return null;
  const isQueryableCacheList = o.is && o.is.constructor===Array && isQueryableCacheListLabels.every(s => o.is.includes(s));
  const hasMatch = query && query.constructor===Object && query.match
  if(path==='list' && isQueryableCacheList && hasMatch) return cacheQuery(o, path, query);
  if(path==='.') return o;
  const pathbits = path.split('.');
  let c=o;
  for(var i=0; i<pathbits.length; i++){
    if(pathbits[i]==='') return c;
    if(pathbits[i]==='Timer') return c.Timer || 0;
    const val = c[pathbits[i]];
    if(val == null) return null;
    if(i==pathbits.length-1){
      if(!hasMatch) return val;
      if(val.constructor === Array){
        if(query.match.constructor===Array){
          return (query.match.length <= val.length && query.match.every(q => val.find(v=>valMatch(q,v))) && val) || null;
        }
        const r = val.filter(v => {
          if(valMatch(v, query.match)) return true;
          if(v.constructor === String){
            // TODO: ensureObjectState?
            const o=getCachedObject(v)
            if(!o) return false;
            setNotify(o,uid);
            return Object.keys(query.match).every(k => valMatch(o[k], query.match[k]));
          }
          if(v.constructor===Object){
            return Object.keys(query.match).every(k => valMatch(v[k],query.match[k]));
          }
          return false;
        });
        return (r.length && r) || null;
      }
      return valMatch(val,query.match)? val: null;
    }
    if(val.constructor === Object){ c = val; continue; }
    if(val.constructor === String){
      c = ensureObjectState(val, uid);
      if(!c) return null;
    }
  }
  })(u,p,q);
  // if(debug) console.log('object',getCachedObject(u),'path',p,'query',q,'=>',r);
  return r;
}

function valMatch(a, b){
  return a == b || ((isURL(a) || isURL(b)) && toUID(a) === toUID(b));
}

function checkTimer(o,time){
  if(time && time > 0 && !o.TimerId){
    o.TimerId = setTimeout(() => {
      getCachedObject(o.UID).TimerId = null;
      setObjectState(o.UID, { Timer: 0 });
      doEvaluate(o.UID);
    }, time);
  }
}

function setPromiseState(uid, o, p){
  p.then(newState => {
    if(debug) console.log('<<<<<<<<<<<<< new state bits: ', newState);
    checkTimer(o,newState.Timer);
    setObjectState(uid, newState);
  });
  return {};
}

function doEvaluate(uid, params) {
  var o = getCachedObject(uid);
  if(!o || !o.Evaluator || typeof o.Evaluator !== 'function') return o;
  const reactnotify = o.ReactNotify;
  for(var i=0; i<4; i++){
    if(debug) console.log(i, '>>>>>>>>>>>>> ', object(uid, '.'));
    if(debug) console.log(i, '>>>>>>>>>>>>> ', object(uid, 'userState.'));
    const evalout = o.Evaluator(object.bind(null, uid), params);
    if(!evalout){ console.error('no eval output for', uid, o); return o; }
    let newState;
    if(evalout.constructor === Array){
      newState = Object.assign({}, ...(evalout.map(x => (x && x.constructor === Promise)? setPromiseState(uid,o,x): (x || {}))))
    }
    else newState = evalout;
    if(debug) console.log(i, '<<<<<<<<<<<<< new state bits: ', newState);
    checkTimer(o,newState.Timer);
    o = setObjectState(uid, newState);
    if(!o) break;
  }
  if(reactnotify) reactnotify();
  return o;
}

function runEvaluator(uid, params){
  return getObject(uid).then(()=>doEvaluate(uid, params));
}

export default {
  notifyUID,
  toUID,
  spawnObject,
  storeObject,
  cacheObjects,
  setNotify,
  setObjectState,
  object,
  runEvaluator,
  getObject,
  setPersistence,
  setNetwork,
  localProps,
  isURL,
  isNotify,
}

<|endoftext|>


<commit_before>const fsp = require( 'fs-promise' ),
	Parser = require( '../src/Parser' ),
	Document = require( '../src/rtf/Document' );

describe( 'Parser', () => {
	let parserMock = new Parser();

	describe( 'parseString', () => {
		it( 'returns a promise', () => {
			return fsp.readFile( path.join( __dirname, '_fixtures', 'rtfSimple.rtf' ), {
					encoding: 'utf8'
				} )
				.then( content => {
					expect( parserMock.parseString( content ) ).to.be.instanceof( Promise );
				} );
		} );

		it( 'resolves simple rtf', () => {
			return fsp.readFile( path.join( __dirname, '_fixtures', 'rtfSimple.rtf' ), {
					encoding: 'utf8'
				} )
				.then( content => parserMock.parseString( content ) )
				.then( doc => {
					//expect( doc ).to.be.instanceof( Document );
				} );
		} );

	} );

} );
<commit_msg>Don't use old Document class.<commit_after>const fsp = require( 'fs-promise' ),
	Parser = require( '../src/Parser' );

describe( 'Parser', () => {
	let parserMock = new Parser();

	describe( 'parseString', () => {
		it( 'returns a promise', () => {
			return fsp.readFile( path.join( __dirname, '_fixtures', 'rtfSimple.rtf' ), {
					encoding: 'utf8'
				} )
				.then( content => {
					expect( parserMock.parseString( content ) ).to.be.instanceof( Promise );
				} );
		} );

		it( 'resolves simple rtf', () => {
			return fsp.readFile( path.join( __dirname, '_fixtures', 'rtfSimple.rtf' ), {
					encoding: 'utf8'
				} )
				.then( content => parserMock.parseString( content ) )
				.then( doc => {
					//expect( doc ).to.be.instanceof( Document );
				} );
		} );

	} );

} );
<|endoftext|>