<commit_before>var async = require('async'),
    assert = require('assert'),
    EngineSmith = require('./smiths/engine.smith.js'),
    Layout = require('layout'),
    CanvasSmith = require('./smiths/canvas.smith.js'),
    engines = {};

/**
 * Spritesmith generation function
 * @param {Object} params Parameters for spritesmith
 * @param {String[]} [params.src] Images to generate into sprite sheet
 * @param {String} [params.engine="auto"] Engine to use (phantomjs, canvas, gm, or user-defined via Spritesmith.addEngine)
 * @param {String} [params.algorithm="top-down"] Algorithm to pack images with (top-down or user-defined via Spritesmith.addAlgorithm)
 * @param {Number} [params.padding] Padding to use between images
 * @param {Mixed} [params.exportOpts] Options to pass through to engine for export
 * @param {Function} callback Function that receives compiled spritesheet and map
 * @returns {Mixed} callback[0] err If an error was encountered, this will be returned to callback
 * @returns {Object} callback[1] result Result object of spritesmith
 * @returns {String} callback[1].image Binary string representation of image
 * @returns {Object} callback[1].coordinates Map from file name to an object containing x, y, height, and width information about the source image
 */
function Spritesmith(params, callback) {
  var retObj = {},
      files = params.src,
      enginePref = params.engine || 'auto',
      engine = engines[enginePref],
      algorithmPref = params.algorithm || 'top-down';

  // If the engine is not defined
  if (engine === undefined) {
    // If the engine was not auto, inform the user
    assert.strictEqual(enginePref, 'auto', 'Sorry, the spritesmith engine \'' + enginePref + '\' could not be loaded. Please be sure you have installed it properly on your machine.');

    // Begin attempting to load the engines
    engine = engines.phantomjs || engines.canvas || engines.gm;

    // Assert there is an engine
    assert(engine, 'Sorry, no spritesmith engine could be loaded for your machine. Please be sure you have installed canvas or gm.');
  }

  // If there is a set parameter for the engine, use it
  if (engine.set) {
    var engineOpts = params.engineOpts || {};
    engine.set(engineOpts);
  }

  // Create our smiths
  var engineSmith = new EngineSmith(engine),
      layer = new Layout(algorithmPref),
      padding = params.padding || 0,
      exportOpts = params.exportOpts || {},
      packedObj;

  // In a waterfall fashion
  async.waterfall([
    function grabImages (cb) {
      // Map the files into their image counterparts
      engineSmith.createImages(files, cb);
    },
    // Then, add the images to our canvas (dry run)
    function smithAddFiles (images, cb) {
      images.forEach(function (img) {
        // Save the non-padded properties as meta data
        var width = img.width,
            height = img.height,
            meta = {'img': img, 'actualWidth': width, 'actualHeight': height};

        // Add the item with padding to our layer
        layer.addItem({'width': width + padding, 'height': height + padding, 'meta': meta});
      });

      // Callback with nothing
      cb(null);
    },
    // Then, output the coordinates
    function smithOutputCoordinates (cb) {
      // Export and saved packedObj for later
      packedObj = layer['export']();

      // Extract the coordinates
      var coordinates = {},
          packedItems = packedObj.items;
      packedItems.forEach(function (item) {
        var meta = item.meta,
            img = meta.img,
            name = img._filepath;
        coordinates[name] = {
          'x': item.x,
          'y': item.y,
          'width': meta.actualWidth,
          'height': meta.actualHeight
        };
      });

      // Save the coordinates
      retObj.coordinates = coordinates;

      // Continue
      cb(null);
    },
    // Then, generate a canvas
    function generateCanvas (cb) {
      // Grab and fallback the width/height
      var width = Math.max(packedObj.width || 0, 0),
          height = Math.max(packedObj.height || 0, 0);

      // If there are items
      var itemsExist = packedObj.items.length;
      if (itemsExist) {
        // Remove the last item's padding
        width -= padding;
        height -= padding;
      }

      // Export the total width and height of the generated canvas
      retObj.properties = {
        width: width,
        height: height
      };

      // If there are items, generate the canvas
      if (itemsExist) {
        engine.createCanvas(width, height, cb);
      } else {
      // Otherwise, skip over potential errors/CPU
        cb(null, '');
      }
    },
    // Then, export the canvas
    function exportCanvas (canvas, cb) {
      // If there is no canvas, callback with an empty string
      var items = packedObj.items;
      if (!canvas) {
        return cb(null, '');
      }

      // Create a CanvasSmithy
      var canvasSmith = new CanvasSmith(canvas);

      // Add the images onto canvasSmith
      canvasSmith.addImages(items);

      // Export our canvas
      canvasSmith['export'](exportOpts, cb);
    },
    function saveImageToRetObj(imgStr, cb) {
      // Save the image to the retObj
      retObj.image = imgStr;

      // Callback
      cb(null);
    },
    function smithCallbackData (cb) {
      // Callback with the return object
      cb(null, retObj);
    }
  ], callback);
}

// Add the smiths to Spritesmith
Spritesmith.EngineSmith = EngineSmith;
Spritesmith.Layout = Layout;
Spritesmith.CanvasSmith = CanvasSmith;

/**
 * Method to add new engines via
 * @param {String} name Name of engine
 * @param {Function} engine Engine to bind under name
 */
function addEngine(name, engine) {
  engines[name] = engine;
}
Spritesmith.addEngine = addEngine;
Spritesmith.engines = engines;

// Attempt to load canvas and imagemagick
var canvasEngine,
    gmEngine,
    phantomjsEngine,
    pngEngine;
try {
  canvasEngine = require('canvassmith');
} catch (e) {}

try {
  gmEngine = require('gmsmith');
} catch (e) {}

try {
  phantomjsEngine = require('phantomjssmith');
} catch (e) {}

try {
  pngEngine = require('pngsmith');
} catch (e) {}

if (canvasEngine) { addEngine('canvas', canvasEngine); }
if (gmEngine) { addEngine('gm', gmEngine); }
if (phantomjsEngine) { addEngine('phantomjs', phantomjsEngine); }
if (pngEngine) { addEngine('pngsmith', pngEngine); }

// Export Spritesmith
module.exports = Spritesmith;
<commit_msg>Updated engine ordering<commit_after>var async = require('async'),
    assert = require('assert'),
    EngineSmith = require('./smiths/engine.smith.js'),
    Layout = require('layout'),
    CanvasSmith = require('./smiths/canvas.smith.js'),
    engines = {};

/**
 * Spritesmith generation function
 * @param {Object} params Parameters for spritesmith
 * @param {String[]} [params.src] Images to generate into sprite sheet
 * @param {String} [params.engine="auto"] Engine to use (phantomjs, canvas, gm, or user-defined via Spritesmith.addEngine)
 * @param {String} [params.algorithm="top-down"] Algorithm to pack images with (top-down or user-defined via Spritesmith.addAlgorithm)
 * @param {Number} [params.padding] Padding to use between images
 * @param {Mixed} [params.exportOpts] Options to pass through to engine for export
 * @param {Function} callback Function that receives compiled spritesheet and map
 * @returns {Mixed} callback[0] err If an error was encountered, this will be returned to callback
 * @returns {Object} callback[1] result Result object of spritesmith
 * @returns {String} callback[1].image Binary string representation of image
 * @returns {Object} callback[1].coordinates Map from file name to an object containing x, y, height, and width information about the source image
 */
function Spritesmith(params, callback) {
  var retObj = {},
      files = params.src,
      enginePref = params.engine || 'auto',
      engine = engines[enginePref],
      algorithmPref = params.algorithm || 'top-down';

  // If the engine is not defined
  if (engine === undefined) {
    // If the engine was not auto, inform the user
    assert.strictEqual(enginePref, 'auto', 'Sorry, the spritesmith engine \'' + enginePref + '\' could not be loaded. Please be sure you have installed it properly on your machine.');

    // Begin attempting to load the engines (in order of hardest to easiest)
    engine = engines.canvas || engines.gm || engines.phantomjs || engine['png-engine'];

    // Assert there is an engine
    assert(engine, 'Sorry, no spritesmith engine could be loaded for your machine. Please be sure you have installed canvas or gm.');
  }

  // If there is a set parameter for the engine, use it
  if (engine.set) {
    var engineOpts = params.engineOpts || {};
    engine.set(engineOpts);
  }

  // Create our smiths
  var engineSmith = new EngineSmith(engine),
      layer = new Layout(algorithmPref),
      padding = params.padding || 0,
      exportOpts = params.exportOpts || {},
      packedObj;

  // In a waterfall fashion
  async.waterfall([
    function grabImages (cb) {
      // Map the files into their image counterparts
      engineSmith.createImages(files, cb);
    },
    // Then, add the images to our canvas (dry run)
    function smithAddFiles (images, cb) {
      images.forEach(function (img) {
        // Save the non-padded properties as meta data
        var width = img.width,
            height = img.height,
            meta = {'img': img, 'actualWidth': width, 'actualHeight': height};

        // Add the item with padding to our layer
        layer.addItem({'width': width + padding, 'height': height + padding, 'meta': meta});
      });

      // Callback with nothing
      cb(null);
    },
    // Then, output the coordinates
    function smithOutputCoordinates (cb) {
      // Export and saved packedObj for later
      packedObj = layer['export']();

      // Extract the coordinates
      var coordinates = {},
          packedItems = packedObj.items;
      packedItems.forEach(function (item) {
        var meta = item.meta,
            img = meta.img,
            name = img._filepath;
        coordinates[name] = {
          'x': item.x,
          'y': item.y,
          'width': meta.actualWidth,
          'height': meta.actualHeight
        };
      });

      // Save the coordinates
      retObj.coordinates = coordinates;

      // Continue
      cb(null);
    },
    // Then, generate a canvas
    function generateCanvas (cb) {
      // Grab and fallback the width/height
      var width = Math.max(packedObj.width || 0, 0),
          height = Math.max(packedObj.height || 0, 0);

      // If there are items
      var itemsExist = packedObj.items.length;
      if (itemsExist) {
        // Remove the last item's padding
        width -= padding;
        height -= padding;
      }

      // Export the total width and height of the generated canvas
      retObj.properties = {
        width: width,
        height: height
      };

      // If there are items, generate the canvas
      if (itemsExist) {
        engine.createCanvas(width, height, cb);
      } else {
      // Otherwise, skip over potential errors/CPU
        cb(null, '');
      }
    },
    // Then, export the canvas
    function exportCanvas (canvas, cb) {
      // If there is no canvas, callback with an empty string
      var items = packedObj.items;
      if (!canvas) {
        return cb(null, '');
      }

      // Create a CanvasSmithy
      var canvasSmith = new CanvasSmith(canvas);

      // Add the images onto canvasSmith
      canvasSmith.addImages(items);

      // Export our canvas
      canvasSmith['export'](exportOpts, cb);
    },
    function saveImageToRetObj(imgStr, cb) {
      // Save the image to the retObj
      retObj.image = imgStr;

      // Callback
      cb(null);
    },
    function smithCallbackData (cb) {
      // Callback with the return object
      cb(null, retObj);
    }
  ], callback);
}

// Add the smiths to Spritesmith
Spritesmith.EngineSmith = EngineSmith;
Spritesmith.Layout = Layout;
Spritesmith.CanvasSmith = CanvasSmith;

/**
 * Method to add new engines via
 * @param {String} name Name of engine
 * @param {Function} engine Engine to bind under name
 */
function addEngine(name, engine) {
  engines[name] = engine;
}
Spritesmith.addEngine = addEngine;
Spritesmith.engines = engines;

