<commit_before>define(['serversync'], function (ServerSync) {
    'use strict';

    function syncNext(connectionManager, servers, index, options, resolve, reject) {

        var length = servers.length;

        if (index >= length) {

            resolve();
            return;
        }

        var server = servers[index];

        console.log("Creating ServerSync to server: " + server.Id);

        new ServerSync().sync(connectionManager, server, options).then(function () {

            console.log("ServerSync succeeded to server: " + server.Id);

            syncNext(connectionManager, servers, index + 1, options, resolve, reject);

        }, function (err) {

            console.log("ServerSync failed to server: " + server.Id + '. ' + err);

            syncNext(connectionManager, servers, index + 1, options, resolve, reject);
        });
    }

    function MultiServerSync() {

    }

    MultiServerSync.prototype.sync = function (connectionManager, options) {

        console.log('MultiServerSync.sync starting...');

        return new Promise(function (resolve, reject) {

            var servers = connectionManager.getSavedServers();

            syncNext(connectionManager, servers, 0, options, resolve, reject);
        });
    };

    return MultiServerSync;
});<commit_msg>fix get request<commit_after>define(['serversync'], function (ServerSync) {
    'use strict';

    function syncNext(connectionManager, servers, index, options, resolve, reject) {

        var length = servers.length;

        if (index >= length) {

            console.log('MultiServerSync.sync complete');
            resolve();
            return;
        }

        var server = servers[index];

        console.log("Creating ServerSync to server: " + server.Id);

        new ServerSync().sync(connectionManager, server, options).then(function () {

            console.log("ServerSync succeeded to server: " + server.Id);

            syncNext(connectionManager, servers, index + 1, options, resolve, reject);

        }, function (err) {

            console.log("ServerSync failed to server: " + server.Id + '. ' + err);

            syncNext(connectionManager, servers, index + 1, options, resolve, reject);
        });
    }

    function MultiServerSync() {

    }

    MultiServerSync.prototype.sync = function (connectionManager, options) {

        console.log('MultiServerSync.sync starting...');

        return new Promise(function (resolve, reject) {

            var servers = connectionManager.getSavedServers();

            syncNext(connectionManager, servers, 0, options, resolve, reject);
        });
    };

    return MultiServerSync;
});<|endoftext|>


<commit_before>import errors from 'feathers-errors';
import Debug from 'debug';
const debug = Debug('feathers-authentication:express:authenticate');