// Attempt to load canvas and imagemagick
var canvasEngine,
    gmEngine,
    phantomjsEngine,
    pngEngine;
try {
  canvasEngine = require('canvassmith');
} catch (e) {}

try {
  gmEngine = require('gmsmith');
} catch (e) {}

try {
  phantomjsEngine = require('phantomjssmith');
} catch (e) {}

try {
  pngEngine = require('pngsmith');
} catch (e) {}

if (canvasEngine) { addEngine('canvas', canvasEngine); }
if (gmEngine) { addEngine('gm', gmEngine); }
if (phantomjsEngine) { addEngine('phantomjs', phantomjsEngine); }
if (pngEngine) { addEngine('pngsmith', pngEngine); }

// Export Spritesmith
module.exports = Spritesmith;
<|endoftext|>


<commit_before>class Space {
  constructor(iterable) {
    if (!iterable) {
      throw new Error();
    }
    this.table = new Map(iterable);
  }

  toString() {
    let object = Object.create(null);
    for (let [key, value] of this.table) {
      object[key] = value;
    }
    return JSON.stringify(object);
  }

  isValid() {
    return false;
  }

  clone() {
    throw new Error("clone() is not implemented");
  }

  hex() {
    throw new Error("hex() is not implemented");
  }

  rgb() {
    throw new Error("rgb() is not implemented");
  }

  hsl() {
    throw new Error("hsl() is not implemented");
  }

  hsv() {
    throw new Error("hsv() is not implemented");
  }

  cmyk() {
    throw new Error("cmyk() is not implemented");
  }

  get(key) {
    return this.table.get(key);
  }

  set(key, value) {
    return this.table.set(key, value);
  }

  getOrSet(key, value = null) {
    if (value == null) {
      return this.get(key);
    }
    this.set(key, value);
    return this;
  }

  values() {
    return this.table.values();
  }
}

export class Rgb extends Space {
  static KEY_R = "r";
  static KEY_G = "g";
  static KEY_B = "b";

  constructor(r, g, b) {
    super([[Rgb.KEY_R, r], [Rgb.KEY_G, g], [Rgb.KEY_B, b]]);
  }

  toString() {
    return `rgb(${this.r()}, ${this.g()}, ${this.b()})`;
  }

  isValid() {
    let values = this.values();
    for (let value of values) {
      if (value < 0x00 || value > 0xFF) {
        return false;
      }
    }
    return true;
  }

  clone() {
    return new Rgb(this.r(), this.g(), this.b());
  }

  r(value = null) {
    return this.getOrSet(Rgb.KEY_R, value);
  }

  g(value = null) {
    return this.getOrSet(Rgb.KEY_G, value);
  }

  b(value = null) {
    return this.getOrSet(Rgb.KEY_B, value);
  }

  hex() {
    let parts = [];
    for (let value of this.values()) {
      let text = value.toString(16);
      if (text.length === 1) {
        text = `0${text}`;
      }
      parts.push(text.toUppderCase());
    }
    return `#${parts.join('')}`;
  }

  rgb() {
    return this;
  }

  hsl() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let max   = Math.max(r, g, b);
    let min   = Math.min(r, g, b);
    let delta = max - min;
    if (delta === 0) {
      return new Hsl(0, 0, max);
    }

    let h = 0;
    if (max === r) {
      h = 60 * ((g - b) / delta % 360);
    } else if (max === g) {
      h = 60 * ((b - r) / delta + 2);
    } else if (max === b) {
      h = 60 * ((r - g) / delta + 4);
    }

    let l = (max + min) / 2;

    let s = delta / (1 - Math.abs(2 * l - 1))

    return new Hsl(h, s, l);
  }

  hsv() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let max   = Math.max(r, g, b);
    let min   = Math.min(r, g, b);
    let delta = max - min;
    if (delta === 0) {
      return new Hsv(0, 0, max);
    }

    let h = 0;
    if (max === r) {
      h = 60 * (((g - b) / delta) % 6);
    } else if (max === g) {
      h = 60 * (((b - r) / delta) + 2);
    } else if (max === b) {
      h = 60 * (((r - g) / delta) + 4);
    }

    let s = 0;
    if (max !== 0) {
      s = delta / max;
    }

    let v = max;
    return new Hsv(h, s, v);
  }

  cmyk() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let k = 1 - Math.max(r, g, b);
    if (k === 1) {
      return new Cmyk(0, 0, 0, 1);
    }

    let c = (1 - r - k) / (1 - k);
    let m = (1 - g - k) / (1 - k);
    let y = (1 - b - k) / (1 - k);
    return new Cmyk(c, m, y, k);
  }
}

export class Hsl extends Space {
  static KEY_H = "h";
  static KEY_S = "s";
  static KEY_L = "l";

  constructor(h, s, l) {
    super([[Hsl.KEY_H, h], [Hsl.KEY_S, s], [Hsl.KEY_L, l]]);
  }

  toString() {
    return `hsl(${this.h()}, ${this.s()}%, ${this.l()}%)`;
  }

  clone() {
    return new Hsl(this.h(), this.s(), this.l());
  }

  isValid() {
    let h = this.h();
    if (!Number.isFinite(h) || h < 0 || h > 360) {
      return false;
    }

    let s = this.s();
    if (!Number.isFinite(s) || s < 0 || s > 100) {
      return false;
    }

    let l = this.l();
    if (!Number.isFinite(l) || l < 0 || l > 100) {
      return false;
    }
    return true;
  }

  h(value = null) {
    return this.getOrSet(Hsl.KEY_H, value);
  }

  s(value = null) {
    return this.getOrSet(Hsl.KEY_S, value);
  }

  l(value = null) {
    return this.getOrSet(Hsl.KEY_L, value);
  }

  hex() {
    return this.rgb().hex();
  }

  rgb() {
    let h = this.h();
    let s = this.s();
    let l = this.l();
    if (s === 0) {
      let value = Math.round(l * 0xFF);
      return new Rgb(value, value, value);
    }

    let m2 = 0;
    if (l < 0.5) {
      m2 = l * (s + 1);
    } else {
      m2 = l + s - l * s;
    }

    let m1 = 2 * l - m2;
    let r = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360 + 1 / 3));
    let g = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360));
    let b = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360 - 1 / 3));
    return new Rgb(r, g, b)
  }

  hsl() {
    return this;
  }

  hsv() {
    return this.rgb().hsv();
  }

  cmyk() {
    return this.rgb().cmyk();
  }

  static hueToRgb(m1, m2, h) {
    if (h < 0) {
      h += 1;
    } else if (h > 1) {
      h -= 1;
    }

    if (h * 6 < 1) {
      return m1 + (m2 - m1) * h * 6;
    } else if (h * 2 < 1) {
      return m2;
    } else if (h * 3 < 2) {
      return m1 + (m2 - m1) * (2 / 3 - h) * 6;
    }
    return m1;
  }
}

export class Hsv extends Space {
  static KEY_H = 'h';
  static KEY_S = 's';
  static KEY_V = 'v';

  constructor(h, s, v) {
    super([[Hsv.KEY_H, h], [Hsv.KEY_S, s], [Hsv.KEY_V, v]]);
  }

  isValid() {
    let h = this.h();
    if (!Number.isFinite(h) || h < 0 || h > 360) {
      return false;
    }

    let s = this.s();
    if (!Number.isFinite(s) || s < 0 || s > 1) {
      return false;
    }

    let v = this.v();
    if (!Number.isFinite(v) || v < 0 || v > 1) {
      return false;
    }
    return true;
  }

  clone() {
    return new Hsv(...this.values());
  }

  h(value = null) {
    return this.getOrSet(Hsv.KEY_H, value);
  }

  s(value = null) {
    return this.getOrSet(Hsv.KEY_S, value);
  }

  v(value = null) {
    return this.getOrSet(Hsv.KEY_V, value);
  }

  hex() {
    return this.rgb().hex();
  }

  rgb() {
    let h = this.h();
    let s = this.s();
    let v = this.v();

    let c = v * s;
    let x = c * (1 - Math.abs(((h / 60) % 2) - 1));
    let m = v - c;

    // TODO: Need to rafactor the following switch statement
    let [r, g, b] = [0, 0, 0];
    switch (!!(h / 60)) {
      case 0:
        [r, g, b] = [c, x, 0];
        break;
      case 1:
        [r, g, b] = [x, c, 0];
        break;
      case 2:
        [r, g, b] = [0, c, x];
        break;
      case 3:
        [r, g, b] = [0, x, c];
        break;
      case 4:
        [r, g, b] = [x, 0, c];
        break;
      case 5:
        [r, g, b] = [c, 0, x];
        break;
      default:
        break;
    }

    return new Rgb((r + m) * 0xFF, (g + m) * 0xFF, (b + m) * 0xFF);
  }
}

export class Cmyk extends Space {
  static KEY_C = 'c';
  static KEY_M = 'm';
  static KEY_Y = 'y';
  static KEY_K = 'k';

  constructor(c, m, y, k) {
    super([[Cmyk.KEY_C, c], [Cmyk.KEY_M, m], [Cmyk.KEY_Y, y], [Cmyk.KEY_K, k]]);
  }

  isValid() {
    for (let value of this.values()) {
      if (!Number.isFinite(value)) {
        return false;
      }
      if (value < 0 || value > 1) {
        return false;
      }
    }
    return true;
  }

  clone() {
    return new Cmyk(this.c(), this.m(), this.y(), this.k());
  }

  c(value = null) {
    return this.getOrSet(Cmyk.KEY_C, value);
  }

  m(value = null) {
    return this.getOrSet(Cmyk.KEY_M, value);
  }

  y(value = null) {
    return this.getOrSet(Cmyk.KEY_Y, value);
  }

  k(value = null) {
    return this.getOrSet(Cmyk.KEY_K, value);
  }

  hex() {
    return this.rgb().hex();
  }

  rgb() {
    let k = this.k();
    if (k === 1) {
      return new Rgb(0, 0, 0);
    }

    let r = 0xFF * (1 - this.c()) * (1 - k);
    let g = 0xFF * (1 - this.m()) * (1 - k);
    let b = 0xFF * (1 - this.y()) * (1 - k);
    return new Rgb(r, g, b);
  }

  hsl() {
    return this.rgb().hsl();
  }

  hsv() {
    return this.rgb().hsv();
  }

  cmyk() {
    return this;
  }
}
<commit_msg>Added conversion methods that converts HSV to HSL or CMYK<commit_after>class Space {
  constructor(iterable) {
    if (!iterable) {
      throw new Error();
    }
    this.table = new Map(iterable);
  }

  toString() {
    let object = Object.create(null);
    for (let [key, value] of this.table) {
      object[key] = value;
    }
    return JSON.stringify(object);
  }

  isValid() {
    return false;
  }

  clone() {
    throw new Error("clone() is not implemented");
  }

  hex() {
    throw new Error("hex() is not implemented");
  }

  rgb() {
    throw new Error("rgb() is not implemented");
  }

  hsl() {
    throw new Error("hsl() is not implemented");
  }

  hsv() {
    throw new Error("hsv() is not implemented");
  }

  cmyk() {
    throw new Error("cmyk() is not implemented");
  }

  get(key) {
    return this.table.get(key);
  }

  set(key, value) {
    return this.table.set(key, value);
  }

  getOrSet(key, value = null) {
    if (value == null) {
      return this.get(key);
    }
    this.set(key, value);
    return this;
  }

  values() {
    return this.table.values();
  }
}

export class Rgb extends Space {
  static KEY_R = "r";
  static KEY_G = "g";
  static KEY_B = "b";

  constructor(r, g, b) {
    super([[Rgb.KEY_R, r], [Rgb.KEY_G, g], [Rgb.KEY_B, b]]);
  }

  toString() {
    return `rgb(${this.r()}, ${this.g()}, ${this.b()})`;
  }

  isValid() {
    let values = this.values();
    for (let value of values) {
      if (value < 0x00 || value > 0xFF) {
        return false;
      }
    }
    return true;
  }

  clone() {
    return new Rgb(this.r(), this.g(), this.b());
  }

  r(value = null) {
    return this.getOrSet(Rgb.KEY_R, value);
  }

  g(value = null) {
    return this.getOrSet(Rgb.KEY_G, value);
  }

  b(value = null) {
    return this.getOrSet(Rgb.KEY_B, value);
  }

  hex() {
    let parts = [];
    for (let value of this.values()) {
      let text = value.toString(16);
      if (text.length === 1) {
        text = `0${text}`;
      }
      parts.push(text.toUppderCase());
    }
    return `#${parts.join('')}`;
  }

  rgb() {
    return this;
  }

  hsl() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let max   = Math.max(r, g, b);
    let min   = Math.min(r, g, b);
    let delta = max - min;
    if (delta === 0) {
      return new Hsl(0, 0, max);
    }

    let h = 0;
    if (max === r) {
      h = 60 * ((g - b) / delta % 360);
    } else if (max === g) {
      h = 60 * ((b - r) / delta + 2);
    } else if (max === b) {
      h = 60 * ((r - g) / delta + 4);
    }

    let l = (max + min) / 2;

    let s = delta / (1 - Math.abs(2 * l - 1))

    return new Hsl(h, s, l);
  }

  hsv() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let max   = Math.max(r, g, b);
    let min   = Math.min(r, g, b);
    let delta = max - min;
    if (delta === 0) {
      return new Hsv(0, 0, max);
    }

    let h = 0;
    if (max === r) {
      h = 60 * (((g - b) / delta) % 6);
    } else if (max === g) {
      h = 60 * (((b - r) / delta) + 2);
    } else if (max === b) {
      h = 60 * (((r - g) / delta) + 4);
    }

    let s = 0;
    if (max !== 0) {
      s = delta / max;
    }

    let v = max;
    return new Hsv(h, s, v);
  }

  cmyk() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let k = 1 - Math.max(r, g, b);
    if (k === 1) {
      return new Cmyk(0, 0, 0, 1);
    }

    let c = (1 - r - k) / (1 - k);
    let m = (1 - g - k) / (1 - k);
    let y = (1 - b - k) / (1 - k);
    return new Cmyk(c, m, y, k);
  }
}

export class Hsl extends Space {
  static KEY_H = "h";
  static KEY_S = "s";
  static KEY_L = "l";

  constructor(h, s, l) {
    super([[Hsl.KEY_H, h], [Hsl.KEY_S, s], [Hsl.KEY_L, l]]);
  }

  toString() {
    return `hsl(${this.h()}, ${this.s()}%, ${this.l()}%)`;
  }

  clone() {
    return new Hsl(this.h(), this.s(), this.l());
  }

  isValid() {
    let h = this.h();
    if (!Number.isFinite(h) || h < 0 || h > 360) {
      return false;
    }

    let s = this.s();
    if (!Number.isFinite(s) || s < 0 || s > 100) {
      return false;
    }

    let l = this.l();
    if (!Number.isFinite(l) || l < 0 || l > 100) {
      return false;
    }
    return true;
  }

  h(value = null) {
    return this.getOrSet(Hsl.KEY_H, value);
  }

  s(value = null) {
    return this.getOrSet(Hsl.KEY_S, value);
  }

  l(value = null) {
    return this.getOrSet(Hsl.KEY_L, value);
  }

  hex() {
    return this.rgb().hex();
  }

  rgb() {
    let h = this.h();
    let s = this.s();
    let l = this.l();
    if (s === 0) {
      let value = Math.round(l * 0xFF);
      return new Rgb(value, value, value);
    }

    let m2 = 0;
    if (l < 0.5) {
      m2 = l * (s + 1);
    } else {
      m2 = l + s - l * s;
    }

    let m1 = 2 * l - m2;
    let r = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360 + 1 / 3));
    let g = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360));
    let b = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360 - 1 / 3));
    return new Rgb(r, g, b)
  }

  hsl() {
    return this;
  }

  hsv() {
    return this.rgb().hsv();
  }

  cmyk() {
    return this.rgb().cmyk();
  }

  static hueToRgb(m1, m2, h) {
    if (h < 0) {
      h += 1;
    } else if (h > 1) {
      h -= 1;
    }

    if (h * 6 < 1) {
      return m1 + (m2 - m1) * h * 6;
    } else if (h * 2 < 1) {
      return m2;
    } else if (h * 3 < 2) {
      return m1 + (m2 - m1) * (2 / 3 - h) * 6;
    }
    return m1;
  }
}

export class Hsv extends Space {
  static KEY_H = 'h';
  static KEY_S = 's';
  static KEY_V = 'v';

  constructor(h, s, v) {
    super([[Hsv.KEY_H, h], [Hsv.KEY_S, s], [Hsv.KEY_V, v]]);
  }

  isValid() {
    let h = this.h();
    if (!Number.isFinite(h) || h < 0 || h > 360) {
      return false;
    }

    let s = this.s();
    if (!Number.isFinite(s) || s < 0 || s > 1) {
      return false;
    }

    let v = this.v();
    if (!Number.isFinite(v) || v < 0 || v > 1) {
      return false;
    }
    return true;
  }

  clone() {
    return new Hsv(...this.values());
  }

  h(value = null) {
    return this.getOrSet(Hsv.KEY_H, value);
  }

  s(value = null) {
    return this.getOrSet(Hsv.KEY_S, value);
  }

  v(value = null) {
    return this.getOrSet(Hsv.KEY_V, value);
  }

  hex() {
    return this.rgb().hex();
  }

  rgb() {
    let h = this.h();
    let s = this.s();
    let v = this.v();

    let c = v * s;
    let x = c * (1 - Math.abs(((h / 60) % 2) - 1));
    let m = v - c;

    // TODO: Need to rafactor the following switch statement
    let [r, g, b] = [0, 0, 0];
    switch (!!(h / 60)) {
      case 0:
        [r, g, b] = [c, x, 0];
        break;
      case 1:
        [r, g, b] = [x, c, 0];
        break;
      case 2:
        [r, g, b] = [0, c, x];
        break;
      case 3:
        [r, g, b] = [0, x, c];
        break;
      case 4:
        [r, g, b] = [x, 0, c];
        break;
      case 5:
        [r, g, b] = [c, 0, x];
        break;
      default:
        break;
    }

    return new Rgb((r + m) * 0xFF, (g + m) * 0xFF, (b + m) * 0xFF);
  }

  hsl() {
    return this.rgb().hsl();
  }

  hsv() {
    return this;
  }

  cmyk() {
    return this.rgb().cmyk();
  }
}

export class Cmyk extends Space {
  static KEY_C = 'c';
  static KEY_M = 'm';
  static KEY_Y = 'y';
  static KEY_K = 'k';

  constructor(c, m, y, k) {
    super([[Cmyk.KEY_C, c], [Cmyk.KEY_M, m], [Cmyk.KEY_Y, y], [Cmyk.KEY_K, k]]);
  }

  isValid() {
    for (let value of this.values()) {
      if (!Number.isFinite(value)) {
        return false;
      }
      if (value < 0 || value > 1) {
        return false;
      }
    }
    return true;
  }

  clone() {
    return new Cmyk(this.c(), this.m(), this.y(), this.k());
  }

  c(value = null) {
    return this.getOrSet(Cmyk.KEY_C, value);
  }

  m(value = null) {
    return this.getOrSet(Cmyk.KEY_M, value);
  }

  y(value = null) {
    return this.getOrSet(Cmyk.KEY_Y, value);
  }

  k(value = null) {
    return this.getOrSet(Cmyk.KEY_K, value);
  }

  hex() {
    return this.rgb().hex();
  }

  rgb() {
    let k = this.k();
    if (k === 1) {
      return new Rgb(0, 0, 0);
    }

    let r = 0xFF * (1 - this.c()) * (1 - k);
    let g = 0xFF * (1 - this.m()) * (1 - k);
    let b = 0xFF * (1 - this.y()) * (1 - k);
    return new Rgb(r, g, b);
  }

  hsl() {
    return this.rgb().hsl();
  }

  hsv() {
    return this.rgb().hsv();
  }

  cmyk() {
    return this;
  }
}
<|endoftext|>


<commit_before>import constant from "./constant.js";
import offsetNone from "./offset/none.js";
import orderNone from "./order/none.js";

function stackValue(d, key) {
  return d[key];
}

function stackSeries(key) {
  const series = [];
  series.key = key;
  return series;
}

export default function() {
  var keys = constant([]),
      order = orderNone,
      offset = offsetNone,
      value = stackValue;

  function stack(data) {
    var kz = keys.apply(this, arguments),
        sz = kz.map(stackSeries),
        i, n = sz.length, m = -1,
        oz;

    for (const d of data) {
      for (i = 0, ++m; i < n; ++i) {
        (sz[i][m] = [0, +value(d, kz[i], m, data)]).data = d;
      }
    }

    for (i = 0, oz = order(sz); i < n; ++i) {
      sz[oz[i]].index = i;
    }

    offset(sz, oz);
    return sz;
  }

  stack.keys = function(_) {
    return arguments.length ? (keys = typeof _ === "function" ? _ : constant(Array.from(_)), stack) : keys;
  };

  stack.value = function(_) {
    return arguments.length ? (value = typeof _ === "function" ? _ : constant(+_), stack) : value;
  };

  stack.order = function(_) {
    return arguments.length ? (order = _ == null ? orderNone : typeof _ === "function" ? _ : constant(Array.from(_)), stack) : order;
  };

  stack.offset = function(_) {
    return arguments.length ? (offset = _ == null ? offsetNone : _, stack) : offset;
  };

  return stack;
}
<commit_msg>Rename.<commit_after>import constant from "./constant.js";
import offsetNone from "./offset/none.js";
import orderNone from "./order/none.js";

function stackValue(d, key) {
  return d[key];
}

function stackSeries(key) {
  const series = [];
  series.key = key;
  return series;
}

export default function() {
  var keys = constant([]),
      order = orderNone,
      offset = offsetNone,
      value = stackValue;

  function stack(data) {
    var kz = keys.apply(this, arguments),
        sz = kz.map(stackSeries),
        i, n = sz.length, j = -1,
        oz;

    for (const d of data) {
      for (i = 0, ++j; i < n; ++i) {
        (sz[i][j] = [0, +value(d, kz[i], j, data)]).data = d;
      }
    }

    for (i = 0, oz = order(sz); i < n; ++i) {
      sz[oz[i]].index = i;
    }

    offset(sz, oz);
    return sz;
  }

  stack.keys = function(_) {
    return arguments.length ? (keys = typeof _ === "function" ? _ : constant(Array.from(_)), stack) : keys;
  };

  stack.value = function(_) {
    return arguments.length ? (value = typeof _ === "function" ? _ : constant(+_), stack) : value;
  };

  stack.order = function(_) {
    return arguments.length ? (order = _ == null ? orderNone : typeof _ === "function" ? _ : constant(Array.from(_)), stack) : order;
  };

  stack.offset = function(_) {
    return arguments.length ? (offset = _ == null ? offsetNone : _, stack) : offset;
  };

  return stack;
}
<|endoftext|>