export default function authenticate (strategy, options = {}) {
  // TODO (EK): Support arrays of strategies

  if (!strategy) {
    throw new Error(`The 'authenticate' hook requires one of your registered passport strategies.`);
  }

  return function (req, res, next) {
    // If we are already authenticated skip
    if (req.authenticated) {
      return next();
    }

    // if (!req.app.passport._strategy(strategy)) {
    //   return next(new Error(`Your '${strategy}' authentication strategy is not registered with passport.`));
    // }
    // TODO (EK): Can we do something in here to get away
    // from express-session for OAuth1?
    // TODO (EK): Handle chaining multiple strategies
    req.app.authenticate(strategy, options)(req).then((result = {}) => {
      // TODO (EK): Support passport failureFlash
      // TODO (EK): Support passport successFlash
      if (result.success) {
        Object.assign(req, { authenticated: true }, result.data);
        Object.assign(req.feathers, { authenticated: true }, result.data);

        if (options.successRedirect) {
          debug(`Redirecting to ${options.successRedirect}`);
          res.status(302);
          return res.redirect(options.successRedirect);
        }

        return next();
      }

      if (result.fail) {
        if (options.failureRedirect) {
          debug(`Redirecting to ${options.failureRedirect}`);
          res.status(302);
          return res.redirect(options.failureRedirect);
        }

        const { challenge, status = 401 } = result;
        let message = challenge && challenge.message ? challenge.message : challenge;

        if (options.failureMessage) {
          message = options.failureMessage;
        }

        res.status(status);
        return Promise.reject(new errors[status](message, challenge));
      }

      if (result.redirect) {
        debug(`Redirecting to ${result.url}`);
        res.status(result.status);
        return res.redirect(result.url);
      }

      // Only gets here if pass() is called by the strategy
      next();
    }).catch(next);
  };
}
<commit_msg>adding a value for checking against oauth (#374)<commit_after>import errors from 'feathers-errors';
import Debug from 'debug';
const debug = Debug('feathers-authentication:express:authenticate');

export default function authenticate (strategy, options = {}) {
  // TODO (EK): Support arrays of strategies

  if (!strategy) {
    throw new Error(`The 'authenticate' hook requires one of your registered passport strategies.`);
  }

  return function (req, res, next) {
    // If we are already authenticated skip
    if (req.authenticated) {
      return next();
    }

    // if (!req.app.passport._strategy(strategy)) {
    //   return next(new Error(`Your '${strategy}' authentication strategy is not registered with passport.`));
    // }
    // TODO (EK): Can we do something in here to get away
    // from express-session for OAuth1?
    // TODO (EK): Handle chaining multiple strategies
    req.app.authenticate(strategy, options)(req).then((result = {}) => {
      // TODO (EK): Support passport failureFlash
      // TODO (EK): Support passport successFlash
      if (result.success) {
        Object.assign(req, { authenticated: true }, result.data);
        Object.assign(req.feathers, { authenticated: true }, result.data);

        if (options.successRedirect && !options.__oauth) {
          debug(`Redirecting to ${options.successRedirect}`);
          res.status(302);
          return res.redirect(options.successRedirect);
        }

        return next();
      }

      if (result.fail) {
        if (options.failureRedirect && !options.__oauth) {
          debug(`Redirecting to ${options.failureRedirect}`);
          res.status(302);
          return res.redirect(options.failureRedirect);
        }

        const { challenge, status = 401 } = result;
        let message = challenge && challenge.message ? challenge.message : challenge;

        if (options.failureMessage) {
          message = options.failureMessage;
        }

        res.status(status);
        return Promise.reject(new errors[status](message, challenge));
      }

      if (result.redirect) {
        debug(`Redirecting to ${result.url}`);
        res.status(result.status);
        return res.redirect(result.url);
      }

      // Only gets here if pass() is called by the strategy
      next();
    }).catch(next);
  };
}
<|endoftext|>


<commit_before>/*
Everything related to changing the graph display, without interaction with
other parts of the system.
*/

// A way to order the connection by creation age
var connection_counter = 0;
var pos0 = null;
var dragging = false;
var dragged = false;

function create_paper() {
  return new joint.dia.Paper({
    el: $('#paper'),
    model: graph,
    width: "100%",
    height: "100%",
    gridSize: 10,
    snapLinks: true,
    defaultLink: function (elementView, magnet) {
      connection_counter++;
      return new joint.shapes.incredible.Link({
        counter : connection_counter
      });
    },

    validateConnection: function (vs, ms, vt, mt, e, vl) {
      //console.log(vs,ms,vt,mt,e,vl);

      if (ms && mt && ms.getAttribute('direction') == mt.getAttribute('direction')) {
        return false;
      }

      return true;
    }
  });
}

/**
 * Rescales the paper.
 *
 * Parameters:
 *  - amount: The ratio between the rescaled and current paper
 *            (e.g. '2.0' makes everything appear two times bigger)
 *  - x, y: Coordinates of the point that shall remain stable,
 *          given by pixel coordinates relative to the top left corner of the viewport.
 */
function paper_scale(amount, x, y) {
  var scale = V(paper.viewport).scale().sx;
  var newScale = scale*amount;
  paper.scale(newScale, newScale);

  var ox = paper.options.origin.x;
  var oy = paper.options.origin.y;
  var dx = (newScale - scale)/scale * (x - ox);
  var dy = (newScale - scale)/scale * (y - oy);
  paper.setOrigin(ox - dx, oy - dy);
}

function selectNothing() {
  $.each(graph.getElements(), function (i, el) {
    el.set('selected', false);
  });
}

$(function() {
  $(document).on('mousemove', function(e) {
    if (dragging && pos0 != null) {
      var pos1 = {x: e.pageX, y: e.pageY};
      paper.setOrigin(
        paper.options.origin.x + pos1.x - pos0.x,
        paper.options.origin.y + pos1.y - pos0.y
      );
      pos0 = pos1;
      dragged = true;
    }
  })

  paper.on('cell:pointerdown', function (cellView, evt, x, y) {
    var cell = cellView.model;

    if (evt.shiftKey) { return; }

    // Check if this was a click on a delete element
    // This assumes that all visible elements of the delete SVG are direct childs
    // of a <g> element with event="remove" set
    var targetParentEvent = evt.target.parentNode.getAttribute('event');
    if (targetParentEvent && targetParentEvent == "remove" ) {
      cell.remove();
      return;
    }
  });

  paper.on('blank:pointerdown', function (e, x, y) {
    if (e.shiftKey) { return; }

    pos0 = {x: e.pageX, y: e.pageY};
    dragging = true;
  });

  paper.on('blank:pointerclick', function (evt, x, y) {
    if (dragging) {
      dragging = false;

      if (dragged) {
        dragged = false;
        return;
      }
    }

    if (evt.shiftKey) { return; }

    selectNothing();
  });

  paper.on('cell:pointerclick', function (cellView, evt, x, y) {
    var cell = cellView.model;

    if (dragging) {
      dragging = false;

      if (dragged) {
        dragged = false;
        return;
      }
    }

    if (evt.shiftKey) {
      cell.set('selected', ! cell.get('selected'));
      return;
    }

    // Deselect everything
    selectNothing();

    if (cell.get('annotation')) {
      var done = false;
      var prmpt = i18n.t('Input proposition');
      var val = cell.get('annotation');
      while (!done) {
        val = window.prompt(prmpt, val);
        if (val) {
          var prettyPrinted = incredibleFormatTerm(val);
          if (prettyPrinted) {
            done = true;
            cell.set('annotation', prettyPrinted);
          } else {
            prmpt = i18n.t('Could not parse, please try again:');
          }
        } else {
          done = true;
        }
      }
    }
  });

  paper.on('element:schieblehre',function(cellView, direction, dx, dy) {
    var basewidth0 = cellView.model.get('schieblehrebasewidth');
    if (basewidth0 === undefined) { basewidth0 = 40; }
    var basewidth1 = basewidth0;
    if (direction == "resize-left") {
      basewidth1 -= dx;
    } else if (direction == "resize-right") {
      basewidth1 += dx;
    } else {
      throw Error("element:schieblehre: Unknown direction " + direction);
    }
    cellView.model.set('schieblehrebasewidth', basewidth1);
    var width0 = cellView.model.get('schieblehrewidth');
    if (width0 === undefined) { width0 = 40; }
    var width1 = Math.max(0, g.snapToGrid(basewidth1, paper.options.gridSize));
    if (width0 != width1) {
      cellView.model.set('schieblehrewidth', width1);
      // Move center accordingly
      if (direction == "resize-left") {
        cellView.model.translate(width0 - width1, 0, {ui: true});
      }
    }
  });

  graph.on('change:position', function (model, pos1, options) {
    if (options.derivedMove) { return; }

    if (model.get('selected')) {
      var dx = options.tx;
      var dy = options.ty;
      if (dx === 0 && dy === 0) { return; }

      $.each(graph.getElements(), function (i, el) {
        if (el.get('selected') && el != model) {
          el.translate(dx,dy, { derivedMove : true });
        }
      });
      $.each(graph.getLinks(), function (i, conn) {
	var source = conn.get('source');
	var target = conn.get('target');
	var connected = source.id || target.id;
	var sourceSelected = source.id && graph.getCell(source.id).get('selected');
	var targetSelected = target.id && graph.getCell(target.id).get('selected');
	if (connected && (!source.id || sourceSelected) && (!target.id || targetSelected)) {
          conn.translate(dx,dy, { derivedMove : true });
	}
      });
    }
  });

  $("#savesvg").on('click', function (){
    save_svg("#paper svg", "incredible-proof.svg");
  });

  $("#paper").on('wheel', function (evt) {
    var up = evt.originalEvent.deltaY < 0;
    paper_scale(up ? 1.1 : 1/1.1, evt.originalEvent.offsetX, evt.originalEvent.offsetY);
  });

  $("#zoom-in").on('click', function (){
    paper_scale(1.2, $("#paper").innerWidth() / 2, $("#paper").innerHeight() / 2);
  });

  $("#zoom-out").on('click', function (){
    paper_scale(1/1.2, $("#paper").innerWidth() / 2, $("#paper").innerHeight() / 2);
   });

  $("#zoom-orig").on('click', function (){
    paper_scale(1/V(paper.viewport).scale().sx, $("#paper").innerWidth() / 2, $("#paper").innerHeight() / 2);
  });

  $("#zoom-fit").on('click', function (){
    var padding = 15;
    var cbb = paper.getContentBBox();
    if (cbb.width !== 0 && cbb.height !== 0) {
      // Only rescale and pan if we have content.
      var scale = V(paper.viewport).scale().sx;
      var w = $("#paper").innerWidth();
      var h = $("#paper").innerHeight();
      scale *= Math.min((w - padding) / cbb.width,
                       (h - padding) / cbb.height);
      paper.scale(scale, scale);

      cbb = paper.getContentBBox();
      var ox = paper.options.origin.x + (w - cbb.width) / 2 - cbb.x;
      var oy = paper.options.origin.y + (h - cbb.height) / 2 - cbb.y;
      paper.setOrigin(ox, oy);
    }
  });

});

// Separate function, to use it from the developer’s console on other SVG
// elements.
function save_svg(element, filename) {
  // Connect all CSS data that is possibly relevant
  var rules = [];
  $.each(document.styleSheets, function(sheetIndex, sheet) {
    if (sheet.ownerNode.dataset.css) {
      $.each(sheet.cssRules || sheet.rules, function(ruleIndex, rule) {
          rules.push(rule.cssText);
      });
    }
  });
  // This is wrong in general, as the SVG is not necessary the paper. How else
  // can we get the width?
  var bb = paper.getContentBBox();
  var css = rules.join("\n");
  var svg = $(element) // this way, element can be a selector, dom node or jquery object
    .clone()
    .prepend($("<style type='text/css'>").text(css))
    .attr({width: bb.x + bb.width + 10, height: bb.y + bb.height + 10})
    .wrap('<div>')
    .parent()
    .html()
    .replace(/ /g," ");
  saveAs(new Blob([svg], {type:"application/svg+xml"}), filename);
}
<commit_msg>Improve block selection<commit_after>/*
Everything related to changing the graph display, without interaction with
other parts of the system.
*/

// A way to order the connection by creation age
var connection_counter = 0;
var pos0 = null;
var dragging = false;
var dragged = false;

function create_paper() {
  return new joint.dia.Paper({
    el: $('#paper'),
    model: graph,
    width: "100%",
    height: "100%",
    gridSize: 10,
    snapLinks: true,
    defaultLink: function (elementView, magnet) {
      connection_counter++;
      return new joint.shapes.incredible.Link({
        counter : connection_counter
      });
    },

    validateConnection: function (vs, ms, vt, mt, e, vl) {
      //console.log(vs,ms,vt,mt,e,vl);

      if (ms && mt && ms.getAttribute('direction') == mt.getAttribute('direction')) {
        return false;
      }

      return true;
    }
  });
}

/**
 * Rescales the paper.
 *
 * Parameters:
 *  - amount: The ratio between the rescaled and current paper
 *            (e.g. '2.0' makes everything appear two times bigger)
 *  - x, y: Coordinates of the point that shall remain stable,
 *          given by pixel coordinates relative to the top left corner of the viewport.
 */
function paper_scale(amount, x, y) {
  var scale = V(paper.viewport).scale().sx;
  var newScale = scale*amount;
  paper.scale(newScale, newScale);

  var ox = paper.options.origin.x;
  var oy = paper.options.origin.y;
  var dx = (newScale - scale)/scale * (x - ox);
  var dy = (newScale - scale)/scale * (y - oy);
  paper.setOrigin(ox - dx, oy - dy);
}

function selectNothing() {
  $.each(graph.getElements(), function (i, el) {
    el.set('selected', false);
  });
}

$(function() {
  $(document).on('mousemove', function(e) {
    if (dragging && pos0 != null) {
      var pos1 = {x: e.pageX, y: e.pageY};
      paper.setOrigin(
        paper.options.origin.x + pos1.x - pos0.x,
        paper.options.origin.y + pos1.y - pos0.y
      );
      pos0 = pos1;
      dragged = true;
    }
  })

  paper.on('cell:pointerdown', function (cellView, evt, x, y) {
    var cell = cellView.model;

    if (evt.shiftKey) {
      cell.set('selected', ! cell.get('selected'));
      return;
    }

    // Check if this was a click on a delete element
    // This assumes that all visible elements of the delete SVG are direct childs
    // of a <g> element with event="remove" set
    var targetParentEvent = evt.target.parentNode.getAttribute('event');
    if (targetParentEvent && targetParentEvent == "remove" ) {
      cell.remove();
      return;
    }
  });

  paper.on('blank:pointerdown', function (e, x, y) {
    if (e.shiftKey) { return; }

    pos0 = {x: e.pageX, y: e.pageY};
    dragging = true;
  });

  paper.on('blank:pointerclick', function (evt, x, y) {
    if (dragging) {
      dragging = false;

      if (dragged) {
        dragged = false;
        return;
      }
    }

    if (evt.shiftKey) { return; }

    selectNothing();
  });

  paper.on('cell:pointerclick', function (cellView, evt, x, y) {
    var cell = cellView.model;

    if (dragging) {
      dragging = false;

      if (dragged) {
        dragged = false;
        return;
      }
    }

    if (evt.shiftKey) { return; }

    // Deselect everything
    selectNothing();

    if (cell.get('annotation')) {
      var done = false;
      var prmpt = i18n.t('Input proposition');
      var val = cell.get('annotation');
      while (!done) {
        val = window.prompt(prmpt, val);
        if (val) {
          var prettyPrinted = incredibleFormatTerm(val);
          if (prettyPrinted) {
            done = true;
            cell.set('annotation', prettyPrinted);
          } else {
            prmpt = i18n.t('Could not parse, please try again:');
          }
        } else {
          done = true;
        }
      }
    }
  });

  paper.on('element:schieblehre',function(cellView, direction, dx, dy) {
    var basewidth0 = cellView.model.get('schieblehrebasewidth');
    if (basewidth0 === undefined) { basewidth0 = 40; }
    var basewidth1 = basewidth0;
    if (direction == "resize-left") {
      basewidth1 -= dx;
    } else if (direction == "resize-right") {
      basewidth1 += dx;
    } else {
      throw Error("element:schieblehre: Unknown direction " + direction);
    }
    cellView.model.set('schieblehrebasewidth', basewidth1);
    var width0 = cellView.model.get('schieblehrewidth');
    if (width0 === undefined) { width0 = 40; }
    var width1 = Math.max(0, g.snapToGrid(basewidth1, paper.options.gridSize));
    if (width0 != width1) {
      cellView.model.set('schieblehrewidth', width1);
      // Move center accordingly
      if (direction == "resize-left") {
        cellView.model.translate(width0 - width1, 0, {ui: true});
      }
    }
  });

  graph.on('change:position', function (model, pos1, options) {
    if (options.derivedMove) { return; }

    if (model.get('selected')) {
      var dx = options.tx;
      var dy = options.ty;
      if (dx === 0 && dy === 0) { return; }

      $.each(graph.getElements(), function (i, el) {
        if (el.get('selected') && el != model) {
          el.translate(dx,dy, { derivedMove : true });
        }
      });
      $.each(graph.getLinks(), function (i, conn) {
	var source = conn.get('source');
	var target = conn.get('target');
	var connected = source.id || target.id;
	var sourceSelected = source.id && graph.getCell(source.id).get('selected');
	var targetSelected = target.id && graph.getCell(target.id).get('selected');
	if (connected && (!source.id || sourceSelected) && (!target.id || targetSelected)) {
          conn.translate(dx,dy, { derivedMove : true });
	}
      });
    }
  });

  $("#savesvg").on('click', function (){
    save_svg("#paper svg", "incredible-proof.svg");
  });

  $("#paper").on('wheel', function (evt) {
    var up = evt.originalEvent.deltaY < 0;
    paper_scale(up ? 1.1 : 1/1.1, evt.originalEvent.offsetX, evt.originalEvent.offsetY);
  });

  $("#zoom-in").on('click', function (){
    paper_scale(1.2, $("#paper").innerWidth() / 2, $("#paper").innerHeight() / 2);
  });

  $("#zoom-out").on('click', function (){
    paper_scale(1/1.2, $("#paper").innerWidth() / 2, $("#paper").innerHeight() / 2);
   });

  $("#zoom-orig").on('click', function (){
    paper_scale(1/V(paper.viewport).scale().sx, $("#paper").innerWidth() / 2, $("#paper").innerHeight() / 2);
  });

  $("#zoom-fit").on('click', function (){
    var padding = 15;
    var cbb = paper.getContentBBox();
    if (cbb.width !== 0 && cbb.height !== 0) {
      // Only rescale and pan if we have content.
      var scale = V(paper.viewport).scale().sx;
      var w = $("#paper").innerWidth();
      var h = $("#paper").innerHeight();
      scale *= Math.min((w - padding) / cbb.width,
                       (h - padding) / cbb.height);
      paper.scale(scale, scale);

      cbb = paper.getContentBBox();
      var ox = paper.options.origin.x + (w - cbb.width) / 2 - cbb.x;
      var oy = paper.options.origin.y + (h - cbb.height) / 2 - cbb.y;
      paper.setOrigin(ox, oy);
    }
  });

});

// Separate function, to use it from the developer’s console on other SVG
// elements.
function save_svg(element, filename) {
  // Connect all CSS data that is possibly relevant
  var rules = [];
  $.each(document.styleSheets, function(sheetIndex, sheet) {
    if (sheet.ownerNode.dataset.css) {
      $.each(sheet.cssRules || sheet.rules, function(ruleIndex, rule) {
          rules.push(rule.cssText);
      });
    }
  });
  // This is wrong in general, as the SVG is not necessary the paper. How else
  // can we get the width?
  var bb = paper.getContentBBox();
  var css = rules.join("\n");
  var svg = $(element) // this way, element can be a selector, dom node or jquery object
    .clone()
    .prepend($("<style type='text/css'>").text(css))
    .attr({width: bb.x + bb.width + 10, height: bb.y + bb.height + 10})
    .wrap('<div>')
    .parent()
    .html()
    .replace(/ /g," ");
  saveAs(new Blob([svg], {type:"application/svg+xml"}), filename);
}
<|endoftext|>


<commit_before>import scriptManager from  "src/client/script-manager.js";
// import * as persistence from  "src/client/persistence.js";
import Morph from "templates/Morph.js";
import {pt} from '../graphics.js';
import { through } from "utils";

// store promises of loaded and currently loading templates
export var loadingPromises = {};

// #MetaNote #UserCase this is an example for preserving module internal state while reloading a module
var _templates;
var _prototypes;
var _proxies;
var _templatePaths;

// for compatibility
export function register(componentName, template, prototype) {
  return ComponentLoader.register(componentName, template, prototype);
}

/* #FutureWork should interactive state change of "(module) global" state be preserved while reloading / developing modules
    ComponentLoader.foo = 3
    ComponentLoader.foo

#Discussion

pro) expected in Smalltalk-like developent and live-programmning experience
contra) gap between development-time and runtime (those manualy changes could make something work that without it won't...)

synthese) if modules and classes are also objects that can have run-time-specific state they should be migrated the same as objects.

*/

export default class ComponentLoader {

  static get templates() {
    if (!_templates) _templates = {};
    return _templates;
  }

  static get prototypes() {
    if (!_prototypes) _prototypes = {};
    return _prototypes;
  }

  static get proxies() {
     if (!_proxies) _proxies = {};
    return _proxies;
  }

  static protypeToComponentName(prototype) {
    if (!prototype || !prototype.constructor) return
    var prototypeName =  prototype.constructor.name
    return _.keys(this.prototypes).find(name => {
      var otherProto = this.prototypes[name]
      var constructor = otherProto && otherProto.constructor;
      return constructor && (constructor.name ===  prototypeName)});
  }

  static updatePrototype(prototype) {
    var componentName = this.protypeToComponentName(prototype)
    if (componentName) {
      this.prototypes[componentName] = prototype
      this.proxies[componentName].__proto__ = prototype
    }
  }

  static onCreatedCallback(object, componentName) {
    // if (persistence.isCurrentlyCloning()) {
    //   return;
    // }

    var shadow = object.createShadowRoot();

    // clone the template again, so when more elements are created,
    // they get their own copy of elements
    var clone = document.importNode(ComponentLoader.templates[componentName], true);
    // #TODO replace the "template" reference with an indirection that can be changed from the outside,
    // e.g. var clone = document.importNode(this.templates[componentName], true);
    // but beeing able to modify it, because we have a reference should suffice at the moment...

    shadow.appendChild(clone);

    // attach lively4scripts from the shadow root to this
    scriptManager.attachScriptsFromShadowDOM(object);

    // attach lively4script from the instance
    scriptManager.findLively4Script(object, false);

    if (ComponentLoader.prototypes[componentName].createdCallback) {
      ComponentLoader.prototypes[componentName].createdCallback.call(object);
    }

    // load any unknown elements, which this component might introduce
    ComponentLoader.loadUnresolved(object, true, "onCreated " + componentName).then((args) => {
      // lively.fillTemplateStyles(object.shadowRoot, "source: " + componentName).then(() => {
        // call the initialize script, if it exists

        if (typeof object.initialize === "function") {
          object.initialize();
        }
        // console.log("dispatch created " +componentName )
        // console.log("Identitity: " + (window.LastRegistered === object))

        object.dispatchEvent(new Event("created"));
      // })
    }).catch( e => {
      console.error(e);
      return e
    });
  }

  static onAttachedCallback(object, componentName) {
    // if (ComponentLoader.proxies[componentName]) {
    //   console.log("[component loader] WARNING: no proxy for " + componentName )
    //   return
    // }

    if (object.attachedCallback &&
      ComponentLoader.proxies[componentName].attachedCallback != object.attachedCallback) {
        object.attachedCallback.call(object);
    }
    if (ComponentLoader.prototypes[componentName].attachedCallback) {
      ComponentLoader.prototypes[componentName].attachedCallback.call(object);
    }
  }

  static onDetachedCallback(object, componentName) {
    // if (ComponentLoader.proxies[componentName]) {
    //   console.log("[component loader] WARNING: no proxy for " + componentName )
    //   return
    // }

    if (object.detachedCallback
    && ComponentLoader.proxies[componentName].detachedCallback != object.detachedCallback) {
      object.detachedCallback.call(object);
    } else if (ComponentLoader.prototypes[componentName].detachedCallback) {
      ComponentLoader.prototypes[componentName].detachedCallback.call(object);
    }
  }

  // this function registers a custom element,
  // it is called from the bootstap code in the component templates
  static register(componentName, template, prototype) {
    var proto = prototype || Object.create(Morph.prototype);

    // For reflection and debugging
    this.templates[componentName] = template;
    this.prototypes[componentName] = proto;
    this.proxies[componentName] = Object.create(proto) // not changeable

    lively.fillTemplateStyles(template, "source: " + componentName).then( () => {

      // FOR DEBUGGING
      // if (!window.createdCompontents)
      //   window.createdCompontents = {}
      // window.createdCompontents[componentName] = []

      // #Mystery #Debugging #ExperienceReport
      // Task was to figure out why the created callback is called several times when loading the
      // componen for the first time? E.g 5 ace editors for the container, where 2 are actually needed
      // maybe they are also called for the template documents that we store?
      // e.g. lively-editor (+ 1 ace + lively-version (+1 ace)) + lively-version (1 ace)
      // that would actually account for the missing three instances
      // It is Saturday night... past midnight and I finnally have at least an hypothesis,
      // where I was debugging in the dark the last 3 hours.
      // I got burned so hard by the "created" event that was thrown at me even if not myself
      // was created but if a child of mine was created too... and we did not expected such behavior
      // that this time I wanted to find out what was going on here. Even though it seemed to
      // be not a problem

      this.proxies[componentName].createdCallback = function() {
        // window.createdCompontents[componentName].push(this)
        // console.log("[components] call createdCallback for " + componentName, this)
        ComponentLoader.onCreatedCallback(this, componentName, this)
      }
      this.proxies[componentName].attachedCallback = function() {
        ComponentLoader.onAttachedCallback(this, componentName)
      };
      this.proxies[componentName].detachedCallback = function() {
         ComponentLoader.onDetachedCallback(this, componentName)
      };

      // don't store it just in a lexical scope, but make it available for runtime development

      document.registerElement(componentName, {
        prototype: this.proxies[componentName]
      });
    })
  }

  // this function creates the bootstrap script for the component templates
  static createRegistrationScript(componentId) {
    var script = document.createElement("script");
    script.className = "registrationScript";
    script.innerHTML = "lively.registerTemplate()";
    return script;
  }

  // this function loads all unregistered elements, starts looking in lookupRoot,
  // if lookupRoot is not set, it looks in the whole document.body,
  // if deep is set, it also looks into shadow roots
  static loadUnresolved(lookupRoot, deep, debuggingHint) {
    lookupRoot = lookupRoot || document.body;

    var selector = ":unresolved";

    // find all unresolved elements looking downwards from lookupRoot
    var unresolved = Array.from(lookupRoot.querySelectorAll(selector));
    if (deep) {
      var deepUnresolved = findUnresolvedDeep(lookupRoot);
      unresolved = unresolved.concat(deepUnresolved);
    }

    function findUnresolvedDeep(root) {
      var shadow = root.shadowRoot;
      if (!shadow) {
        return [];
      }

      var result = Array.from(shadow.querySelectorAll(selector));

      Array.from(shadow.children).forEach((child) => {
        result = result.concat(findUnresolvedDeep(child));
      });

      return result;
    }

    // helper set to filter for unique tags
    var unique = new Set();

    var promises = unresolved.filter((el) => {
      // filter for unique tag names
      var name = el.nodeName.toLowerCase();
      return !unique.has(name) && unique.add(name);
    }).map(async (el) => {
      var name = el.nodeName.toLowerCase();
      if (loadingPromises[name]) {
        // the loading was already triggered
        return loadingPromises[name];
      }

      // create a promise that resolves once el is completely created
      var createdPromise = new Promise((resolve, reject) => {
        el.addEventListener("created", (evt) => {
          evt.stopPropagation();
          resolve(evt);
        });
      });

      // trigger loading the template of the unresolved element
      loadingPromises[name] = createdPromise;

      loadingPromises[name].name = name + " " + Date.now()

      await this.loadByName(name);

      return createdPromise;
    });

    // return a promise that resolves once all unresolved elements from the unresolved-array
    // are completely created
    return new Promise( (resolve, reject) => {

      // fuck promises!!!! I hate them. There is one promise pending.... but just does not fail. It just hangs around doing nothing! #Jens
      promises.forEach( p => {
        p.then( r => {
          p.finished = true;
        }, er => console.log("ERROR in promise: " + p.name))

      })
      window.setTimeout( function() {
        var unfinished = false;
        var unfinishedPromise;
        promises.forEach( p => {
          if (!p.finished) {
            unfinishedPromise = p
            unfinished = true;
          }
        })
        if (unfinished) {
          resolve("timeout") // "(if) the fuel gauge breaks, call maintenance. If they are not there in 20 minutes, fuck it."
          lively.notify("Timout due to unresolved promises, while loading " + unfinishedPromise.name + " context: " + debuggingHint )
        }
      }, 15 * 1000)

      Promise.all(promises).then( result => resolve(), reject => {
          console.log("ERROR loading " +reject)
          reject()
      })
    })
  }

  static getTemplatePaths() {
    if (!_templatePaths) {
      _templatePaths = [
        lively4url + '/templates/',
        lively4url + '/src/components/'
      ]; // default
    }
    return _templatePaths
  }

  static addTemplatePath(path) {
    var all = this.getTemplatePaths()
    if (!all.includes(path)) {
      all.push(path)
    }
  }

  static async searchTemplateFilename(filename) {
    var templatePaths =  this.getTemplatePaths()
    let templateDir = undefined;

    // #IDEA, using HTTP HEAD could be faster, but is not always implemented... as ource OPTIONS is neigher
    // this method avoids the 404 in the console.log
    for(templateDir of templatePaths) {
      var stats = await fetch(templateDir, { method: 'OPTIONS' }).then(resp => resp.json());
      var found = stats.contents.find(ea => ea.name == filename)
      if (found) break;
    }

    // so the server did not understand OPTIONS, so lets ask for the files directly
    if (!found) {
      for(templateDir of templatePaths) {
        var found = await fetch(templateDir + filename, { method: 'GET' }) // #TODO use HEAD, after implementing it in lively4-server
          .then(resp => resp.status == 200);
        if (found) break;
      }
      if (!found) return undefined;
    }
    return templateDir + filename
  }

  // this function loads a component by adding a link tag to the head
  static async loadByName(name) {
      var url = await this.searchTemplateFilename(name + '.html')
      if (!url) {
        throw new Error("Could not find template for " + name)
      }

      console.log("load component: " + url)

      // #TODO continue here url.exists()
      var link = document.createElement("link");
      link.rel = "import";
      link.href = url;
      link.dataset.lively4Donotpersist = "all";

      document.head.appendChild(link);
  }

  static createComponent(tagString) {
    var comp = document.createElement(tagString);
    return comp;
  }

  static async loadAndOpenComponent(componentName, immediate = () => {}) {
    var component = this.createComponent(componentName);

    const compPromise = this.openIn(<div />, component);
    immediate(component);

    return compPromise::through(comp => comp.remove());
  }

  static openIn(parent, component, beginning) {
    var created = false;
    var compPromise = new Promise((resolve, reject) => {
      component.addEventListener("created", (e) => {
        if (e.path[0] !== component) {
          // console.log("[components] ingnore and stop created event from child " + e.path[0].tagName);
          return
        }
        if (created) {
          // #Just check... we had this issue before
          throw new Error("[compontents] created called twice for " + component)
        } else {
          created = true
          e.stopPropagation();
          resolve(e.target);
        }

      });
    });

    if (beginning) {
      parent.insertBefore(component, parent.firstChild);
    } else {
      parent.appendChild(component);
    }
    this.loadUnresolved(parent, true, "openIn " + component);

    return compPromise;
  }

  static openInBody(component) {
    return this.openIn(document.body, component, true);
  }

  static openInWindow(component, pos, title) {
    // this will call the window's createdCallback before
    // we append the child, if the window template is already
    // loaded
    var w = this.createComponent("lively-window");
    if (pos) {
      lively.setPosition(w, pos);
    }
    w.style.opacity = 0.2
    w.appendChild(component);

    this.openInBody(w);

    // therefore, we need to call loadUnresolved again after
    // adding the child, so that it finds it and resolves it,
    // if it is currently unresolved
    var windowPromise = new Promise((resolve, reject) => {
      this.loadUnresolved(document.body, true, "openInWindow " + component).then(() => {
        w.style.opacity = 1
        if (component.windowTitle)
          w.setAttribute('title', '' + component.windowTitle);

        resolve(w);
      });
    });

    return windowPromise;
  }

  static openComponentBin() {
    var bin = createComponent("lively-component-bin");
    openInWindow(bin);
  }

  static reloadComponent(source) {
    var template =  $($.parseHTML(source)).filter("template")[0];
    if (!template) return;
    var name = template.id;
    if (!name) return;
    var templateClone = document.importNode(template.content, true);
    lively.components.templates[name] = templateClone;

    return lively.fillTemplateStyles(templateClone, "source: " + name).then( () => name);
  }

  // #Design #Draft Migration of class-side state (classes are objects themselve)
  static livelyMigrate(other) {

  }
}

// #Design #Draft Migration of module-side state (modules are objects themselve)
export function livelyMigrate(other) {
// Problem: we cannot look into internal "other" state, we can do this with objects but not with
// variable declarations, therefore we let our module system automigrate the module global variable state
}

<commit_msg>karma, how do you like this_<commit_after>import scriptManager from  "src/client/script-manager.js";
// import * as persistence from  "src/client/persistence.js";
import Morph from "templates/Morph.js";
import {pt} from '../graphics.js';
import { through } from "utils";

// store promises of loaded and currently loading templates
export var loadingPromises = {};

// #MetaNote #UserCase this is an example for preserving module internal state while reloading a module
var _templates;
var _prototypes;
var _proxies;
var _templatePaths;

// for compatibility
export function register(componentName, template, prototype) {
  return ComponentLoader.register(componentName, template, prototype);
}

/* #FutureWork should interactive state change of "(module) global" state be preserved while reloading / developing modules
    ComponentLoader.foo = 3
    ComponentLoader.foo

#Discussion

pro) expected in Smalltalk-like developent and live-programmning experience
contra) gap between development-time and runtime (those manualy changes could make something work that without it won't...)

synthese) if modules and classes are also objects that can have run-time-specific state they should be migrated the same as objects.

*/

export default class ComponentLoader {

  static get templates() {
    if (!_templates) _templates = {};
    return _templates;
  }

  static get prototypes() {
    if (!_prototypes) _prototypes = {};
    return _prototypes;
  }

  static get proxies() {
     if (!_proxies) _proxies = {};
    return _proxies;
  }

  static protypeToComponentName(prototype) {
    if (!prototype || !prototype.constructor) return
    var prototypeName =  prototype.constructor.name
    return _.keys(this.prototypes).find(name => {
      var otherProto = this.prototypes[name]
      var constructor = otherProto && otherProto.constructor;
      return constructor && (constructor.name ===  prototypeName)});
  }

  static updatePrototype(prototype) {
    var componentName = this.protypeToComponentName(prototype)
    if (componentName) {
      this.prototypes[componentName] = prototype
      this.proxies[componentName].__proto__ = prototype
    }
  }

  static onCreatedCallback(object, componentName) {
    // if (persistence.isCurrentlyCloning()) {
    //   return;
    // }

    var shadow = object.createShadowRoot();

    // clone the template again, so when more elements are created,
    // they get their own copy of elements
    var clone = document.importNode(ComponentLoader.templates[componentName], true);
    // #TODO replace the "template" reference with an indirection that can be changed from the outside,
    // e.g. var clone = document.importNode(this.templates[componentName], true);
    // but beeing able to modify it, because we have a reference should suffice at the moment...

    shadow.appendChild(clone);

    // attach lively4scripts from the shadow root to this
    scriptManager.attachScriptsFromShadowDOM(object);

    // attach lively4script from the instance
    scriptManager.findLively4Script(object, false);

    if (ComponentLoader.prototypes[componentName].createdCallback) {
      ComponentLoader.prototypes[componentName].createdCallback.call(object);
    }

    // load any unknown elements, which this component might introduce
    ComponentLoader.loadUnresolved(object, true, "onCreated " + componentName).then((args) => {
      // lively.fillTemplateStyles(object.shadowRoot, "source: " + componentName).then(() => {
        // call the initialize script, if it exists

        if (typeof object.initialize === "function") {
          object.initialize();
        }
        // console.log("dispatch created " +componentName )
        // console.log("Identitity: " + (window.LastRegistered === object))

        object.dispatchEvent(new Event("created"));
      // })
    }).catch( e => {
      console.error(e);
      return e
    });
  }

  static onAttachedCallback(object, componentName) {
    // if (ComponentLoader.proxies[componentName]) {
    //   console.log("[component loader] WARNING: no proxy for " + componentName )
    //   return
    // }

    if (object.attachedCallback &&
      ComponentLoader.proxies[componentName].attachedCallback != object.attachedCallback) {
        object.attachedCallback.call(object);
    }
    if (ComponentLoader.prototypes[componentName].attachedCallback) {
      ComponentLoader.prototypes[componentName].attachedCallback.call(object);
    }
  }

  static onDetachedCallback(object, componentName) {
    // if (ComponentLoader.proxies[componentName]) {
    //   console.log("[component loader] WARNING: no proxy for " + componentName )
    //   return
    // }

    if (object.detachedCallback
    && ComponentLoader.proxies[componentName].detachedCallback != object.detachedCallback) {
      object.detachedCallback.call(object);
    } else if (ComponentLoader.prototypes[componentName].detachedCallback) {
      ComponentLoader.prototypes[componentName].detachedCallback.call(object);
    }
  }

  // this function registers a custom element,
  // it is called from the bootstap code in the component templates
  static register(componentName, template, prototype) {
    var proto = prototype || Object.create(Morph.prototype);

    // For reflection and debugging
    this.templates[componentName] = template;
    this.prototypes[componentName] = proto;
    this.proxies[componentName] = Object.create(proto) // not changeable

    lively.fillTemplateStyles(template, "source: " + componentName).then( () => {

      // FOR DEBUGGING
      // if (!window.createdCompontents)
      //   window.createdCompontents = {}
      // window.createdCompontents[componentName] = []

      // #Mystery #Debugging #ExperienceReport
      // Task was to figure out why the created callback is called several times when loading the
      // componen for the first time? E.g 5 ace editors for the container, where 2 are actually needed
      // maybe they are also called for the template documents that we store?
      // e.g. lively-editor (+ 1 ace + lively-version (+1 ace)) + lively-version (1 ace)
      // that would actually account for the missing three instances
      // It is Saturday night... past midnight and I finnally have at least an hypothesis,
      // where I was debugging in the dark the last 3 hours.
      // I got burned so hard by the "created" event that was thrown at me even if not myself
      // was created but if a child of mine was created too... and we did not expected such behavior
      // that this time I wanted to find out what was going on here. Even though it seemed to
      // be not a problem

      this.proxies[componentName].createdCallback = function() {
        // window.createdCompontents[componentName].push(this)
        // console.log("[components] call createdCallback for " + componentName, this)
        ComponentLoader.onCreatedCallback(this, componentName, this)
      }
      this.proxies[componentName].attachedCallback = function() {
        ComponentLoader.onAttachedCallback(this, componentName)
      };
      this.proxies[componentName].detachedCallback = function() {
         ComponentLoader.onDetachedCallback(this, componentName)
      };

      // don't store it just in a lexical scope, but make it available for runtime development

      document.registerElement(componentName, {
        prototype: this.proxies[componentName]
      });
    })
  }

  // this function creates the bootstrap script for the component templates
  static createRegistrationScript(componentId) {
    var script = document.createElement("script");
    script.className = "registrationScript";
    script.innerHTML = "lively.registerTemplate()";
    return script;
  }

  // this function loads all unregistered elements, starts looking in lookupRoot,
  // if lookupRoot is not set, it looks in the whole document.body,
  // if deep is set, it also looks into shadow roots
  static loadUnresolved(lookupRoot, deep, debuggingHint) {
    lookupRoot = lookupRoot || document.body;

    var selector = ":unresolved";

    // find all unresolved elements looking downwards from lookupRoot
    var unresolved = Array.from(lookupRoot.querySelectorAll(selector));
    if (deep) {
      var deepUnresolved = findUnresolvedDeep(lookupRoot);
      unresolved = unresolved.concat(deepUnresolved);
    }

    function findUnresolvedDeep(root) {
      var shadow = root.shadowRoot;
      if (!shadow) {
        return [];
      }

      var result = Array.from(shadow.querySelectorAll(selector));

      Array.from(shadow.children).forEach((child) => {
        result = result.concat(findUnresolvedDeep(child));
      });

      return result;
    }

    // helper set to filter for unique tags
    var unique = new Set();

    var promises = unresolved.filter((el) => {
      // filter for unique tag names
      var name = el.nodeName.toLowerCase();
      return !unique.has(name) && unique.add(name);
    }).map(async (el) => {
      var name = el.nodeName.toLowerCase();
      if (loadingPromises[name]) {
        // the loading was already triggered
        return loadingPromises[name];
      }

      // create a promise that resolves once el is completely created
      var createdPromise = new Promise((resolve, reject) => {
        el.addEventListener("created", (evt) => {
          evt.stopPropagation();
          resolve(evt);
        });
      });

      // trigger loading the template of the unresolved element
      loadingPromises[name] = createdPromise;

      loadingPromises[name].name = name + " " + Date.now()

      await this.loadByName(name);

      return createdPromise;
    });

    // return a promise that resolves once all unresolved elements from the unresolved-array
    // are completely created
    return new Promise( (resolve, reject) => {

      // fuck promises!!!! I hate them. There is one promise pending.... but just does not fail. It just hangs around doing nothing! #Jens
      promises.forEach( p => {
        p.then( r => {
          p.finished = true;
        }, er => console.log("ERROR in promise: " + p.name))

      })
      window.setTimeout( function() {
        var unfinished = false;
        var unfinishedPromise;
        promises.forEach( p => {
          if (!p.finished) {
            unfinishedPromise = p
            unfinished = true;
          }
        })
        if (unfinished) {
          resolve("timeout") // "(if) the fuel gauge breaks, call maintenance. If they are not there in 20 minutes, fuck it."
          lively.notify("Timout due to unresolved promises, while loading " + unfinishedPromise.name + " context: " + debuggingHint )
        }
      }, 15 * 1000)

      Promise.all(promises).then( result => resolve(), reject => {
          console.log("ERROR loading " +reject)
          reject()
      })
    })
  }

  static getTemplatePaths() {
    if (!_templatePaths) {
      _templatePaths = [
        lively4url + '/templates/',
        lively4url + '/src/components/'
      ]; // default
    }
    return _templatePaths
  }

  static addTemplatePath(path) {
    var all = this.getTemplatePaths()
    if (!all.includes(path)) {
      all.push(path)
    }
  }

  static async searchTemplateFilename(filename) {
    var templatePaths =  this.getTemplatePaths()
    let templateDir = undefined;

    // #IDEA, using HTTP HEAD could be faster, but is not always implemented... as ource OPTIONS is neigher
    // this method avoids the 404 in the console.log

    // the OPTIONS request seems to break karma... waits to long..
	  if (!window.__karma__) {
      for(templateDir of templatePaths) {
        var stats = await fetch(templateDir, { method: 'OPTIONS' }).then(resp => resp.json());
        var found = stats.contents.find(ea => ea.name == filename)
        if (found) break;
      }
    }

    // so the server did not understand OPTIONS, so lets ask for the files directly
    if (!found) {
      for(templateDir of templatePaths) {
        var found = await fetch(templateDir + filename, { method: 'GET' }) // #TODO use HEAD, after implementing it in lively4-server
          .then(resp => resp.status == 200);
        if (found) break;
      }
      if (!found) return undefined;
    }
    return templateDir + filename
  }

  // this function loads a component by adding a link tag to the head
  static async loadByName(name) {
      var url = await this.searchTemplateFilename(name + '.html')
      if (!url) {
        throw new Error("Could not find template for " + name)
      }

      console.log("load component: " + url)

      // #TODO continue here url.exists()
      var link = document.createElement("link");
      link.rel = "import";
      link.href = url;
      link.dataset.lively4Donotpersist = "all";

      document.head.appendChild(link);
  }

  static createComponent(tagString) {
    var comp = document.createElement(tagString);
    return comp;
  }

  static async loadAndOpenComponent(componentName, immediate = () => {}) {
    var component = this.createComponent(componentName);

    const compPromise = this.openIn(<div />, component);
    immediate(component);

    return compPromise::through(comp => comp.remove());
  }

  static openIn(parent, component, beginning) {
    var created = false;
    var compPromise = new Promise((resolve, reject) => {
      component.addEventListener("created", (e) => {
        if (e.path[0] !== component) {
          // console.log("[components] ingnore and stop created event from child " + e.path[0].tagName);
          return
        }
        if (created) {
          // #Just check... we had this issue before
          throw new Error("[compontents] created called twice for " + component)
        } else {
          created = true
          e.stopPropagation();
          resolve(e.target);
        }

      });
    });

    if (beginning) {
      parent.insertBefore(component, parent.firstChild);
    } else {
      parent.appendChild(component);
    }
    this.loadUnresolved(parent, true, "openIn " + component);

    return compPromise;
  }

  static openInBody(component) {
    return this.openIn(document.body, component, true);
  }

  static openInWindow(component, pos, title) {
    // this will call the window's createdCallback before
    // we append the child, if the window template is already
    // loaded
    var w = this.createComponent("lively-window");
    if (pos) {
      lively.setPosition(w, pos);
    }
    w.style.opacity = 0.2
    w.appendChild(component);

    this.openInBody(w);

    // therefore, we need to call loadUnresolved again after
    // adding the child, so that it finds it and resolves it,
    // if it is currently unresolved
    var windowPromise = new Promise((resolve, reject) => {
      this.loadUnresolved(document.body, true, "openInWindow " + component).then(() => {
        w.style.opacity = 1
        if (component.windowTitle)
          w.setAttribute('title', '' + component.windowTitle);

        resolve(w);
      });
    });

    return windowPromise;
  }

  static openComponentBin() {
    var bin = createComponent("lively-component-bin");
    openInWindow(bin);
  }

  static reloadComponent(source) {
    var template =  $($.parseHTML(source)).filter("template")[0];
    if (!template) return;
    var name = template.id;
    if (!name) return;
    var templateClone = document.importNode(template.content, true);
    lively.components.templates[name] = templateClone;

    return lively.fillTemplateStyles(templateClone, "source: " + name).then( () => name);
  }

  // #Design #Draft Migration of class-side state (classes are objects themselve)
  static livelyMigrate(other) {

  }
}

// #Design #Draft Migration of module-side state (modules are objects themselve)
export function livelyMigrate(other) {
// Problem: we cannot look into internal "other" state, we can do this with objects but not with
// variable declarations, therefore we let our module system automigrate the module global variable state
}

<|endoftext|>


<commit_before>var timedCallouts = {
    timeoutID: null,
    storedRequest: null
};

var events = {};

events.onLoad = function (plugin) {
    timedCallouts.config = plugin.getConfig();

    timedCallouts.scheduleShoutout();
    timedCallouts.storedRequest = plugin;
};

events.onUnload = function () {
    clearTimeout(timedCallouts.timeoutID);
    timedCallouts.timeoutID = null;
};

events.plug_command_callouts = function (request) {
    var sendChat = timedCallouts.storedRequest.manager.getPlugin("plug").plugin.plug.sendChat;

    if (request.from.role < 4) {
        request.from.sendEmote("Sorry you do not have access to this command.");
        return false;
    }

    if (request.args === undefined) {
        // output the current callout count
        sendChat("There are currently " + timedCallouts.config.callouts.length
        + " entries in the list of valid callouts. To see each, please type /callouts view #");
    }
    else {
        var args = request.args;
        if (args[0] == "view") {
            if (!isNaN(args[1])) {
                // rettrieve a certain number
                sendChat(timedCallouts.config.callouts[args[1] - 1]);
            }
            else {
                sendChat("Invalid callout number. The correct format for this command is /callouts view #");
            }
        }
        else if (args[0] == "add") {
            // add a callout
            args.splice(0, 1);
            var newCallout = args.join(' ');
            timedCallouts.config.callouts.push(newCallout);
            sendChat("Added a callout: " + newCallout);
        }
        else if (args[0] == "del") {
            if (!isNaN(args[1])) {
                // remove a callout
                timedCallouts.config.callouts.splice(args[1] - 1, 1);
                sendChat("Removed 1 callout.")
            }
            else {
                sendChat("Invalid callout number. The correct format for this command is /callouts del #");
            }
        }
        else {
            sendChat("That command was not understood. The valid commands for /callouts are view|add|del")
        }
    }

};

timedCallouts.scheduleShoutout = function () {
    if (timedCallouts.timeoutID !== null) {
        // We've got a timeout scheduled already, let's clear it.
        clearTimeout(timedCallouts.timeoutID);
    }

    var shoutOutInterval = timedCallouts.config.shoutOutInterval * 1000 * 60; // Conversion
    // from
    // minutes
    // ->
    // milliseconds

    // Schedule a single timeout
    timedCallouts.timeoutID = setTimeout(timedCallouts.doShoutout, shoutOutInterval);
};
timedCallouts.doShoutout = function () {
    // The timeout has executed, we don't need the handle anymore.
    timedCallouts.timeoutID = null;

    var message = timedCallouts.config.callouts[Math.floor(Math.random() * timedCallouts.config.callouts.length)];
    if (message && message.trim().length) {
        console.log("scheduledShoutout: " + message);

        var sendChat = timedCallouts.storedRequest.manager.getPlugin("plug").plugin.plug.sendChat;
        sendChat(message);
    }

    // Reschedule ourselves
    timedCallouts.scheduleShoutout();
};

module.exports = {
    "events": events
};
<commit_msg>new callouts style, will now be configurable via slack too.<commit_after>var PluginInstance = require(__core + "PluginInstance.js");

var timedCallouts = new PluginInstance();

timedCallouts.vars = {
    timeoutID: null,
    storedRequest: null
};

timedCallouts.events.onLoad = function (plugin) {
    timedCallouts.config = plugin.getConfig();

    timedCallouts.scheduleShoutout();
    timedCallouts.storedRequest = plugin;
};

timedCallouts.events.onUnload = function () {
    clearTimeout(timedCallouts.timeoutID);
    timedCallouts.timeoutID = null;
};

timedCallouts.events.command_callouts = function (request) {
    if (request.from.role < 4) {
        request.from.sendEmote("Sorry you do not have access to this command.");
        return false;
    }

    console.log(request.args.length);

    if (request.args == 0) {
        // output the current callout count
        request.from.sendEmote("There are currently " + timedCallouts.config.callouts.length
        + " entries in the list of valid callouts. To see each, please type /callouts view #");
    }
    else {
        var args = request.args;
        if (args[0] == "view") {
            if (!isNaN(args[1])) {
                // rettrieve a certain number
                request.from.sendEmote(timedCallouts.config.callouts[args[1] - 1]);
            }
            else {
                request.from.sendEmote("Invalid callout number. The correct format for this command is /callouts view #");
            }
        }
        else if (args[0] == "add") {
            // add a callout
            args.splice(0, 1);
            var newCallout = args.join(' ');
            timedCallouts.config.callouts.push(newCallout);
            request.from.sendEmote("Added a callout: " + newCallout);
        }
        else if (args[0] == "del") {
            if (!isNaN(args[1])) {
                // remove a callout
                timedCallouts.config.callouts.splice(args[1] - 1, 1);
                request.from.sendEmote("Removed 1 callout.")
            }
            else {
                request.from.sendEmote("Invalid callout number. The correct format for this command is /callouts del #");
            }
        }
        else {
            request.from.sendEmote("That command was not understood. The valid commands for /callouts are view|add|del")
        }
    }

};

timedCallouts.scheduleShoutout = function () {
    if (timedCallouts.timeoutID !== null) {
        // We've got a timeout scheduled already, let's clear it.
        clearTimeout(timedCallouts.timeoutID);
    }

    var shoutOutInterval = timedCallouts.config.shoutOutInterval * 1000 * 60; // Conversion
    // from
    // minutes
    // ->
    // milliseconds

    // Schedule a single timeout
    timedCallouts.timeoutID = setTimeout(timedCallouts.doShoutout, shoutOutInterval);
};
timedCallouts.doShoutout = function () {
    // The timeout has executed, we don't need the handle anymore.
    timedCallouts.timeoutID = null;

    var message = timedCallouts.config.callouts[Math.floor(Math.random() * timedCallouts.config.callouts.length)];
    if (message && message.trim().length) {
        console.log("scheduledShoutout: " + message);

        var sendChat = timedCallouts.storedRequest.manager.getPlugin("plug").plugin.plug.sendChat;
        sendChat(message);
    }

    // Reschedule ourselves
    timedCallouts.scheduleShoutout();
};

module.exports = timedCallouts;
<|endoftext|>


<commit_before>//////////////////////////////////////////
// Needle -- Node Simple HTTP Client
// Written by Tomás Pollak <tomas@forkhq.com>
// (c) 2011 - Fork Ltd.
// MIT Licensed
//////////////////////////////////////////

var util = require('util'),
		fs = require('fs'),
		path = require('path'),
		http = require('http'),
		https = require('https'),
		url = require('url'),
		stringify = require('qs').stringify,
		version = JSON.parse(fs.readFileSync('./package.json').toString()).version;

try { var unzip = require('zlib').unzip; } catch(e){ /* zlib not supported */ }

var parsers = {
	'application/json': function(data, callback){
		callback(data && JSON.parse(data));
	}
};

try {
	var xml2js = require('xml2js');

	parsers['application/xml'] = function(data, callback){

		var xml_parser = new xml2js.Parser();

		xml_parser.on('end', callback);
		xml_parser.on('error', function(result) {
			// throw("Error parsing XML!")
			callback(null);
		});

		xml_parser.parseString(data);

	};

} catch(e) { }

// utility function for flattening params in multipart POST's

function flatten(object, into, prefix){

	into = into || {};

	for(key in object){
		var prefix_key = prefix ? prefix + "[" + key + "]" : key;
		var prop = object[key];

		if(prop && typeof prop === "object" && !(prop.file && prop.content_type))
			flatten(prop, into, prefix_key)
		else
			into[prefix_key] = prop;

	}

	return into;

}

var Needle = {

	default_boundary: '--------------------NODENEEDLEHTTPCLIENT',

	request: function(uri, method, data, options, callback){

		// normalize arguments
		var callback = (typeof options == 'function') ? options : callback;
		var options = options || {};
		if(uri.indexOf('http') == -1) uri = 'http://' + uri;

		var remote = url.parse(uri);
		var is_https = remote.protocol === "https:";
		var port = remote.port || (is_https ? 443 : 80);
		var protocol = is_https ? https : http;
		var request_compressed = options.compressed || false;
		var request_encoding = options.multipart ? 'binary' : 'utf8';

		var parse_response = options.parse == false ? false : true;
		var timeout = options.timeout || 10000; // 10 seconds timeout
		var timer = null;

		var headers = {
			"Host" : remote.hostname,
			"User-Agent": "Needle/" + version + " (NodeJS " + process.version + ")",
			"Connection": "close",
			"Accept": "*/*"
		}

		if(request_compressed) headers['Accept-Encoding'] = 'gzip,deflate';

		if(data) {
			if(options.multipart){
				var boundary = options.boundary || this.default_boundary;
				var post_data = this.build_multipart_body(data, boundary);
				headers['Content-Type'] = 'multipart/form-data; boundary=' + boundary;
			} else {
				var post_data = (typeof(data) === "string") ? data : stringify(data);
				headers['Content-Type'] = 'application/x-www-form-urlencoded'
			}
			headers['Content-Length'] = post_data.length;
			if(process.env.DEBUG) console.log(post_data);
		}

		for(h in options.headers)
			if(headers.hasOwnProperty(h)) headers[h] = options.headers[h];

		if(options.username && options.password){
			var b = new Buffer([options.username, options.password].join(':'));
			headers['Authorization'] = "Basic " + b.toString('base64');
		}

		var response_end = function(response, body){

			if(process.env.DEBUG) console.log(response.headers);
			var content_type = response.headers['content-type'] && response.headers['content-type'].split(';')[0];

			if(parse_response && parsers[content_type]) {
				parsers[content_type](body, function(result){
					if(callback) callback(null, response, result);
				});
			} else {
				if(callback) callback(null, response, body);
			}

		};

		var request_opts = {
			host: remote.hostname,
			port: port,
			path: remote.pathname + remote.search,
			method: method,
			headers: headers
		}

		if(process.env.DEBUG) console.log(request_opts);

		var request = protocol.request(request_opts, function(response){

			var body = '';
			var compressed = /gzip|deflate/.test(response.headers['content-encoding']);
			var response_encoding = compressed ? 'binary' : 'utf8';
			response.setEncoding(response_encoding);

			if(timer) clearTimeout(timer);

			response.on('data', function(chunk){
				body += chunk;
			});

			response.on('end', function() {
				if(typeof unzip != 'undefined' && compressed)
					unzip(new Buffer(body, 'binary'), function(err, buff){
						response_end(response, buff.toString())
					});
				else
					response_end(response, body);
			});

		});

		if(timeout) {
			timer = setTimeout(function() {
				request.abort();
			}, timeout)
		}

		request.on('error', function(err) {
			if(process.env.DEBUG) console.log('Error on request: ' + err);
			if(timer) clearTimeout(timer);
			if(callback) callback(err || true);
		});

		if(post_data) request.write(post_data, request_encoding);
		request.end();

		return request;

	},

	build_multipart_body: function(data, boundary){

		var body = '';
		var object = flatten(data);

		for(var key in object){

			var value = object[key];
			var part = value.file && value.content_type ? value : {value: value};
			body += this.generate_part(key, part, boundary);

		}

		return body + '\r\n' + '--' + boundary + '--';

	},

	generate_part: function(name, part, boundary){

		var return_part = '--' + boundary + "\r\n";
		return_part += "Content-Disposition: form-data; name=\"" + name + "\"";

		if(part.file && part.content_type){

			var filename = path.basename(part.file);
			var data = fs.readFileSync(part.file);

			return_part += "; filename=\"" + filename + "\"\r\n";
			return_part += "Content-Type: " + part.content_type + "\r\n\r\n";
			return_part += (part.content_type.indexOf('text') == -1)
				? data.toString('binary')
				: data.toString('utf8');

		} else {

			return_part += "\r\n\r\n";
			return_part += part.value;

		}

		return return_part + '\r\n';

	}

}

exports.get = function(uri, options, callback){
	return Needle.request(uri, "GET", null, options, callback);
}

exports.post = function(uri, data, options, callback){
	return Needle.request(uri, "POST", data, options, callback);
}

exports.put = function(uri, data, options, callback){
	return Needle.request(uri, "PUT", data, options, callback);
}

exports.delete = function(uri, data, options, callback){
	return Needle.request(uri, "DELETE", null, options, callback);
}
<commit_msg>Fixed remote.search when undefined<commit_after>//////////////////////////////////////////
// Needle -- Node Simple HTTP Client
// Written by Tomás Pollak <tomas@forkhq.com>
// (c) 2011 - Fork Ltd.
// MIT Licensed
//////////////////////////////////////////

var util = require('util'),
		fs = require('fs'),
		path = require('path'),
		http = require('http'),
		https = require('https'),
		url = require('url'),
		stringify = require('qs').stringify,
		version = JSON.parse(fs.readFileSync('./package.json').toString()).version;

try { var unzip = require('zlib').unzip; } catch(e){ /* zlib not supported */ }

var parsers = {
	'application/json': function(data, callback){
		callback(data && JSON.parse(data));
	}
};

try {
	var xml2js = require('xml2js');

	parsers['application/xml'] = function(data, callback){

		var xml_parser = new xml2js.Parser();

		xml_parser.on('end', callback);
		xml_parser.on('error', function(result) {
			// throw("Error parsing XML!")
			callback(null);
		});

		xml_parser.parseString(data);

	};

} catch(e) { }

// utility function for flattening params in multipart POST's

function flatten(object, into, prefix){

	into = into || {};

	for(key in object){
		var prefix_key = prefix ? prefix + "[" + key + "]" : key;
		var prop = object[key];

		if(prop && typeof prop === "object" && !(prop.file && prop.content_type))
			flatten(prop, into, prefix_key)
		else
			into[prefix_key] = prop;

	}

	return into;

}

var Needle = {

	default_boundary: '--------------------NODENEEDLEHTTPCLIENT',

	request: function(uri, method, data, options, callback){

		// normalize arguments
		var callback = (typeof options == 'function') ? options : callback;
		var options = options || {};
		if(uri.indexOf('http') == -1) uri = 'http://' + uri;

		var remote = url.parse(uri);
		var is_https = remote.protocol === "https:";
		var port = remote.port || (is_https ? 443 : 80);
		var protocol = is_https ? https : http;
		var request_compressed = options.compressed || false;
		var request_encoding = options.multipart ? 'binary' : 'utf8';

		var parse_response = options.parse == false ? false : true;
		var timeout = options.timeout || 10000; // 10 seconds timeout
		var timer = null;

		var headers = {
			"Host" : remote.hostname,
			"User-Agent": "Needle/" + version + " (NodeJS " + process.version + ")",
			"Connection": "close",
			"Accept": "*/*"
		}

		if(request_compressed) headers['Accept-Encoding'] = 'gzip,deflate';

		if(data) {
			if(options.multipart){
				var boundary = options.boundary || this.default_boundary;
				var post_data = this.build_multipart_body(data, boundary);
				headers['Content-Type'] = 'multipart/form-data; boundary=' + boundary;
			} else {
				var post_data = (typeof(data) === "string") ? data : stringify(data);
				headers['Content-Type'] = 'application/x-www-form-urlencoded'
			}
			headers['Content-Length'] = post_data.length;
			if(process.env.DEBUG) console.log(post_data);
		}

		for(h in options.headers)
			if(headers.hasOwnProperty(h)) headers[h] = options.headers[h];

		if(options.username && options.password){
			var b = new Buffer([options.username, options.password].join(':'));
			headers['Authorization'] = "Basic " + b.toString('base64');
		}

		var response_end = function(response, body){

			if(process.env.DEBUG) console.log(response.headers);
			var content_type = response.headers['content-type'] && response.headers['content-type'].split(';')[0];

			if(parse_response && parsers[content_type]) {
				parsers[content_type](body, function(result){
					if(callback) callback(null, response, result);
				});
			} else {
				if(callback) callback(null, response, body);
			}

		};

		var request_opts = {
			host: remote.hostname,
			port: port,
			path: remote.pathname + (remote.search || ""),
			method: method,
			headers: headers
		}

		if(process.env.DEBUG) console.log(request_opts);

		var request = protocol.request(request_opts, function(response){

			var body = '';
			var compressed = /gzip|deflate/.test(response.headers['content-encoding']);
			var response_encoding = compressed ? 'binary' : 'utf8';
			response.setEncoding(response_encoding);

			if(timer) clearTimeout(timer);

			response.on('data', function(chunk){
				body += chunk;
			});

			response.on('end', function() {
				if(typeof unzip != 'undefined' && compressed)
					unzip(new Buffer(body, 'binary'), function(err, buff){
						response_end(response, buff.toString())
					});
				else
					response_end(response, body);
			});

		});

		if(timeout) {
			timer = setTimeout(function() {
				request.abort();
			}, timeout)
		}

		request.on('error', function(err) {
			if(process.env.DEBUG) console.log('Error on request: ' + err);
			if(timer) clearTimeout(timer);
			if(callback) callback(err || true);
		});

		if(post_data) request.write(post_data, request_encoding);
		request.end();

		return request;

	},

	build_multipart_body: function(data, boundary){

		var body = '';
		var object = flatten(data);

		for(var key in object){

			var value = object[key];
			var part = value.file && value.content_type ? value : {value: value};
			body += this.generate_part(key, part, boundary);

		}

		return body + '\r\n' + '--' + boundary + '--';

	},

	generate_part: function(name, part, boundary){

		var return_part = '--' + boundary + "\r\n";
		return_part += "Content-Disposition: form-data; name=\"" + name + "\"";

		if(part.file && part.content_type){

			var filename = path.basename(part.file);
			var data = fs.readFileSync(part.file);

			return_part += "; filename=\"" + filename + "\"\r\n";
			return_part += "Content-Type: " + part.content_type + "\r\n\r\n";
			return_part += (part.content_type.indexOf('text') == -1)
				? data.toString('binary')
				: data.toString('utf8');

		} else {

			return_part += "\r\n\r\n";
			return_part += part.value;

		}

		return return_part + '\r\n';

	}

}

exports.get = function(uri, options, callback){
	return Needle.request(uri, "GET", null, options, callback);
}

exports.post = function(uri, data, options, callback){
	return Needle.request(uri, "POST", data, options, callback);
}

exports.put = function(uri, data, options, callback){
	return Needle.request(uri, "PUT", data, options, callback);
}

exports.delete = function(uri, data, options, callback){
	return Needle.request(uri, "DELETE", null, options, callback);
}
<|endoftext|>


<commit_before>const EventEmitter = require('events').EventEmitter;
const prism = require('prism-media');
const StreamDispatcher = require('../dispatcher/StreamDispatcher');

const FFMPEG_ARGUMENTS = [
  '-analyzeduration', '0',
  '-loglevel', '0',
  '-f', 's16le',
  '-ar', '48000',
  '-ac', '2',
];

/**
 * An Audio Player for a Voice Connection.
 * @private
 * @extends {EventEmitter}
 */
class AudioPlayer extends EventEmitter {
  constructor(voiceConnection) {
    super();
    /**
     * The voice connection that the player serves
     * @type {VoiceConnection}
     */
    this.voiceConnection = voiceConnection;

    this.dispatcher = null;

    this.streamingData = {
      channels: 2,
      sequence: 0,
      timestamp: 0,
    };

    this.voiceConnection.once('closing', () => this.destroyCurrentStream());
  }

  destroy() {
    this.destroyDispatcher();
  }

  destroyDispatcher() {
    if (this.dispatcher) {
      this.dispatcher.destroy();
      this.dispatcher = null;
    }
  }

  playUnknownStream(stream, options = {}) {
    this.destroyDispatcher();
    const ffmpeg = new prism.FFmpeg({ args: FFMPEG_ARGUMENTS });
    stream.pipe(ffmpeg);
    return this.playPCMStream(ffmpeg, options, { ffmpeg });
  }

  playPCMStream(stream, options = {}, streams = {}) {
    this.destroyDispatcher();
    const volume = streams.volume = new prism.VolumeTransformer16LE(null, { volume: 0.2 });
    const opus = streams.opus = new prism.opus.Encoder({ channels: 2, rate: 48000, frameSize: 960 });
    stream.pipe(volume).pipe(opus);
    return this.playOpusStream(opus, options, streams);
  }

  playOpusStream(stream, options = {}, streams = {}) {
    this.destroyDispatcher();
    streams.opus = stream;
    const dispatcher = this.dispatcher = this.createDispatcher(options, streams);
    stream.pipe(dispatcher);
    return dispatcher;
  }

  createDispatcher({ seek = 0, volume = 1, passes = 1 } = {}, streams) {
    this.destroyDispatcher();
    const options = { seek, volume, passes };
    const dispatcher = new StreamDispatcher(this, options, streams);
    dispatcher.on('speaking', value => this.voiceConnection.setSpeaking(value));
    return dispatcher;
  }
}

module.exports = AudioPlayer;
<commit_msg>Fix closing bug<commit_after>const EventEmitter = require('events').EventEmitter;
const prism = require('prism-media');
const StreamDispatcher = require('../dispatcher/StreamDispatcher');

const FFMPEG_ARGUMENTS = [
  '-analyzeduration', '0',
  '-loglevel', '0',
  '-f', 's16le',
  '-ar', '48000',
  '-ac', '2',
];

/**
 * An Audio Player for a Voice Connection.
 * @private
 * @extends {EventEmitter}
 */
class AudioPlayer extends EventEmitter {
  constructor(voiceConnection) {
    super();
    /**
     * The voice connection that the player serves
     * @type {VoiceConnection}
     */
    this.voiceConnection = voiceConnection;

    this.dispatcher = null;

    this.streamingData = {
      channels: 2,
      sequence: 0,
      timestamp: 0,
    };

    this.voiceConnection.once('closing', () => this.destroy());
  }

  destroy() {
    this.destroyDispatcher();
  }

  destroyDispatcher() {
    if (this.dispatcher) {
      this.dispatcher.destroy();
      this.dispatcher = null;
    }
  }

  playUnknownStream(stream, options = {}) {
    this.destroyDispatcher();
    const ffmpeg = new prism.FFmpeg({ args: FFMPEG_ARGUMENTS });
    stream.pipe(ffmpeg);
    return this.playPCMStream(ffmpeg, options, { ffmpeg });
  }

  playPCMStream(stream, options = {}, streams = {}) {
    this.destroyDispatcher();
    const volume = streams.volume = new prism.VolumeTransformer16LE(null, { volume: 0.2 });
    const opus = streams.opus = new prism.opus.Encoder({ channels: 2, rate: 48000, frameSize: 960 });
    stream.pipe(volume).pipe(opus);
    return this.playOpusStream(opus, options, streams);
  }

  playOpusStream(stream, options = {}, streams = {}) {
    this.destroyDispatcher();
    streams.opus = stream;
    const dispatcher = this.dispatcher = this.createDispatcher(options, streams);
    stream.pipe(dispatcher);
    return dispatcher;
  }

  createDispatcher({ seek = 0, volume = 1, passes = 1 } = {}, streams) {
    this.destroyDispatcher();
    const options = { seek, volume, passes };
    const dispatcher = new StreamDispatcher(this, options, streams);
    dispatcher.on('speaking', value => this.voiceConnection.setSpeaking(value));
    return dispatcher;
  }
}

module.exports = AudioPlayer;
<|endoftext|>


<commit_before>var querystring= require('querystring'),
    libRequest = require ('request'),
    crypto= require('crypto'),
    https= require('https'),
    http= require('http'),
    URL= require('url'),
    OAuthUtils= require('./_utils');

exports.OAuth2= function(clientId, clientSecret, baseSite, authorizePath, accessTokenPath) {
  this._clientId= clientId;
  this._clientSecret= clientSecret;
  this._baseSite= baseSite;
  this._authorizeUrl= authorizePath || "/oauth/authorize";
  this._accessTokenUrl= accessTokenPath || "/oauth/access_token";
  this._accessTokenName= "access_token";
}

// This 'hack' method is required for sites that don't use
// 'access_token' as the name of the access token (for requests).
// ( http://tools.ietf.org/html/draft-ietf-oauth-v2-16#section-7 )
// it isn't clear what the correct value should be atm, so allowing
// for specific (temporary?) override for now.
exports.OAuth2.prototype.setAccessTokenName= function ( name ) {
  this._accessTokenName= name;
}

exports.OAuth2.prototype._getAccessTokenUrl= function() {
  return this._baseSite + this._accessTokenUrl; /* + "?" + querystring.stringify(params); */
}

exports.OAuth2.prototype._getAuthorizeUrl= function() {
  return this._baseSite + this._authorizeUrl; /* + "?" + querystring.stringify(params); */
}

exports.OAuth2.prototype._request= function(method, url, headers, post_body, access_token, callback) {
  var http_library= https;
  var creds = crypto.createCredentials({ });
  var parsedUrl= URL.parse( url, true );
  if( parsedUrl.protocol == "https:" && !parsedUrl.port ) {
    parsedUrl.port= 443;
  }

  // As this is OAUth2, we *assume* https unless told explicitly otherwise.
  if( parsedUrl.protocol != "https:" ) {
    http_library= http;
  }

  var realHeaders= {};
  if( headers ) {
    for(var key in headers) {
      realHeaders[key] = headers[key];
    }
  }
  realHeaders['Host']= parsedUrl.host;
  realHeaders['Content-Length']= post_body ? Buffer.byteLength(post_body) : 0;
  if (access_token) {
    realHeaders['Authorization'] = 'Bearer ' + access_token;
  }

/*
  if (isDataRequest) {
    console.log('isDataRequest');
    console.log(url);
    console.log(post_body);
    libRequest({
        uri: url,
        body: post_body,
        method: method,
        headers: realHeaders
    }, callback);
    return;
  }
*/
  var result= "";
  var queryStr= querystring.stringify(parsedUrl.query);
  if( queryStr ) queryStr=  "?" + queryStr;
  var options = {
    host:parsedUrl.hostname,
    port: parsedUrl.port,
    path: parsedUrl.pathname + queryStr,
    method: method,
    headers: realHeaders
  };

  // Some hosts *cough* google appear to close the connection early / send no content-length header
  // allow this behaviour.
  var allowEarlyClose= OAuthUtils.isAnEarlyCloseHost(options.host);
  var callbackCalled= false;
  function passBackControl( response, result ) {
    if(!callbackCalled) {
      callbackCalled=true;
      if( response.statusCode != 200 && (response.statusCode != 301) && (response.statusCode != 302) && (response.statusCode != 303) ) {
        callback({ statusCode: response.statusCode, data: result });
      } else {
        console.log('------->response-------->');
        console.log(result);
        console.log('<-------response<--------');
        callback(null, result, response);

      }
    }
  }

  var request = http_library.request(options, function (response) {
    response.on("data", function (chunk) {
      result+= chunk
    });
    response.on("close", function (err) {
      if( allowEarlyClose ) {
        passBackControl( response, result );
      }
    });
    response.addListener("end", function () {
      passBackControl( response, result );
    });
  });
  request.on('error', function(e) {
    callbackCalled= true;
    callback(e);
  });

  if ((method != 'GET') && post_body) {
     request.write(post_body);
  }
  request.end();
}

exports.OAuth2.prototype.getAuthorizeUrl= function( params ) {
  var params= params || {};
  params['client_id'] = this._clientId;
  params['type'] = 'web_server';
  return this._baseSite + this._authorizeUrl + "?" + querystring.stringify(params);
}

exports.OAuth2.prototype.getOAuthRequestToken= function(code, params, callback) {
  var params= params || {};
  params['client_id'] = this._clientId;
  params['client_secret'] = this._clientSecret;
  params['type']= 'web_server';
  var codeParam = (params.grant_type === 'refresh_token') ? 'refresh_token' : 'code';
  params[codeParam]= code;

  var post_data= querystring.stringify( params );
  var post_headers= {
       'Content-Type': 'application/x-www-form-urlencoded'
   };
  this._request("POST", this._getAuthorizeUrl(), post_headers, post_data, null, function(error, data, response) {
    if (error) {
      console.log('error occured');
      console.log(error);
      callback(error);
    } else {
      var results;
      try {
        results = JSON.parse( data );
      } catch(e) {
        results = querystring.parse( data );
      }
      var request_token = results["request_token"];
      delete results["request_token"];
      callback(null, request_token, results);
    }
  });
}

exports.OAuth2.prototype.getOAuthAccessToken= function(code, params, callback) {
  var params= params || {};
  params['client_id'] = this._clientId;
  params['client_secret'] = this._clientSecret;
  params['type']= 'web_server';
  var codeParam = (params.grant_type === 'refresh_token') ? 'refresh_token' : 'code';
  params[codeParam]= code;

  var post_data= querystring.stringify( params );
  var post_headers= {
    'Content-Type': 'application/x-www-form-urlencoded'
  };
  this._request("POST", this._getAccessTokenUrl(), post_headers, post_data, null, function(error, data, response) {
    if( error )  callback(error);
    else {
      var results;
      try {
        // As of http://tools.ietf.org/html/draft-ietf-oauth-v2-07
        // responses should be in JSON
        results= JSON.parse( data );
      }
      catch(e) {
        // .... However both Facebook + Github currently use rev05 of the spec
        // and neither seem to specify a content-type correctly in their response headers :(
        // clients of these services will suffer a *minor* performance cost of the exception
        // being thrown
        results= querystring.parse( data );
      }
      var access_token= results["access_token"];
      var refresh_token= results["refresh_token"];
      delete results["refresh_token"];
      callback(null, access_token, refresh_token, results); // callback results =-=
    }
  });
}

exports.OAuth2.prototype.req= function(method, url, callback, body) {

console.log('-------------------------------->');
console.log(method + ' ' + url);
console.log(body);
console.log('<--------------------------------');

  var headers;
  body = body || "";
  if (method != "GET" && body != "") {
    headers = {'Content-Type': 'application/json'};
  }
  this._request(method, url, headers, body, this.accessToken, callback);
}

// Deprecated
exports.OAuth2.prototype.getProtectedResource= function(url, access_token, callback) {
  this._request("GET", url, {}, "", access_token, callback);
}

exports.OAuth2.prototype.get= function(url, access_token, callback) {
  this._request("GET", url, {}, "", access_token, callback);
}
<commit_msg>Removed excessive logging<commit_after>var querystring= require('querystring'),
    libRequest = require ('request'),
    crypto= require('crypto'),
    https= require('https'),
    http= require('http'),
    URL= require('url'),
    OAuthUtils= require('./_utils');

exports.OAuth2= function(clientId, clientSecret, baseSite, authorizePath, accessTokenPath) {
  this._clientId= clientId;
  this._clientSecret= clientSecret;
  this._baseSite= baseSite;
  this._authorizeUrl= authorizePath || "/oauth/authorize";
  this._accessTokenUrl= accessTokenPath || "/oauth/access_token";
  this._accessTokenName= "access_token";
}

// This 'hack' method is required for sites that don't use
// 'access_token' as the name of the access token (for requests).
// ( http://tools.ietf.org/html/draft-ietf-oauth-v2-16#section-7 )
// it isn't clear what the correct value should be atm, so allowing
// for specific (temporary?) override for now.
exports.OAuth2.prototype.setAccessTokenName= function ( name ) {
  this._accessTokenName= name;
}

exports.OAuth2.prototype._getAccessTokenUrl= function() {
  return this._baseSite + this._accessTokenUrl; /* + "?" + querystring.stringify(params); */
}

exports.OAuth2.prototype._getAuthorizeUrl= function() {
  return this._baseSite + this._authorizeUrl; /* + "?" + querystring.stringify(params); */
}

exports.OAuth2.prototype._request= function(method, url, headers, post_body, access_token, callback) {
  var http_library= https;
  var creds = crypto.createCredentials({ });
  var parsedUrl= URL.parse( url, true );
  if( parsedUrl.protocol == "https:" && !parsedUrl.port ) {
    parsedUrl.port= 443;
  }

  // As this is OAUth2, we *assume* https unless told explicitly otherwise.
  if( parsedUrl.protocol != "https:" ) {
    http_library= http;
  }

  var realHeaders= {};
  if( headers ) {
    for(var key in headers) {
      realHeaders[key] = headers[key];
    }
  }
  realHeaders['Host']= parsedUrl.host;
  realHeaders['Content-Length']= post_body ? Buffer.byteLength(post_body) : 0;
  if (access_token) {
    realHeaders['Authorization'] = 'Bearer ' + access_token;
  }

  var result= "";
  var queryStr= querystring.stringify(parsedUrl.query);
  if( queryStr ) queryStr=  "?" + queryStr;
  var options = {
    host:parsedUrl.hostname,
    port: parsedUrl.port,
    path: parsedUrl.pathname + queryStr,
    method: method,
    headers: realHeaders
  };

  // Some hosts *cough* google appear to close the connection early / send no content-length header
  // allow this behaviour.
  var allowEarlyClose= OAuthUtils.isAnEarlyCloseHost(options.host);
  var callbackCalled= false;
  function passBackControl( response, result ) {
    if(!callbackCalled) {
      callbackCalled=true;
      if( response.statusCode != 200 && (response.statusCode != 301) && (response.statusCode != 302) && (response.statusCode != 303) ) {
        callback({ statusCode: response.statusCode, data: result });
      } else {
        callback(null, result, response);

      }
    }
  }

  var request = http_library.request(options, function (response) {
    response.on("data", function (chunk) {
      result+= chunk
    });
    response.on("close", function (err) {
      if( allowEarlyClose ) {
        passBackControl( response, result );
      }
    });
    response.addListener("end", function () {
      passBackControl( response, result );
    });
  });
  request.on('error', function(e) {
    callbackCalled= true;
    callback(e);
  });

  if ((method != 'GET') && post_body) {
     request.write(post_body);
  }
  request.end();
}

exports.OAuth2.prototype.getAuthorizeUrl= function( params ) {
  var params= params || {};
  params['client_id'] = this._clientId;
  params['type'] = 'web_server';
  return this._baseSite + this._authorizeUrl + "?" + querystring.stringify(params);
}

exports.OAuth2.prototype.getOAuthRequestToken= function(code, params, callback) {
  var params= params || {};
  params['client_id'] = this._clientId;
  params['client_secret'] = this._clientSecret;
  params['type']= 'web_server';
  var codeParam = (params.grant_type === 'refresh_token') ? 'refresh_token' : 'code';
  params[codeParam]= code;

  var post_data= querystring.stringify( params );
  var post_headers= {
       'Content-Type': 'application/x-www-form-urlencoded'
   };
  this._request("POST", this._getAuthorizeUrl(), post_headers, post_data, null, function(error, data, response) {
    if (error) {
      callback(error);
    } else {
      var results;
      try {
        results = JSON.parse( data );
      } catch(e) {
        results = querystring.parse( data );
      }
      var request_token = results["request_token"];
      delete results["request_token"];
      callback(null, request_token, results);
    }
  });
}

exports.OAuth2.prototype.getOAuthAccessToken= function(code, params, callback) {
  var params= params || {};
  params['client_id'] = this._clientId;
  params['client_secret'] = this._clientSecret;
  params['type']= 'web_server';
  var codeParam = (params.grant_type === 'refresh_token') ? 'refresh_token' : 'code';
  params[codeParam]= code;

  var post_data= querystring.stringify( params );
  var post_headers= {
    'Content-Type': 'application/x-www-form-urlencoded'
  };
  this._request("POST", this._getAccessTokenUrl(), post_headers, post_data, null, function(error, data, response) {
    if( error )  callback(error);
    else {
      var results;
      try {
        // As of http://tools.ietf.org/html/draft-ietf-oauth-v2-07
        // responses should be in JSON
        results= JSON.parse( data );
      }
      catch(e) {
        // .... However both Facebook + Github currently use rev05 of the spec
        // and neither seem to specify a content-type correctly in their response headers :(
        // clients of these services will suffer a *minor* performance cost of the exception
        // being thrown
        results= querystring.parse( data );
      }
      var access_token= results["access_token"];
      var refresh_token= results["refresh_token"];
      delete results["refresh_token"];
      callback(null, access_token, refresh_token, results); // callback results =-=
    }
  });
}

exports.OAuth2.prototype.req= function(method, url, callback, body) {
  var headers;
  body = body || "";
  if (method != "GET" && body != "") {
    headers = {'Content-Type': 'application/json'};
  }
  this._request(method, url, headers, body, this.accessToken, callback);
}

// Deprecated
exports.OAuth2.prototype.getProtectedResource= function(url, access_token, callback) {
  this._request("GET", url, {}, "", access_token, callback);
}

exports.OAuth2.prototype.get= function(url, access_token, callback) {
  this._request("GET", url, {}, "", access_token, callback);
}
<|endoftext|>


<commit_before>/**
 * Native
 * ------
 *
 * Extension
 * ---------
 * .keys()              Get a list of keys of the object
 * .isset()             Returns true if a key is set (same as .hasOwnProperty)
 * .unset()             Deletes a key
 **/
var Extensions = {};

module.exports.extendNative = function () {
	for (fun in Extensions) {
		if (!Object.prototype[fun]) {
			Object.defineProperty(Object.prototype, fun, {
				"value": Extensions[fun],
				"configurable": false,
				"enumerable": false,
				"writable": false
			});
		}
	}
};

Extensions.watch = function (prop, handler) {
	var oldval = this[prop],
	    newval = oldval,
	    getter = function () { return newval },
	    setter = function (val) { oldval = newval; return newval = handler.call(this, prop, oldval, val) };

	if (delete this[prop]) { // can't watch constants
		if (Object.defineProperty) { // ECMAScript 5
			Object.defineProperty(this, prop, {
				"get": getter,
				"set": setter,
				"enumerable": false,
				"configurable": true
			});
		} else if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) { // legacy
			Object.prototype.__defineGetter__.call(this, prop, getter);
			Object.prototype.__defineSetter__.call(this, prop, setter);
		}
	}
};
Extensions.unwatch = function (prop) {
	var val = this[prop];
	delete this[prop]; // remove accessors
	this[prop] = val;
};
Extensions.keys = function () {
	var keys = [];

	for (k in this) {
		if (this.hasOwnProperty(k)) {
			keys.push(k);
		}
	}
	return keys;
};
Extensions.isset = function (k) {
	return this.hasOwnProperty(k);
};
Extensions.unset = function (k) {
	delete this[k];
	return this;
};<commit_msg>- Remove deprecated code for Object.watch()<commit_after>/**
 * Native
 * ------
 *
 * Extension
 * ---------
 * .keys()              Get a list of keys of the object
 * .isset()             Returns true if a key is set (same as .hasOwnProperty)
 * .unset()             Deletes a key
 **/
var Extensions = {};

module.exports.extendNative = function () {
	for (fun in Extensions) {
		if (!Object.prototype[fun]) {
			Object.defineProperty(Object.prototype, fun, {
				"value": Extensions[fun],
				"configurable": false,
				"enumerable": false,
				"writable": false
			});
		}
	}
};

Extensions.watch = function (prop, handler) {
	var oldval = this[prop],
	    newval = oldval,
	    getter = function () { return newval },
	    setter = function (val) { oldval = newval; return newval = handler.call(this, prop, oldval, val) };

	if (delete this[prop]) { // can't watch constants
		Object.defineProperty(this, prop, {
			"get": getter,
			"set": setter,
			"enumerable": false,
			"configurable": true
		});
	}
};
Extensions.unwatch = function (prop) {
	var val = this[prop];
	delete this[prop]; // remove accessors
	this[prop] = val;
};
Extensions.keys = function () {
	var keys = [];

	for (k in this) {
		if (this.hasOwnProperty(k)) {
			keys.push(k);
		}
	}
	return keys;
};
Extensions.isset = function (k) {
	return this.hasOwnProperty(k);
};
Extensions.unset = function (k) {
	delete this[k];
	return this;
};<|endoftext|>