<commit_before>/**
 * @file this file is loaded on application start and initializes the application
 * @link https://github.com/datarhei/restreamer
 * @copyright 2015 datarhei.org
 * @license Apache-2.0
 */
'use strict';
const path = require('path');

global.__src = __dirname;
global.__base = path.join(__dirname, '..');
global.__public = path.join(__dirname, 'webserver', 'public');

const logger = require('./classes/Logger')('start');
const EnvVar = require('./classes/EnvVar');
const packageJson = require(path.join('..', 'package.json'));
const config = require(path.join(global.__base, 'conf', 'live.json'));
const nginxrtmp = require('./classes/Nginxrtmp')(config);
const Q = require('q');
const Restreamer = require('./classes/Restreamer');
const RestreamerData = require('./classes/RestreamerData');
const restreamerApp = require('./webserver/app');

// show start message
logger.info('     _       _             _           _ ', false);
logger.info('  __| | __ _| |_ __ _ _ __| |___   ___(_)', false);
logger.info(' / _  |/ _  | __/ _  |  __|  _  |/  _ | |', false);
logger.info('| (_| | (_| | || (_| | |  | | | |  __/| |', false);
logger.info('|_____|_____|_||_____|_|  |_| |_|____||_|', false);
logger.info('', false);
logger.info('Restreamer v' + packageJson.version, false);
logger.info('', false);
logger.info('ENVIRONMENTS', false);
logger.info('More information in our Docs', false);
logger.info('', false);

// setup environment vars
EnvVar.init(config);

// ffmpeg reporting
if(process.env.RS_FFMPEG_DEBUG == true) {
    process.env['FFREPORT'] = 'file=/restreamer/src/webserver/public/debug/%p-%t.log:level=40';
}

// start the app
nginxrtmp.start()
    .then(() => {
        return RestreamerData.checkJSONDb();
    })
    .then(() => {
        Restreamer.checkForUpdates();
        Restreamer.getPublicIp();
        Restreamer.bindWebsocketEvents();
        return restreamerApp.startWebserver();
    })
    .then(() => {
        return Q.fcall(Restreamer.restoreFFMpegProcesses);
    })
    .catch((error) => {
        logger.error('Error starting webserver and nginx for application: ' + error);
    });
<commit_msg>Fix FFMPEG_DEBUG environment variable<commit_after>/**
 * @file this file is loaded on application start and initializes the application
 * @link https://github.com/datarhei/restreamer
 * @copyright 2015 datarhei.org
 * @license Apache-2.0
 */
'use strict';
const path = require('path');

global.__src = __dirname;
global.__base = path.join(__dirname, '..');
global.__public = path.join(__dirname, 'webserver', 'public');

const logger = require('./classes/Logger')('start');
const EnvVar = require('./classes/EnvVar');
const packageJson = require(path.join('..', 'package.json'));
const config = require(path.join(global.__base, 'conf', 'live.json'));
const nginxrtmp = require('./classes/Nginxrtmp')(config);
const Q = require('q');
const Restreamer = require('./classes/Restreamer');
const RestreamerData = require('./classes/RestreamerData');
const restreamerApp = require('./webserver/app');

// show start message
logger.info('     _       _             _           _ ', false);
logger.info('  __| | __ _| |_ __ _ _ __| |___   ___(_)', false);
logger.info(' / _  |/ _  | __/ _  |  __|  _  |/  _ | |', false);
logger.info('| (_| | (_| | || (_| | |  | | | |  __/| |', false);
logger.info('|_____|_____|_||_____|_|  |_| |_|____||_|', false);
logger.info('', false);
logger.info('Restreamer v' + packageJson.version, false);
logger.info('', false);
logger.info('ENVIRONMENTS', false);
logger.info('More information in our Docs', false);
logger.info('', false);

// setup environment vars
EnvVar.init(config);

// ffmpeg reporting
if(process.env.RS_FFMPEG_DEBUG == 'true') {
    process.env['FFREPORT'] = 'file=/restreamer/src/webserver/public/debug/%p-%t.log';
}

// start the app
nginxrtmp.start()
    .then(() => {
        return RestreamerData.checkJSONDb();
    })
    .then(() => {
        Restreamer.checkForUpdates();
        Restreamer.getPublicIp();
        Restreamer.bindWebsocketEvents();
        return restreamerApp.startWebserver();
    })
    .then(() => {
        return Q.fcall(Restreamer.restoreFFMpegProcesses);
    })
    .catch((error) => {
        logger.error('Error starting webserver and nginx for application: ' + error);
    });
<|endoftext|>


<commit_before>var Utils = require('./utils');
var Loc = require('./loc');
var Wait = require('./wait');
var Diff = require('levenlistdiff');
var P = Utils.P;
var debug = Utils.debug;

module.exports = State;

var INIT = "init";
var READY = "ready";
var BUILD = "build";
var PATCH = "patch";
var SETUP = "setup";
var CLOSE = "close";
var ERROR = "error";
var HASH = "hash";
var Stages = [INIT, READY, BUILD, PATCH, SETUP, HASH, ERROR, CLOSE];

function State() {
	this.data = {};
	this.chains = {};
}

State.prototype.init = function(W) {
	var state = this;
	Stages.forEach(function(stage) {
		W[stage] = function(fn) {
			return state.chain(stage, fn);
		};
		W['un' + stage] = function(fn) {
			return state.unchain(stage, fn);
		};
	});
	state.reload = function() {
		return W.reload(state);
	};
	state.replace = function(loc) {
		return W.replace(loc, state);
	};
	state.push = function(loc) {
		return W.push(loc, state);
	};
	state.save = function() {
		return W.save(state);
	};
};

function stamp(stage) {
	var root = document.documentElement;
	if (stage != null) root.setAttribute('data-page-stage', stage);
	else stage = root.dataset.pageStage;
	return stage;
}

State.prototype.run = function(W) {
	var state = this;
	var refer = this.referrer;
	if (!refer) {
		if (document.referrer) {
			refer = Loc.parse(document.referrer);
		} else {
			refer = new State();
		}
		this.referrer = refer;
	}
	return Wait.dom().then(function() {
		return state.runChain(INIT);
	}).then(function() {
		if (refer.stage == SETUP && refer.pathname != state.pathname) {
			return refer.runChain(CLOSE);
		}
	}).then(function() {
		// it is up to the default router to NOT load a document upon first load
		// other routers might choose to do otherwise
		if (refer.pathname != state.pathname || !refer.stage) {
			return W.router(state, refer);
		} else {
			if (!state.emitter) state.emitter = refer.emitter;
		}
	}).then(function(doc) {
		return state.load(doc || document);
	}).then(function() {
		if (!state.initial) state.initial = stamp() || INIT;
		state.stage = state.initial;
		debug("doc ready at stage", state.stage);
		return state.runChain(READY);
	}).then(function() {
		if (state.initial != INIT) return;
		return (state.runChain(BUILD) || P()).then(function() {
			return state.runChain(PATCH);
		}).then(function() {
			stamp(BUILD);
		});
	}).then(function() {
		if (state.initial == SETUP) {
			if (!Loc.samePath(state, refer)) {
				return state.runChain(PATCH) || state.runChain(BUILD);
			}
		} else return Wait.ui().then(function() {
			if (state._abort) return Promise.reject("abort");
			return (state.runChain(SETUP) || P()).then(function() {
				stamp(SETUP);
			});
		});
	}).then(function() {
		if (state.hash != refer.hash) return state.runChain(HASH);
	}).catch(function(err) {
		delete state._abort;
		if (err != "abort") {
			// eslint-disable-next-line no-console
			if (typeof err != "number") console.error(err);
			state.error = err;
			return state.runChain(ERROR)
		}
	}).then(function() {
		return state;
	});
};

State.prototype.abort = function() {
	if (this.stage != BUILD) return false;
	this._abort = true;
	return true;
};

State.prototype.emit = function(name) {
	var e = new CustomEvent(name, {
		view: window,
		bubbles: true,
		cancelable: true,
		detail: this
	});
	Utils.all(document, 'script').forEach(function(node) {
		node.dispatchEvent(e);
	});
	if (this.emitter) this.emitter.dispatchEvent(e);
};

State.prototype.runChain = function(name) {
	this.stage = name;
	var chain = this.chains[name];
	if (!chain) chain = this.chains[name] = {};
	debug("run chain", name);
	chain.count = 0;
	chain.promise = P();
	this.emit("page" + name);
	debug("run chain count", name, chain.count);
	if (chain.count) return chain.promise;
};

State.prototype.chain = function(stage, fn) {
	var state = this;
	var ls = fn.pageListeners;
	if (!ls) ls = fn.pageListeners = {};
	var lfn = ls[stage];
	var emitter = document.currentScript;
	if (!emitter) {
		emitter = state.emitter;
		if (!emitter) emitter = state.emitter = document.createElement('div');
	}

	if (!lfn) {
		lfn = ls[stage] = {
			fn: chainListener(stage, fn),
			em: emitter
		};
		emitter.addEventListener('page' + stage, lfn.fn);
	} else {
		debug("already chained", stage, fn);
	}
	var p;
	var curNum = state.stage ? Stages.indexOf(state.stage) : 0;
	var tryNum = Stages.indexOf(stage);
	if (tryNum <= curNum) {
		debug("chain has run, execute fn now", stage);
		p = new Promise(function(resolve) {
			setTimeout(resolve);
		}).then(function() {
			return fn(state);
		});
	} else {
		debug("chain pending", stage);
		p = P();
	}
	return p;
};

State.prototype.unchain = function(stage, fn) {
	var ls = fn.pageListeners;
	if (!ls) return;
	var lfn = ls[stage];
	if (!lfn) return;
	delete ls[stage];
	lfn.em.removeEventListener('page' + stage, lfn.fn);
};

function chainListener(stage, fn) {
	return function(e) {
		var state = e.detail;
		var chain = state.chains[stage];
		if (chain.count == null) chain.count = 0;
		chain.count++;
		chain.promise = chain.promise.then(function() {
			return fn(e.detail);
		}).catch(function(err) {
			// eslint-disable-next-line no-console
			console.error("Uncaught error during", stage, err, fn);
		});
	};
}

State.prototype.load = function(doc) {
	if (doc == document) {
		Utils.trackListeners(document.body);
		return P();
	}
	Utils.clearListeners(document.body);
	debug("Import new document");
	var states = {};
	var selector = 'script:not([type]),script[type="text/javascript"],link[rel="import"]';
	Utils.all(document, selector).forEach(function(node) {
		var src = node.src || node.href;
		if (src) states[src] = true;
	});

	// if there is no HTMLImports support, some loaded script might contain
	// the HTMLImports polyfill itself, which will load imports however it likes
	// so it's hard to decide which order is good, and it's also impossible to know
	// if that polyfill will be available - so state.load does not preload
	// imports nor does it let them run on insert
	// if there is native support then it's like other resources.

	var nodes = Utils.all(doc, selector);

	nodes.forEach(function(node) {
		// just preload everything
		if (node.nodeName == "SCRIPT") {
			node.setAttribute('type', "none");
		} else if (node.nodeName == "LINK") {
			node.setAttribute('rel', 'none');
			if (!node.import) return; // polyfill already do preloading
		}
		var src = node.src || node.href;
		if (!src) return;
		if (states[src] === true) return;
		// not data-uri
		if (src.slice(0, 5) == 'data:') return;
		states[src] = Utils.get(src, 400).then(function() {
			debug("preloaded", src);
		}).catch(function(err) {
			debug("not preloaded", src, err);
		});
	});

	function loadNode(node) {
		var p = P();
		var src = node.src || node.href;
		var state = states[src];
		var old = state === true;
		var loader = !old && state;
		if (loader) {
			p = p.then(loader);
		}
		return p.then(function() {
			var parent = node.parentNode;
			var cursor;
			if (!old) {
				cursor = document.createTextNode("");
				parent.insertBefore(cursor, node);
				parent.removeChild(node);
			}
			if (node.nodeName == "LINK") {
				node.setAttribute('rel', 'import');
			} else if (node.nodeName == "SCRIPT") {
				node.removeAttribute('type');
			}
			if (old) return;
			var copy = document.createElement(node.nodeName);
			for (var i=0; i < node.attributes.length; i++) {
				copy.setAttribute(node.attributes[i].name, node.attributes[i].value);
			}
			if (node.textContent) copy.textContent = node.textContent;
			var rp;
			if (src) {
				debug("async node loading", src);
				if (node.nodeName == "LINK" && !node.import) {
					debug("not loading import", src);
				} else {
					rp = Wait.node(copy);
				}
			} else {
				debug("inline node loading");
				rp = new Promise(function(resolve) {
					setTimeout(resolve);
				});
			}
			parent.insertBefore(copy, cursor);
			parent.removeChild(cursor);
			if (rp) return rp;
		});
	}

	var root = document.documentElement;
	var nroot = document.adoptNode(doc.documentElement);
	var head = nroot.querySelector('head');
	var body = nroot.querySelector('body');

	Utils.trackListeners(body);

	var atts = nroot.attributes;

	for (var i=0; i < atts.length; i++) {
		root.setAttribute(atts[i].name, atts[i].value);
	}
	atts = Array.prototype.slice.call(root.attributes);
	for (var j=0; j < atts.length; j++) {
		if (!nroot.hasAttribute(atts[j].name)) nroot.removeAttribute(atts[j].name);
	}

	var parallels = Wait.styles(head, document.head);
	var serials = Utils.all(nroot, 'script[type="none"],link[rel="none"]');

	var state = this;

	return P().then(function() {
		state.setHead(head);
		return parallels;
	}).then(function() {
		return P().then(function() {
			return state.setBody(body);
		});
	}).then(function() {
		// scripts must be run in order
		var p = P();
		serials.forEach(function(node) {
			p = p.then(function() {
				return loadNode(node);
			});
		});
		return p;
	});
};

State.prototype.setHead = function(node) {
	this.updateAttributes(document.head, node);
	this.updateChildren(document.head, node);
};

State.prototype.setBody = function(node) {
	document.body.parentNode.replaceChild(node, document.body);
};

State.prototype.updateAttributes = function(from, to) {
	var attFrom = from.attributes;
	var attTo = to.attributes;
	Diff(attFrom, attTo, function(att) {
		return att.name + "_" + att.value;
	}).forEach(function(patch) {
		var att = attFrom[patch.index];
		switch (patch.type) {
		case Diff.INSERTION:
			if (patch.item.value) {
				from.setAttribute(patch.item.name, patch.item.value);
			}
			break;
		case Diff.SUBSTITUTION:
			if (att.name != patch.item.name) {
				from.removeAttribute(att.name);
			}
			if (patch.item.value) {
				from.setAttribute(patch.item.name, patch.item.value);
			} else {
				from.removeAttribute(patch.item.name);
			}
			break;
		case Diff.DELETION:
			from.removeAttribute(att.name);
			break;
		}
	});
};

State.prototype.updateChildren = function(from, to) {
	Diff(from.children, to.children, function(node) {
		var key = node.src || node.href;
		if (key) return node.nodeName + '_' + key;
		else return node.outerHTML;
	}).forEach(function(patch) {
		var node = from.children[patch.index];
		switch (patch.type) {
		case Diff.INSERTION:
			from.insertBefore(patch.item, node);
			break;
		case Diff.SUBSTITUTION:
			from.replaceChild(patch.item, node);
			break;
		case Diff.DELETION:
			node.remove();
			break;
		}
	});
};

<commit_msg>Execute init after close, delete state emitter before anything<commit_after>var Utils = require('./utils');
var Loc = require('./loc');
var Wait = require('./wait');
var Diff = require('levenlistdiff');
var P = Utils.P;
var debug = Utils.debug;

module.exports = State;

var INIT = "init";
var READY = "ready";
var BUILD = "build";
var PATCH = "patch";
var SETUP = "setup";
var CLOSE = "close";
var ERROR = "error";
var HASH = "hash";
var Stages = [INIT, READY, BUILD, PATCH, SETUP, HASH, ERROR, CLOSE];

function State() {
	this.data = {};
	this.chains = {};
}

State.prototype.init = function(W) {
	var state = this;
	Stages.forEach(function(stage) {
		W[stage] = function(fn) {
			return state.chain(stage, fn);
		};
		W['un' + stage] = function(fn) {
			return state.unchain(stage, fn);
		};
	});
	state.reload = function() {
		return W.reload(state);
	};
	state.replace = function(loc) {
		return W.replace(loc, state);
	};
	state.push = function(loc) {
		return W.push(loc, state);
	};
	state.save = function() {
		return W.save(state);
	};
};

function stamp(stage) {
	var root = document.documentElement;
	if (stage != null) root.setAttribute('data-page-stage', stage);
	else stage = root.dataset.pageStage;
	return stage;
}

State.prototype.run = function(W) {
	var state = this;
	var refer = this.referrer;
	if (!refer) {
		if (document.referrer) {
			refer = Loc.parse(document.referrer);
		} else {
			refer = new State();
		}
		this.referrer = refer;
	}
	delete state.emitter;
	return Wait.dom().then(function() {
		if (refer.stage == SETUP && refer.pathname != state.pathname) {
			return refer.runChain(CLOSE);
		}
	}).then(function() {
		return state.runChain(INIT);
	}).then(function() {
		// it is up to the default router to NOT load a document upon first load
		// other routers might choose to do otherwise
		if (refer.pathname != state.pathname || !refer.stage) {
			return W.router(state, refer);
		} else {
			if (!state.emitter) state.emitter = refer.emitter;
		}
	}).then(function(doc) {
		return state.load(doc || document);
	}).then(function() {
		if (!state.initial) state.initial = stamp() || INIT;
		state.stage = state.initial;
		debug("doc ready at stage", state.stage);
		return state.runChain(READY);
	}).then(function() {
		if (state.initial != INIT) return;
		return (state.runChain(BUILD) || P()).then(function() {
			return state.runChain(PATCH);
		}).then(function() {
			stamp(BUILD);
		});
	}).then(function() {
		if (state.initial == SETUP) {
			if (!Loc.samePath(state, refer)) {
				return state.runChain(PATCH) || state.runChain(BUILD);
			}
		} else return Wait.ui().then(function() {
			if (state._abort) return Promise.reject("abort");
			return (state.runChain(SETUP) || P()).then(function() {
				stamp(SETUP);
			});
		});
	}).then(function() {
		if (state.hash != refer.hash) return state.runChain(HASH);
	}).catch(function(err) {
		delete state._abort;
		if (err != "abort") {
			// eslint-disable-next-line no-console
			if (typeof err != "number") console.error(err);
			state.error = err;
			return state.runChain(ERROR)
		}
	}).then(function() {
		return state;
	});
};

State.prototype.abort = function() {
	if (this.stage != BUILD) return false;
	this._abort = true;
	return true;
};

State.prototype.emit = function(name) {
	var e = new CustomEvent(name, {
		view: window,
		bubbles: true,
		cancelable: true,
		detail: this
	});
	Utils.all(document, 'script').forEach(function(node) {
		node.dispatchEvent(e);
	});
	if (this.emitter) this.emitter.dispatchEvent(e);
};

State.prototype.runChain = function(name) {
	this.stage = name;
	var chain = this.chains[name];
	if (!chain) chain = this.chains[name] = {};
	debug("run chain", name);
	chain.count = 0;
	chain.promise = P();
	this.emit("page" + name);
	debug("run chain count", name, chain.count);
	if (chain.count) return chain.promise;
};

State.prototype.chain = function(stage, fn) {
	var state = this;
	var ls = fn.pageListeners;
	if (!ls) ls = fn.pageListeners = {};
	var lfn = ls[stage];
	var emitter = document.currentScript;
	if (!emitter) {
		emitter = state.emitter;
		if (!emitter) emitter = state.emitter = document.createElement('div');
	}

	if (!lfn) {
		lfn = ls[stage] = {
			fn: chainListener(stage, fn),
			em: emitter
		};
		emitter.addEventListener('page' + stage, lfn.fn);
	} else {
		debug("already chained", stage, fn);
	}
	var p;
	var curNum = state.stage ? Stages.indexOf(state.stage) : 0;
	var tryNum = Stages.indexOf(stage);
	if (tryNum <= curNum) {
		debug("chain has run, execute fn now", stage);
		p = new Promise(function(resolve) {
			setTimeout(resolve);
		}).then(function() {
			return fn(state);
		});
	} else {
		debug("chain pending", stage);
		p = P();
	}
	return p;
};

State.prototype.unchain = function(stage, fn) {
	var ls = fn.pageListeners;
	if (!ls) return;
	var lfn = ls[stage];
	if (!lfn) return;
	delete ls[stage];
	lfn.em.removeEventListener('page' + stage, lfn.fn);
};

function chainListener(stage, fn) {
	return function(e) {
		var state = e.detail;
		var chain = state.chains[stage];
		if (chain.count == null) chain.count = 0;
		chain.count++;
		chain.promise = chain.promise.then(function() {
			return fn(e.detail);
		}).catch(function(err) {
			// eslint-disable-next-line no-console
			console.error("Uncaught error during", stage, err, fn);
		});
	};
}

State.prototype.load = function(doc) {
	if (doc == document) {
		Utils.trackListeners(document.body);
		return P();
	}
	Utils.clearListeners(document.body);
	debug("Import new document");
	var states = {};
	var selector = 'script:not([type]),script[type="text/javascript"],link[rel="import"]';
	Utils.all(document, selector).forEach(function(node) {
		var src = node.src || node.href;
		if (src) states[src] = true;
	});

	// if there is no HTMLImports support, some loaded script might contain
	// the HTMLImports polyfill itself, which will load imports however it likes
	// so it's hard to decide which order is good, and it's also impossible to know
	// if that polyfill will be available - so state.load does not preload
	// imports nor does it let them run on insert
	// if there is native support then it's like other resources.

	var nodes = Utils.all(doc, selector);

	nodes.forEach(function(node) {
		// just preload everything
		if (node.nodeName == "SCRIPT") {
			node.setAttribute('type', "none");
		} else if (node.nodeName == "LINK") {
			node.setAttribute('rel', 'none');
			if (!node.import) return; // polyfill already do preloading
		}
		var src = node.src || node.href;
		if (!src) return;
		if (states[src] === true) return;
		// not data-uri
		if (src.slice(0, 5) == 'data:') return;
		states[src] = Utils.get(src, 400).then(function() {
			debug("preloaded", src);
		}).catch(function(err) {
			debug("not preloaded", src, err);
		});
	});

	function loadNode(node) {
		var p = P();
		var src = node.src || node.href;
		var state = states[src];
		var old = state === true;
		var loader = !old && state;
		if (loader) {
			p = p.then(loader);
		}
		return p.then(function() {
			var parent = node.parentNode;
			var cursor;
			if (!old) {
				cursor = document.createTextNode("");
				parent.insertBefore(cursor, node);
				parent.removeChild(node);
			}
			if (node.nodeName == "LINK") {
				node.setAttribute('rel', 'import');
			} else if (node.nodeName == "SCRIPT") {
				node.removeAttribute('type');
			}
			if (old) return;
			var copy = document.createElement(node.nodeName);
			for (var i=0; i < node.attributes.length; i++) {
				copy.setAttribute(node.attributes[i].name, node.attributes[i].value);
			}
			if (node.textContent) copy.textContent = node.textContent;
			var rp;
			if (src) {
				debug("async node loading", src);
				if (node.nodeName == "LINK" && !node.import) {
					debug("not loading import", src);
				} else {
					rp = Wait.node(copy);
				}
			} else {
				debug("inline node loading");
				rp = new Promise(function(resolve) {
					setTimeout(resolve);
				});
			}
			parent.insertBefore(copy, cursor);
			parent.removeChild(cursor);
			if (rp) return rp;
		});
	}

	var root = document.documentElement;
	var nroot = document.adoptNode(doc.documentElement);
	var head = nroot.querySelector('head');
	var body = nroot.querySelector('body');

	Utils.trackListeners(body);

	var atts = nroot.attributes;

	for (var i=0; i < atts.length; i++) {
		root.setAttribute(atts[i].name, atts[i].value);
	}
	atts = Array.prototype.slice.call(root.attributes);
	for (var j=0; j < atts.length; j++) {
		if (!nroot.hasAttribute(atts[j].name)) nroot.removeAttribute(atts[j].name);
	}

	var parallels = Wait.styles(head, document.head);
	var serials = Utils.all(nroot, 'script[type="none"],link[rel="none"]');

	var state = this;

	return P().then(function() {
		state.setHead(head);
		return parallels;
	}).then(function() {
		return P().then(function() {
			return state.setBody(body);
		});
	}).then(function() {
		// scripts must be run in order
		var p = P();
		serials.forEach(function(node) {
			p = p.then(function() {
				return loadNode(node);
			});
		});
		return p;
	});
};

State.prototype.setHead = function(node) {
	this.updateAttributes(document.head, node);
	this.updateChildren(document.head, node);
};

State.prototype.setBody = function(node) {
	document.body.parentNode.replaceChild(node, document.body);
};

State.prototype.updateAttributes = function(from, to) {
	var attFrom = from.attributes;
	var attTo = to.attributes;
	Diff(attFrom, attTo, function(att) {
		return att.name + "_" + att.value;
	}).forEach(function(patch) {
		var att = attFrom[patch.index];
		switch (patch.type) {
		case Diff.INSERTION:
			if (patch.item.value) {
				from.setAttribute(patch.item.name, patch.item.value);
			}
			break;
		case Diff.SUBSTITUTION:
			if (att.name != patch.item.name) {
				from.removeAttribute(att.name);
			}
			if (patch.item.value) {
				from.setAttribute(patch.item.name, patch.item.value);
			} else {
				from.removeAttribute(patch.item.name);
			}
			break;
		case Diff.DELETION:
			from.removeAttribute(att.name);
			break;
		}
	});
};

State.prototype.updateChildren = function(from, to) {
	Diff(from.children, to.children, function(node) {
		var key = node.src || node.href;
		if (key) return node.nodeName + '_' + key;
		else return node.outerHTML;
	}).forEach(function(patch) {
		var node = from.children[patch.index];
		switch (patch.type) {
		case Diff.INSERTION:
			from.insertBefore(patch.item, node);
			break;
		case Diff.SUBSTITUTION:
			from.replaceChild(patch.item, node);
			break;
		case Diff.DELETION:
			node.remove();
			break;
		}
	});
};

<|endoftext|>


<commit_before>// @flow
import React from 'react';
import type { Event } from '../../Domain/Event';
import moment from 'moment';
import Icon from 'retail-ui/components/Icon';
import StatusIndicator from '../StatusIndicator/StatusIndicator';
import cn from './EventList.less';

type Props = {|
    items: Array<Event>;
|};

export default function EventList(props: Props): React.Element<*> {
    return (

Metric

State change

Event time

            {props.items.map((data, i) => {
                const { metric, old_state: oldState, state, timestamp } = data;
                return (

{metric}

                            <StatusIndicator statuses={[oldState]} size={14} />

                                <Icon name='ArrowBoldRight' />

                            <StatusIndicator statuses={[state]} size={14} />

{moment(timestamp).format('MMMM D, HH:mm:ss')}

                );
            })}

    );
}
<commit_msg>Show placeholder for empty metric field<commit_after>// @flow
import React from 'react';
import type { Event } from '../../Domain/Event';
import moment from 'moment';
import Icon from 'retail-ui/components/Icon';
import StatusIndicator from '../StatusIndicator/StatusIndicator';
import cn from './EventList.less';

type Props = {|
    items: Array<Event>;
|};

export default function EventList(props: Props): React.Element<*> {
    return (

Metric

State change

Event time

            {props.items.map((data, i) => {
                const { metric, old_state: oldState, state, timestamp } = data;
                return (

{metric || '—'}

                            <StatusIndicator statuses={[oldState]} size={14} />

                                <Icon name='ArrowBoldRight' />

                            <StatusIndicator statuses={[state]} size={14} />

{moment(timestamp).format('MMMM D, HH:mm:ss')}

                );
            })}

    );
}
<|endoftext|>


<commit_before>// Table generator
Bridle.Table = function() {

  var numFormat = d3.format('.3f');
  var margin = {
    top: 0,
    bottom: 0,
    left: 0,
    right: 0
  };
  var sortBy = "";
  var sortDesc = false;
  var comparators = {};
  var unsortedData;
  var toggledColumn = false;

  function chart(selection) {
    selection.each(function(data) {
      var containerID = this;

      // first time chart is called, or rows are appended/removed, do a hard clone of the unsorted data object to store
      if (typeof unsortedData === 'undefined' || unsortedData.rows.length != data.rows.length) {
        unsortedData = $.extend(true, {}, data);
      }

      // check whether a column has already been sorted twice, revert to unsorted data
      if (toggledColumn == true && sortBy == "") {
        toggledColumn = false
        data = unsortedData
      }

      // sort data if a column is specified
      // use merge sort algorithm because it is stable (preserves order) for equal values
      if (sortBy !== "") {
        data.rows = merge_sort(data.rows, function (a,b) {
          var comparatorFn = comparators.hasOwnProperty(sortBy) ? comparators[sortBy] : compare;
          return comparatorFn(a[getIndex(sortBy)], b[getIndex(sortBy)]) * (sortDesc ? 1 : -1);
        });
      }

      // Select the table element, if it exists.
      var table = d3.select(containerID)
      // .append("table")
      // .attr("style", "margin-left: 250px")

      .selectAll("table").data([data]);

      // Otherwise, create the skeletal chart.
      var tEnter = table.enter().append("table")
        .attr("class", "table table-hover bridle")
        .attr("style", "margin-top: " + margin.top + "px; margin-bottom: " + margin.bottom + "px; margin-left: " + margin.left + "px; margin-right: " + margin.top + "px")
      var thead = tEnter.append("thead").append('tr');
      var tbody = tEnter.append("tbody");

      var headers = table.select('thead').select('tr').selectAll('th').data(function(d) {
        return d.headers;
      })
      // assume a list of headers
      var hEnter = headers.enter().append('th')

      // now be a body
      // add the rows
      var rows = table.select('tbody').selectAll('tr').data(function(d) {
        return d.rows
      })

      rowsEnter = rows.enter().append('tr')

      rows.exit().remove()

      var cells = rows.selectAll('td').data(function(d) {
        return d;
      })

      cells.enter()
        .append("td")

      table.selectAll('th')
        .style('text-align', function(d) {
        if (isNumber(d)) {
          return 'right'
        }
        return 'left'
      })
        .html(function(d) {
        return d
      })
      .on('click', function(d, i) {
        if (toggledColumn == true) {
          d3.select(containerID).call(chart.sortBy(""));
        }
        else {
          // if column is already sorted, mark toggledColumn as true
          if (i == getIndex(sortBy)) {
            sortDesc = !sortDesc;
            toggledColumn = true;
          }
          else toggledColumn = false;

          d3.select(containerID).call(chart.sortBy(d));
        }
      })
      .each(function(d, i) {
        if (i == getIndex(sortBy)) {
          if (sortDesc) {
            d3.select(this).append('span').attr('class', 'desc')
          } else {
            d3.select(this).append('span').attr('class', 'asc')
          }
        }
      })

      table.selectAll('td')
        .style('text-align', function(d) {
        if (isNumber(d)) {
          return 'left'; //right
        }
        return 'left';
      })
        .text(function(d) {
        if (isNumber(d)) {
          var fmt = numFormat(d);
          return fmt
        }
        return d
      });

      function getIndex (header) {
        return data.headers.indexOf(header)
      }

    });

  }

  function merge_sort(array,comparison)
  {
    if(array.length < 2)
      return array;
    var middle = Math.ceil(array.length/2);
    return merge(merge_sort(array.slice(0,middle),comparison),
        merge_sort(array.slice(middle),comparison),
        comparison);
  }

function merge(left,right,comparison)
{
  // //console.log(left, right, comparison)
  var result = new Array();
  while((left.length > 0) && (right.length > 0))
  {
    if(comparison(left[0],right[0]) <= 0)
      result.push(left.shift());
    else
      result.push(right.shift());
  }
  while(left.length > 0)
    result.push(left.shift());
  while(right.length > 0)
    result.push(right.shift());
  return result;
}

  function compare(a, b) {
    if (a === null || b === null) return 0
    if (typeof a === 'string' && typeof b === 'string') {
      a = a.toLowerCase();
      b = b.toLowerCase();
    }
    return a > b ? 1 : a == b ? 0 : -1;
  }

  // check if it is a number

  function isNumber(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
  };

  chart.margin = function(_) {
    if (!arguments.length) return margin;
    margin = _;
    return chart;
  };

  chart.numFormat = function(_) {
    if (!arguments.length) return numFormat;
    numFormat = _;
    return chart;
  };
  chart.sortBy = function(_) {
    if (!arguments.length) return sortBy;
    sortBy = _;
    return chart;
  };
  chart.sortDesc = function(_) {
    if (!arguments.length) return sortDesc;
    sortDesc = _;
    return chart;
  };
  chart.comparators = function(_) {
    if (!arguments.length) {
      return comparators;
    }
    comparators = _;
    return chart;
  };

  return chart;
};
<commit_msg>accessors for columns<commit_after>// Table generator
Bridle.Table = function() {

  var numFormat = d3.format('.3f');
  var margin = {
    top    : 0,
    bottom : 0,
    left   : 0,
    right  : 0
  };
  var sortBy = '';
  var sortDesc = false;
  var comparators = {};
  var accessors = {};
  var unsortedData;
  var toggledColumn = false;

  function chart(selection) {
    selection.each(function(data) {
      var containerID = this;

      // first time chart is called, or rows are appended/removed, do a hard clone of the unsorted data object to store
      if (typeof unsortedData === 'undefined' || unsortedData.rows.length !== data.rows.length) {
        unsortedData = $.extend(true, {}, data);
      }

      // check whether a column has already been sorted twice, revert to unsorted data
      if (toggledColumn === true && sortBy === '') {
        toggledColumn = false;
        data = unsortedData;
      }

      // sort data if a column is specified
      // use merge sort algorithm because it is stable (preserves order) for equal values
      if (sortBy !== '') {
        data.rows = mergeSort(data.rows, function(a, b) {
          var comparatorFn = comparators.hasOwnProperty(sortBy) ? comparators[sortBy] : compare;
          return comparatorFn(a[getIndex(sortBy)], b[getIndex(sortBy)]) * (sortDesc ? 1 : -1);
        });
      }

      // Select the table element, if it exists.
      var table = d3.select(containerID)
        // .append('table')
        // .attr('style', 'margin-left: 250px')

        .selectAll('table').data([data]);

      // Otherwise, create the skeletal chart.
      var tEnter = table.enter().append('table')
        .attr('class', 'table table-hover bridle')
        .attr('style', 'margin-top: ' + margin.top + 'px; margin-bottom: ' +
          margin.bottom + 'px; margin-left: ' + margin.left + 'px; margin-right: ' + margin.top + 'px');
      tEnter.append('thead').append('tr');
      tEnter.append('tbody');

      var headers = table.select('thead').select('tr').selectAll('th').data(function(d) {
        return d.headers;
      });
      // assume a list of headers
      headers.enter().append('th');

      // now be a body
      // add the rows
      var rows = table.select('tbody').selectAll('tr').data(function(d) {
        return d.rows;
      });

      rows.enter().append('tr');
      rows.exit().remove();

      var cells = rows.selectAll('td').data(function(d) {
        return d;
      });

      cells.enter()
        .append('td');

      table.selectAll('th')
        .style('text-align', function(d) {
          if (isNumber(d)) {
            return 'right';
          }
          return 'left';
        })
        .html(function(d) {
          return d;
        })
        .on('click', function(d, i) {
          if (toggledColumn === true) {
            d3.select(containerID).call(chart.sortBy(''));
          }
          else {
            // if column is already sorted, mark toggledColumn as true
            if (i === getIndex(sortBy)) {
              sortDesc = !sortDesc;
              toggledColumn = true;
            }
            else {
              toggledColumn = false;
            }

            d3.select(containerID).call(chart.sortBy(d));
          }
        })
        .each(function(d, i) {
          if (i === getIndex(sortBy)) {
            if (sortDesc) {
              d3.select(this).append('span').attr('class', 'desc');
            } else {
              d3.select(this).append('span').attr('class', 'asc');
            }
          }
        });

      table.selectAll('td')
        .style('text-align', function(d) {
          if (isNumber(d)) {
            return 'left'; //right
          }
          return 'left';
        })
        .text(function(d) {
          if (d.type && accessors.hasOwnProperty(d.type)) {
            return accessors[d.type](d);
          }
          if (isNumber(d)) {
            return numFormat(d);
          }
          return d;
        });

      function getIndex(header) {
        return data.headers.indexOf(header);
      }

    });

  }

  function mergeSort(array, comparison) {
    if (array.length < 2) {
      return array;
    }
    var middle = Math.ceil(array.length / 2);
    return merge(mergeSort(array.slice(0, middle), comparison),
      mergeSort(array.slice(middle), comparison),
      comparison);
  }

  function merge(left, right, comparison) {
    // //console.log(left, right, comparison)
    var result = [];
    while ((left.length > 0) && (right.length > 0)) {
      if (comparison(left[0], right[0]) <= 0){
        result.push(left.shift());
      }
      else {
        result.push(right.shift());
      }
    }
    while (left.length > 0) {
      result.push(left.shift());
    }
    while (right.length > 0) {
      result.push(right.shift());
    }

    return result;
  }

  function compare(a, b) {
    if (a === null || b === null) {
      return 0;
    }
    if (typeof a === 'string' && typeof b === 'string') {
      a = a.toLowerCase();
      b = b.toLowerCase();
    }
    return a > b ? 1 : a === b ? 0 : -1;
  }

  // check if it is a number

  function isNumber(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
  }

  chart.margin = function(_) {
    if (!arguments.length) {
      return margin;
    }
    margin = _;
    return chart;
  };

  chart.numFormat = function(_) {
    if (!arguments.length) {
      return numFormat;
    }
    numFormat = _;
    return chart;
  };
  chart.sortBy = function(_) {
    if (!arguments.length) {
      return sortBy;
    }
    sortBy = _;
    return chart;
  };
  chart.sortDesc = function(_) {
    if (!arguments.length) {
      return sortDesc;
    }
    sortDesc = _;
    return chart;
  };
  chart.comparators = function(_) {
    if (!arguments.length) {
      return comparators;
    }
    comparators = _;
    return chart;
  };
  chart.accessors = function(_) {
    if (!arguments.length) {
      return accessors;
    }
    accessors = _;
    return chart;
  };

  return chart;
};
<|endoftext|>


<commit_before>/*========================================================================

   This file is part of the GOsa project -  http://gosa-project.org

   Copyright:
      (C) 2010-2012 GONICUS GmbH, Germany, http://www.gonicus.de

   License:
      LGPL-2.1: http://www.gnu.org/licenses/lgpl-2.1.html

   See the LICENSE file in the project's top-level directory for details.

======================================================================== */

/*
#asset(gosa/*)
*/

/**
 *
 * */
qx.Class.define("gosa.io.Sse", {

  extend : qx.core.Object,

  type   : "singleton",

  construct: function(){
    this.base(arguments);
    this.__eventSource = null;
  },

  destruct : function(){
    this.__eventSource = null;
  },

  events: {
    "objectRemoved": "qx.event.type.Data",
    "objectCreated": "qx.event.type.Data",
    "objectModified": "qx.event.type.Data",
    "objectClosing": "qx.event.type.Data"
  },

  properties: {
    connected: {
      check: "Boolean",
      init: false,
      event: "changeConnected"
    }
  },

  members : {

    __eventSource: null,

    reconnect : function() {

      if (this.__eventSource) {
        this.__eventSource.close();
      }

      var uri = window.location.protocol +"//" + window.location.host + gosa.Config.sse;

      this.__eventSource = new EventSource(uri);
      var that = this;
      this.__eventSource.addEventListener("notification", function (e) {
        var message = qx.lang.Json.parse(e.data);
        that._handleNotificationMessage(message);
      }, false);
      this.__eventSource.addEventListener("objectChange", function (e) {
        var message = qx.lang.Json.parse(e.data);
        that._handleObjectChangeMessage(message);
      }, false);
      this.__eventSource.addEventListener("objectCloseAnnouncement", function (e) {
        var message = qx.lang.Json.parse(e.data);
        that._handleObjectCloseAnnouncement(message);
      }, false);
      this.__eventSource.onerror = function (e) {
        var readyState = e.currentTarget.readyState;
        if (readyState != EventSource.OPEN) {
          // Connection was closed.
          this.setConnected(false);
        }
      }.bind(this);
      this.__eventSource.onopen = function(e) {
        var readyState = e.currentTarget.readyState;
        if (readyState == EventSource.OPEN) {
          // Connection was opened.
          this.setConnected(true);
        }
      }.bind(this);
    },

    /* Handle object change messages and fire up events
     * to inform other objects of the changes. E.g Lists.
     * */
    _handleObjectChangeMessage : function(info) {
      if(info['changeType'] == "update"){
        this.fireDataEvent("objectModified", info);
      }else if(info['changeType'] == "remove"){
        this.fireDataEvent("objectRemoved", info);
      }else if(info['changeType'] == "create"){
        this.fireDataEvent("objectCreated", info);
      }else{
        this.error("unhandled sse event: " + info['changeType']);
      }
    },

    _handleNotificationMessage : function(info) {
      var timeout = 5000;
      var icon = gosa.Config.getImagePath("status/dialog-information.png", 48);
      var title = "";
      if (info['timeout']) {
        timeout = info['timeout'];
      }
      if (info['icon']) {
        if (info['icon'] == "_no_icon_") {
          icon = gosa.Config.getImagePath("status/dialog-information.png", 48);
        } else if (info['icon'].substring(0, 7) == "base64:") {
          icon = "data:image/png;base64, " + info['icon'].substring(7);
        } else {
          icon = gosa.Config.getImagePath("status/" + info['icon'] + ".png", 48);
        }
      }
      if (info['title']) {
        title = "" + info['title'] + "";
      }

      if (gosa.Config.notifications && gosa.Config.notifications.checkPermission() == 0) {
        var data = qx.util.Base64.encode("
" + title + info['body'] + "
");
        var notification = gosa.Config.notifications.createHTMLNotification("data:text/html;charset=utf-8;base64," + data);
        notification.show();
        var timer = qx.util.TimerManager.getInstance();
        timer.start(function(userData, timerId){
          notification.cancel();
        }, 0, this, null, timeout);

      } else {

        // Create bubble with timeout
        var popup = new qx.ui.basic.Atom(title + info["body"].replace(/\n/g, '
'), icon).set({
            backgroundColor : "#0A0A0A",
            textColor : "#F0F0F0",
            decorator : "scroll-knob",
            iconPosition : "left",
            padding : 8,
            paddingRight : 20,
            zIndex : 10000,
            opacity : 0.8,
            rich : true
        });
        popup.addListener("click", function(e){ this.closePopup(popup); }, this);
        this.showPopup(popup);

        var timer = qx.util.TimerManager.getInstance();
        timer.start(function(userData, timerId){
          this.closePopup(popup);
        }, 0, this, null, timeout);

      }
    },

    _handleObjectCloseAnnouncement : function(info) {
      this.fireDataEvent("objectClosing", info);
    },

    closePopup : function(popup) {
      //TODO: take care about multiple popups at one time (rearranging)

      if (!popup) {
        return;
      }

      var duration = 250;
      var desc = {duration: duration, keep: 100, timing: "ease-out", keyFrames : {
        0 : {"opacity" : 0.8},
        100 : {"opacity": 0, display: "none"}
      }};

      qx.bom.element.Animation.animate(popup.getContentElement().getDomElement(), desc);
      setTimeout(function() {if (popup){ popup.destroy();}}, duration + 100);
    },

    showPopup : function(popup) {
      //TODO: take care about multiple popups at one time (positioning)
      var doc = qx.core.Init.getApplication().getRoot();
      doc.add(popup, {right: 15, bottom: 15});
    },

    wordwrap : function(str, width, brk, cut) {
      brk = brk || '\n';
      width = width || 75;
      cut = cut || false;

      if (!str) { return str; }

      var regex = '.{1,' +width+ '}(\\s|$)' + (cut ? '|.{' +width+ '}|.+$' : '|\\S+?(\\s|$)');

      return str.match( RegExp(regex, 'g') ).join( brk );
    }
  }

});
<commit_msg>Added explanation of sse<commit_after>/*========================================================================

   This file is part of the GOsa project -  http://gosa-project.org

   Copyright:
      (C) 2010-2012 GONICUS GmbH, Germany, http://www.gonicus.de

   License:
      LGPL-2.1: http://www.gnu.org/licenses/lgpl-2.1.html

   See the LICENSE file in the project's top-level directory for details.

======================================================================== */

/*
#asset(gosa/*)
*/

/**
 * SSE: Server-sent events (receives information from the server/backend)
 * */
qx.Class.define("gosa.io.Sse", {

  extend : qx.core.Object,

  type   : "singleton",

  construct: function(){
    this.base(arguments);
    this.__eventSource = null;
  },

  destruct : function(){
    this.__eventSource = null;
  },

  events: {
    "objectRemoved": "qx.event.type.Data",
    "objectCreated": "qx.event.type.Data",
    "objectModified": "qx.event.type.Data",
    "objectClosing": "qx.event.type.Data"
  },

  properties: {
    connected: {
      check: "Boolean",
      init: false,
      event: "changeConnected"
    }
  },

  members : {

    __eventSource: null,

    reconnect : function() {

      if (this.__eventSource) {
        this.__eventSource.close();
      }

      var uri = window.location.protocol +"//" + window.location.host + gosa.Config.sse;

      this.__eventSource = new EventSource(uri);
      var that = this;
      this.__eventSource.addEventListener("notification", function (e) {
        var message = qx.lang.Json.parse(e.data);
        that._handleNotificationMessage(message);
      }, false);
      this.__eventSource.addEventListener("objectChange", function (e) {
        var message = qx.lang.Json.parse(e.data);
        that._handleObjectChangeMessage(message);
      }, false);
      this.__eventSource.addEventListener("objectCloseAnnouncement", function (e) {
        var message = qx.lang.Json.parse(e.data);
        that._handleObjectCloseAnnouncement(message);
      }, false);
      this.__eventSource.onerror = function (e) {
        var readyState = e.currentTarget.readyState;
        if (readyState != EventSource.OPEN) {
          // Connection was closed.
          this.setConnected(false);
        }
      }.bind(this);
      this.__eventSource.onopen = function(e) {
        var readyState = e.currentTarget.readyState;
        if (readyState == EventSource.OPEN) {
          // Connection was opened.
          this.setConnected(true);
        }
      }.bind(this);
    },

    /* Handle object change messages and fire up events
     * to inform other objects of the changes. E.g Lists.
     * */
    _handleObjectChangeMessage : function(info) {
      if(info['changeType'] == "update"){
        this.fireDataEvent("objectModified", info);
      }else if(info['changeType'] == "remove"){
        this.fireDataEvent("objectRemoved", info);
      }else if(info['changeType'] == "create"){
        this.fireDataEvent("objectCreated", info);
      }else{
        this.error("unhandled sse event: " + info['changeType']);
      }
    },

    _handleNotificationMessage : function(info) {
      var timeout = 5000;
      var icon = gosa.Config.getImagePath("status/dialog-information.png", 48);
      var title = "";
      if (info['timeout']) {
        timeout = info['timeout'];
      }
      if (info['icon']) {
        if (info['icon'] == "_no_icon_") {
          icon = gosa.Config.getImagePath("status/dialog-information.png", 48);
        } else if (info['icon'].substring(0, 7) == "base64:") {
          icon = "data:image/png;base64, " + info['icon'].substring(7);
        } else {
          icon = gosa.Config.getImagePath("status/" + info['icon'] + ".png", 48);
        }
      }
      if (info['title']) {
        title = "" + info['title'] + "";
      }

      if (gosa.Config.notifications && gosa.Config.notifications.checkPermission() == 0) {
        var data = qx.util.Base64.encode("
" + title + info['body'] + "
");
        var notification = gosa.Config.notifications.createHTMLNotification("data:text/html;charset=utf-8;base64," + data);
        notification.show();
        var timer = qx.util.TimerManager.getInstance();
        timer.start(function(userData, timerId){
          notification.cancel();
        }, 0, this, null, timeout);

      } else {

        // Create bubble with timeout
        var popup = new qx.ui.basic.Atom(title + info["body"].replace(/\n/g, '
'), icon).set({
            backgroundColor : "#0A0A0A",
            textColor : "#F0F0F0",
            decorator : "scroll-knob",
            iconPosition : "left",
            padding : 8,
            paddingRight : 20,
            zIndex : 10000,
            opacity : 0.8,
            rich : true
        });
        popup.addListener("click", function(e){ this.closePopup(popup); }, this);
        this.showPopup(popup);

        var timer = qx.util.TimerManager.getInstance();
        timer.start(function(userData, timerId){
          this.closePopup(popup);
        }, 0, this, null, timeout);

      }
    },

    _handleObjectCloseAnnouncement : function(info) {
      this.fireDataEvent("objectClosing", info);
    },

    closePopup : function(popup) {
      //TODO: take care about multiple popups at one time (rearranging)

      if (!popup) {
        return;
      }

      var duration = 250;
      var desc = {duration: duration, keep: 100, timing: "ease-out", keyFrames : {
        0 : {"opacity" : 0.8},
        100 : {"opacity": 0, display: "none"}
      }};

      qx.bom.element.Animation.animate(popup.getContentElement().getDomElement(), desc);
      setTimeout(function() {if (popup){ popup.destroy();}}, duration + 100);
    },

    showPopup : function(popup) {
      //TODO: take care about multiple popups at one time (positioning)
      var doc = qx.core.Init.getApplication().getRoot();
      doc.add(popup, {right: 15, bottom: 15});
    },

    wordwrap : function(str, width, brk, cut) {
      brk = brk || '\n';
      width = width || 75;
      cut = cut || false;

      if (!str) { return str; }

      var regex = '.{1,' +width+ '}(\\s|$)' + (cut ? '|.{' +width+ '}|.+$' : '|\\S+?(\\s|$)');

      return str.match( RegExp(regex, 'g') ).join( brk );
    }
  }

});
<|endoftext|>


<commit_before>/*!
 * jCarousel Pagination Plugin v@VERSION
 * http://sorgalla.com/jcarousel/
 *
 * Copyright 2011, Jan Sorgalla
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * or GPL Version 2 (http://www.opensource.org/licenses/gpl-2.0.php) licenses.
 *
 * Date: @DATE
 */
(function ($) {
    'use strict';

    $.jcarousel.plugin('jcarouselPagination', {
        _options: {
            perPage: null,
            item: function(page) {
                return '' + page + '';
            }
        },
        _pages: {},
        _items: {},
        _init: function() {
            this.onDestroy = $.proxy(function() {
                this._destroy();
                this.carousel().one('createend.jcarousel', $.proxy(this._create, this));
            }, this);
            this.onReload = $.proxy(this._reload, this);
        },
        _create: function() {
            this.carousel()
                .one('destroy.jcarousel', this.onDestroy)
                .bind('reloadend.jcarousel', this.onReload);

            this._reload();
        },
        _destroy: function() {
            $.each(this._items, function(page, item) {
                item.jcarouselControl('destroy');
            });

            this._element.empty();

            this.carousel()
                .unbind('destroy.jcarousel', this.onDestroy)
                .unbind('reloadend.jcarousel', this.onReload);
        },
        _reload: function() {
            var perPage = this.options('perPage');

            this._pages = {};
            this._items = {};

            // Calculate pages
            if (perPage == null) {
                this._pages = this._calculatePages();
            } else if ($.isFunction(perPage)) {
                this._pages = perPage.call(this);
            } else {
                var pp = parseInt(perPage, 10) || 0,
                    items = this.carousel().jcarousel('items'),
                    page = 1,
                    i = 0,
                    curr;

                while (true) {
                    curr = items.eq(i++);

                    if (curr.size() === 0) {
                        break;
                    }

                    if (!this._pages[page]) {
                        this._pages[page] = curr;
                    } else {
                        this._pages[page] = this._pages[page].add(curr);
                    }

                    if (i % pp === 0) {
                        page++;
                    }
                }
            }

            var self = this,
                element = this._element.empty(),
                item = this.options('item');

            $.each(this._pages, function(page, carouselItems) {
                self._items[page] = $(item.call(self, page, carouselItems))
                    .appendTo(element)
                    .jcarouselControl({
                        carousel: self.carousel(),
                        target: carouselItems.eq(0)
                    });
            });
        },
        items: function() {
            return this._items;
        },
        _calculatePages: function() {
            var carousel = this.carousel().data('jcarousel'),
                items    = carousel.items(),
                clip     = carousel.clipping(),
                wh       = 0,
                idx      = 0,
                page     = 1,
                pages    = {},
                curr;

            while (true) {
                curr = items.eq(idx++);

                if (curr.size() === 0) {
                    break;
                }

                if (!pages[page]) {
                    pages[page] = curr;
                } else {
                    pages[page] = pages[page].add(curr);
                }

                wh += carousel.dimension(curr);

                if (wh >= clip) {
                    page++;
                    wh = 0;
                }
            }

            return pages;
        }
    });
}(jQuery));
<commit_msg>Only apply jcarouselControl if plugin is loaded<commit_after>/*!
 * jCarousel Pagination Plugin v@VERSION
 * http://sorgalla.com/jcarousel/
 *
 * Copyright 2011, Jan Sorgalla
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * or GPL Version 2 (http://www.opensource.org/licenses/gpl-2.0.php) licenses.
 *
 * Date: @DATE
 */
(function ($) {
    'use strict';

    $.jcarousel.plugin('jcarouselPagination', {
        _options: {
            perPage: null,
            item: function(page) {
                return '' + page + '';
            }
        },
        _pages: {},
        _items: {},
        _init: function() {
            this.onDestroy = $.proxy(function() {
                this._destroy();
                this.carousel().one('createend.jcarousel', $.proxy(this._create, this));
            }, this);
            this.onReload = $.proxy(this._reload, this);
        },
        _create: function() {
            this.carousel()
                .one('destroy.jcarousel', this.onDestroy)
                .bind('reloadend.jcarousel', this.onReload);

            this._reload();
        },
        _destroy: function() {
            if ($.fn.jcarouselControl) {
                $.each(this._items, function(page, item) {
                    item.jcarouselControl('destroy');
                });
            }

            this._element.empty();

            this.carousel()
                .unbind('destroy.jcarousel', this.onDestroy)
                .unbind('reloadend.jcarousel', this.onReload);
        },
        _reload: function() {
            var perPage = this.options('perPage');

            this._pages = {};
            this._items = {};

            // Calculate pages
            if (perPage == null) {
                this._pages = this._calculatePages();
            } else if ($.isFunction(perPage)) {
                this._pages = perPage.call(this);
            } else {
                var pp = parseInt(perPage, 10) || 0,
                    items = this.carousel().jcarousel('items'),
                    page = 1,
                    i = 0,
                    curr;

                while (true) {
                    curr = items.eq(i++);

                    if (curr.size() === 0) {
                        break;
                    }

                    if (!this._pages[page]) {
                        this._pages[page] = curr;
                    } else {
                        this._pages[page] = this._pages[page].add(curr);
                    }

                    if (i % pp === 0) {
                        page++;
                    }
                }
            }

            var self = this,
                element = this._element.empty(),
                item = this.options('item');

            $.each(this._pages, function(page, carouselItems) {
                var currItem = self._items[page] = $(item.call(self, page, carouselItems));

                element.append(currItem);

                if ($.fn.jcarouselControl) {
                    currItem.jcarouselControl({
                        carousel: self.carousel(),
                        target: carouselItems.eq(0)
                    });
                }
            });
        },
        items: function() {
            return this._items;
        },
        _calculatePages: function() {
            var carousel = this.carousel().data('jcarousel'),
                items    = carousel.items(),
                clip     = carousel.clipping(),
                wh       = 0,
                idx      = 0,
                page     = 1,
                pages    = {},
                curr;

            while (true) {
                curr = items.eq(idx++);

                if (curr.size() === 0) {
                    break;
                }

                if (!pages[page]) {
                    pages[page] = curr;
                } else {
                    pages[page] = pages[page].add(curr);
                }

                wh += carousel.dimension(curr);

                if (wh >= clip) {
                    page++;
                    wh = 0;
                }
            }

            return pages;
        }
    });
}(jQuery));
<|endoftext|>