<commit_before>/* jslint node: true */

var _   = require('underscore');
var get = require('get');

function getServer(config) {

  /**
   *  The actual server
   */
  var express = require('express');
  var server = express.createServer();
  server.configure(function() {
    server.set('views', __dirname + '/../views');
  });

  var urlvalidation = require('./urlvalidation');
  var blacklist     = require('./blacklist');
  var urlbuilder    = require('./urlbuilder');
  blacklist.set_API_URL(config.API_URL);
  blacklist.set_API_OPTIONS(config.API_OPTIONS);

  /**
   *  Use Jade as templating engine
   */
  server.set('view engine', 'jade');

  /**
   *  Routes and request handling
   */
  server.get('/',       render_page('index'));
  server.get('/header', render_page('header'));
  server.get('/unsupported', render_page('index', config.PROXY_URL + "/inner_unsupported"));
  server.get('/inner_unsupported', render_page('inner_unsupported'));
  server.get('/search', get_search);
  server.get('/parse',  get_parse);
  server.get('/submit', get_submit);

  /**
   * Static file serving in develop
   */
  if (config.ENV === 'development') {
    server.use("/static/", express.static(__dirname + "/../static/"));
  }

  // Hacky-hack to allow invalid SSL connections anyway
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

  function parse_int_or_null(variable) {
    return parseInt(variable, 10) || null;
  }

  var regex_skip_until_in_html_head =/(?:[^<]|<(?:!\s*--(?:(?!-->).)*-->|html|head|[^a-z/]))*/i;
  /* Explanation of the above regex:
   * We want to inject stuff into the <head>, but <head> may be implicit.
   * Though a <head> must officially have a <title>, omitting it doesn't break browsers, so what to detect?
   * Officially, every head needs a <title>, but invalid docs might omit it.  However, every head
   * needs *some* tag; we can just match the first tag that's not <head> or <html> - if a head really is empty, we'll match the closing </head> or <body> tags.
   *
   * Strategy: skip everything that's not a tag in the head:
   *  1. non '<' characters can be skipped; they're not interesting
   *  2. full comments <! -- comment --> can be skipped
   *  3. <head or <html can be skipped.
   *  4. <??? can be skipped when ??? isn't a closing tag so doesn't start with '/' and isn't an opening tag so doesn't start with 'a'-'z'
   * After this match we can't consume tokens and so must be at:
   * - the end of the document OR
   * - some string starting with '<' due to rule 1
   * -- BUT NOT a comment
   * -- AND NOT <html or <head
   * -- AND at a starting or closing tag.
   */

  function inject_html_in_head(page_html, fragment_to_inject) {
    "use strict";
    return page_html.replace(regex_skip_until_in_html_head, function(match) {
      return match + fragment_to_inject;
    });
  }

  function publisherUrl(site, scroll_to, open_id) {
    open_id = parse_int_or_null(open_id);
    scroll_to = parse_int_or_null(scroll_to);

    if (open_id !== null) {
      return site + '#factlink-open-' + open_id;
    } else if (scroll_to !== null) {
      return site + '#factlink-' + scroll_to;
    } else {
      return site;
    }
  }

  /**
   * Add base url and inject proxy.js, and return the proxied site
   */
  function injectFactlinkJs(original_html, site, scroll_to, open_id, successFn) {
    "use strict";

    if (/factlink_loader_publishers.min.js/.test(original_html)) {
      if (config.ENV === "development") {
        // Disable publisher's script in development mode
        original_html = original_html.replace(/factlink_loader_publishers.min.js/g, 'factlink_loader_publishers_DEACTIVATED.min.js');
      } else {
        // Redirect to publishers' sites
        // Circumvent blacklist as we assume we don't want to blacklist publishers for now, and it's faster
        // to not check.
        var redirect_url = publisherUrl(site, scroll_to, open_id);

        successFn('<script>window.parent.location = ' + JSON.stringify(redirect_url) + ';</script>');
        return;
      }
    }

    blacklist.if_allowed(site,function() {

      var output_html = original_html;

      var new_base_tag = '<base href="' + site + '" />';

      var framebuster_script = 'window.self = window.top;\n\n';

      // Inject config also at the top
      var FactlinkConfig = {
        api: config.API_URL,
        lib: config.LIB_URL,
        srcPath: config.ENV === "development" ? "/factlink.core.js" : "/factlink.core.min.js",
        url: site
      };
      var factlink_config_script = 'window.FactlinkConfig = ' + JSON.stringify(FactlinkConfig) + ';\n';
      var factlink_proxy_url_script = 'window.FactlinkProxyUrl = ' + JSON.stringify(config.PROXY_URL) + ';\n';

      var inline_setup_script_tag = '<script>' + framebuster_script +
        factlink_config_script + factlink_proxy_url_script + '</script>';

      var actions = [];

      open_id = parse_int_or_null(open_id) ;
      scroll_to = parse_int_or_null(scroll_to) || open_id;

      if (open_id !== null) {
        actions.push('FACTLINK.openFactlinkModal(' + open_id + ');');
      }

      if (scroll_to !== null) {
        actions.push('FACTLINK.scrollTo(' + scroll_to + ');');
      }

      actions.push('FACTLINK.startHighlighting();');
      actions.push('FACTLINK.startAnnotating();');

      // Inject Factlink library at the end of the file
      var loader_filename = (config.ENV === "development" ? "/factlink_loader_basic.js" : "/factlink_loader_basic.min.js");

      var loader_tag = '<script async defer src="' + config.LIB_URL + loader_filename + '" onload="'+actions.join('')+ '"></script>';
      var proxy_script_tag = '<script async defer src="' + config.PROXY_URL + '/static/scripts/proxy.js?1"></script>';

      var header_content = new_base_tag + inline_setup_script_tag + loader_tag + proxy_script_tag;

      output_html = inject_html_in_head(output_html, header_content);
      successFn(output_html);
    },function(){
      successFn(original_html);
    });
  }

  function handleProxyRequest(res, url, scrollto, open_id, form_hash) {
    if ( typeof url !== "string" || url.length === 0) {
      renderWelcomePage(res);
      return;
    } else {
      site = urlvalidation.clean_url(url);
      if (site === undefined) {
        console.error('Rendered "Something went wrong" page because of urlvalidation.clean_url on ' + url);
        renderErrorPage(res, url);
      } else {
        get(site).asString(function(err, str) {
          if(err) {
            console.error('Rendered "Something went wrong" page because could not download page on ' + url);
            renderErrorPage(res, url);
          } else {
            renderProxiedPage(res, site, scrollto, open_id, str);
          }
        });
      }
    }
  }

  function renderProxiedPage(res, site, scrollto, open_id, html_in) {
    injectFactlinkJs(html_in, site, scrollto, open_id, function(html) {
      res.writeHead(200, {'Content-Type': 'text/html'});
      res.write(html);
      res.end();
    });
  }

  function renderErrorPage(res, url){
    res.render('something_went_wrong', {
      layout: false,
      locals: {
        static_url: config.STATIC_URL,
        proxy_url: config.PROXY_URL,
        site: url
      }
    });
  }

  function renderWelcomePage(res){
    res.render('welcome.jade',{
      layout:false,
      locals: {
        proxy_url:      config.PROXY_URL,
        core_url:       config.API_URL,
        static_url:     config.STATIC_URL
      }
    });
  }

  /**
   *  Render a jade template
   */
  function render_page(pagename, page_url) {
    return function(req, res) {
      var header_url  = urlbuilder.create_url(config.PROXY_URL + "/header", req.query);
      var parse_url   = page_url || urlbuilder.create_url(config.PROXY_URL + "/parse", req.query);
      res.render(pagename, {
        layout: false,
        locals: {
          static_url: config.STATIC_URL,
          proxy_url: config.PROXY_URL,
          core_url: config.API_URL,
          page_url: req.query.url,
          clean_page_url: urlvalidation.clean_url(req.query.url),
          factlinkModus: req.query.factlinkModus,
          header_url: header_url,
          parse_url: parse_url
        }
      });
    };
  }

  /**
   *  Search on Factlink enabled Google
   */
  function get_search(req, res) {
    var query             = req.query.query;
    var search_redir_url  = urlbuilder.search_redir_url(config.PROXY_URL, query, req.query.factlinkModus);

    res.redirect(search_redir_url);
  }

  /**
   *  Inject Factlink in regular get requests
   */
  function get_parse(req, res) {
    var site     = req.query.url;
    var scrollto = req.query.scrollto;
    var open_id  = req.query.open_id;
    handleProxyRequest(res, site, scrollto, open_id, {});
  }

  /**
   * Forms get posted with a hidden 'factlinkFormUrl' field,
   * which is added by the proxy (in proxy.js). This is the 'action' URL which
   * the form normally submits its form to.
   */
  function get_submit(req, res) {
    var form_hash = req.query;
    var site      = form_hash.factlinkFormUrl;
    delete form_hash.factlinkFormUrl;

    handleProxyRequest(res, site, undefined, undefined, {
      'query': form_hash
    });
  }

  return server;
}
exports.getServer = getServer;
<commit_msg>Add a delayed javascript hander to the web proxy<commit_after>/* jslint node: true */

var _   = require('underscore');
var get = require('get');

function getServer(config) {

  /**
   *  The actual server
   */
  var express = require('express');
  var server = express.createServer();
  server.configure(function() {
    server.set('views', __dirname + '/../views');
  });

  var urlvalidation = require('./urlvalidation');
  var blacklist     = require('./blacklist');
  var urlbuilder    = require('./urlbuilder');
  blacklist.set_API_URL(config.API_URL);
  blacklist.set_API_OPTIONS(config.API_OPTIONS);

  /**
   *  Use Jade as templating engine
   */
  server.set('view engine', 'jade');

  /**
   *  Routes and request handling
   */
  server.get('/',       render_page('index'));
  server.get('/header', render_page('header'));
  server.get('/unsupported', render_page('index', config.PROXY_URL + "/inner_unsupported"));
  server.get('/inner_unsupported', render_page('inner_unsupported'));
  server.get('/search', get_search);
  server.get('/parse',  get_parse);
  server.get('/submit', get_submit);
  server.get('/delayed_javascript', function(req, res) {
    setTimeout(function(){
      res.setHeader('Content-Type', 'application/javascript');
      res.send('console.log("loaded intentionally delayed script!");');
    },3000);
  });

  /**
   * Static file serving in develop
   */
  if (config.ENV === 'development') {
    server.use("/static/", express.static(__dirname + "/../static/"));
  }

  // Hacky-hack to allow invalid SSL connections anyway
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

  function parse_int_or_null(variable) {
    return parseInt(variable, 10) || null;
  }

  var regex_skip_until_in_html_head =/(?:[^<]|<(?:!\s*--(?:(?!-->).)*-->|html|head|[^a-z/]))*/i;
  /* Explanation of the above regex:
   * We want to inject stuff into the <head>, but <head> may be implicit.
   * Though a <head> must officially have a <title>, omitting it doesn't break browsers, so what to detect?
   * Officially, every head needs a <title>, but invalid docs might omit it.  However, every head
   * needs *some* tag; we can just match the first tag that's not <head> or <html> - if a head really is empty, we'll match the closing </head> or <body> tags.
   *
   * Strategy: skip everything that's not a tag in the head:
   *  1. non '<' characters can be skipped; they're not interesting
   *  2. full comments <! -- comment --> can be skipped
   *  3. <head or <html can be skipped.
   *  4. <??? can be skipped when ??? isn't a closing tag so doesn't start with '/' and isn't an opening tag so doesn't start with 'a'-'z'
   * After this match we can't consume tokens and so must be at:
   * - the end of the document OR
   * - some string starting with '<' due to rule 1
   * -- BUT NOT a comment
   * -- AND NOT <html or <head
   * -- AND at a starting or closing tag.
   */

  function inject_html_in_head(page_html, fragment_to_inject) {
    "use strict";
    return page_html.replace(regex_skip_until_in_html_head, function(match) {
      return match + fragment_to_inject;
    });
  }

  function publisherUrl(site, scroll_to, open_id) {
    open_id = parse_int_or_null(open_id);
    scroll_to = parse_int_or_null(scroll_to);

    if (open_id !== null) {
      return site + '#factlink-open-' + open_id;
    } else if (scroll_to !== null) {
      return site + '#factlink-' + scroll_to;
    } else {
      return site;
    }
  }

  /**
   * Add base url and inject proxy.js, and return the proxied site
   */
  function injectFactlinkJs(original_html, site, scroll_to, open_id, successFn) {
    "use strict";

    if (/factlink_loader_publishers.min.js/.test(original_html)) {
      if (config.ENV === "development") {
        // Disable publisher's script in development mode
        original_html = original_html.replace(/factlink_loader_publishers.min.js/g, 'factlink_loader_publishers_DEACTIVATED.min.js');
      } else {
        // Redirect to publishers' sites
        // Circumvent blacklist as we assume we don't want to blacklist publishers for now, and it's faster
        // to not check.
        var redirect_url = publisherUrl(site, scroll_to, open_id);

        successFn('<script>window.parent.location = ' + JSON.stringify(redirect_url) + ';</script>');
        return;
      }
    }

    blacklist.if_allowed(site,function() {

      var output_html = original_html;

      var new_base_tag = '<base href="' + site + '" />';

      var framebuster_script = 'window.self = window.top;\n\n';

      // Inject config also at the top
      var FactlinkConfig = {
        api: config.API_URL,
        lib: config.LIB_URL,
        srcPath: config.ENV === "development" ? "/factlink.core.js" : "/factlink.core.min.js",
        url: site
      };
      var factlink_config_script = 'window.FactlinkConfig = ' + JSON.stringify(FactlinkConfig) + ';\n';
      var factlink_proxy_url_script = 'window.FactlinkProxyUrl = ' + JSON.stringify(config.PROXY_URL) + ';\n';

      var inline_setup_script_tag = '<script>' + framebuster_script +
        factlink_config_script + factlink_proxy_url_script + '</script>';

      var actions = [];

      open_id = parse_int_or_null(open_id) ;
      scroll_to = parse_int_or_null(scroll_to) || open_id;

      if (open_id !== null) {
        actions.push('FACTLINK.openFactlinkModal(' + open_id + ');');
      }

      if (scroll_to !== null) {
        actions.push('FACTLINK.scrollTo(' + scroll_to + ');');
      }

      actions.push('FACTLINK.startHighlighting();');
      actions.push('FACTLINK.startAnnotating();');

      // Inject Factlink library at the end of the file
      var loader_filename = (config.ENV === "development" ? "/factlink_loader_basic.js" : "/factlink_loader_basic.min.js");

      var loader_tag = '<script async defer src="' + config.LIB_URL + loader_filename + '" onload="'+actions.join('')+ '"></script>';
      var proxy_script_tag = '<script async defer src="' + config.PROXY_URL + '/static/scripts/proxy.js?1"></script>';

      var header_content = new_base_tag + inline_setup_script_tag + loader_tag + proxy_script_tag;

      output_html = inject_html_in_head(output_html, header_content);
      successFn(output_html);
    },function(){
      successFn(original_html);
    });
  }

  function handleProxyRequest(res, url, scrollto, open_id, form_hash) {
    if ( typeof url !== "string" || url.length === 0) {
      renderWelcomePage(res);
      return;
    } else {
      site = urlvalidation.clean_url(url);
      if (site === undefined) {
        console.error('Rendered "Something went wrong" page because of urlvalidation.clean_url on ' + url);
        renderErrorPage(res, url);
      } else {
        get(site).asString(function(err, str) {
          if(err) {
            console.error('Rendered "Something went wrong" page because could not download page on ' + url);
            renderErrorPage(res, url);
          } else {
            renderProxiedPage(res, site, scrollto, open_id, str);
          }
        });
      }
    }
  }

  function renderProxiedPage(res, site, scrollto, open_id, html_in) {
    injectFactlinkJs(html_in, site, scrollto, open_id, function(html) {
      res.writeHead(200, {'Content-Type': 'text/html'});
      res.write(html);
      res.end();
    });
  }

  function renderErrorPage(res, url){
    res.render('something_went_wrong', {
      layout: false,
      locals: {
        static_url: config.STATIC_URL,
        proxy_url: config.PROXY_URL,
        site: url
      }
    });
  }

  function renderWelcomePage(res){
    res.render('welcome.jade',{
      layout:false,
      locals: {
        proxy_url:      config.PROXY_URL,
        core_url:       config.API_URL,
        static_url:     config.STATIC_URL
      }
    });
  }

  /**
   *  Render a jade template
   */
  function render_page(pagename, page_url) {
    return function(req, res) {
      var header_url  = urlbuilder.create_url(config.PROXY_URL + "/header", req.query);
      var parse_url   = page_url || urlbuilder.create_url(config.PROXY_URL + "/parse", req.query);
      res.render(pagename, {
        layout: false,
        locals: {
          static_url: config.STATIC_URL,
          proxy_url: config.PROXY_URL,
          core_url: config.API_URL,
          page_url: req.query.url,
          clean_page_url: urlvalidation.clean_url(req.query.url),
          factlinkModus: req.query.factlinkModus,
          header_url: header_url,
          parse_url: parse_url
        }
      });
    };
  }

  /**
   *  Search on Factlink enabled Google
   */
  function get_search(req, res) {
    var query             = req.query.query;
    var search_redir_url  = urlbuilder.search_redir_url(config.PROXY_URL, query, req.query.factlinkModus);

    res.redirect(search_redir_url);
  }

  /**
   *  Inject Factlink in regular get requests
   */
  function get_parse(req, res) {
    var site     = req.query.url;
    var scrollto = req.query.scrollto;
    var open_id  = req.query.open_id;
    handleProxyRequest(res, site, scrollto, open_id, {});
  }

  /**
   * Forms get posted with a hidden 'factlinkFormUrl' field,
   * which is added by the proxy (in proxy.js). This is the 'action' URL which
   * the form normally submits its form to.
   */
  function get_submit(req, res) {
    var form_hash = req.query;
    var site      = form_hash.factlinkFormUrl;
    delete form_hash.factlinkFormUrl;

    handleProxyRequest(res, site, undefined, undefined, {
      'query': form_hash
    });
  }

  return server;
}
exports.getServer = getServer;
<|endoftext|>


<commit_before><commit_msg>Revert "Add fixtures for xforms-service"<commit_after><|endoftext|>


<commit_before>
/* global describe, it */

var assert = require("assert");
var fs = require("fs");

var index = require("../index");

describe('index.js', function() {
  describe('#processMessage()', function() {
    it('should process email data and make updates', function(done) {
      var data = {
        config: {},
        email: {
          source: "betsy@example.com"
        },
        emailData: fs.readFileSync("test/assets/message.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.processed.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });

    it('should preserve an existing Reply-To header in emails', function(done) {
      var data = {
        config: {},
        email: {
          source: "betsy@example.com"
        },
        emailData:
          fs.readFileSync("test/assets/message.replyto.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.processed.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });

    it('should preserve an existing Reply-to header', function(done) {
      var data = {
        config: {},
        email: {
          source: "betsy@example.com"
        },
        emailData:
          fs.readFileSync("test/assets/message.replyto_case.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.replyto_case.processed.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });

    it('should allow overriding the From header in emails', function(done) {
      var data = {
        config: {
          fromEmail: "noreply@example.com"
        },
        email: {
          source: "betsy@example.com"
        },
        emailData:
          fs.readFileSync("test/assets/message.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.fromemail.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });

    it('should allow adding a prefix to the Subject in emails', function(done) {
      var data = {
        config: {
          subjectPrefix: "[PREFIX] "
        },
        email: {
          source: "betsy@example.com"
        },
        emailData: fs.readFileSync("test/assets/message.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.subjectprefix.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });

    it('should allow overriding the To header in emails', function(done) {
      var data = {
        config: {
          toEmail: "actualTarget@example.com"
        },
        email: {
          source: "betsy@example.com"
        },
        emailData:
          fs.readFileSync("test/assets/message.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.toemail.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });
  });
});
<commit_msg>No spaces for you!<commit_after>
/* global describe, it */

var assert = require("assert");
var fs = require("fs");

var index = require("../index");

describe('index.js', function() {
  describe('#processMessage()', function() {
    it('should process email data and make updates', function(done) {
      var data = {
        config: {},
        email: {
          source: "betsy@example.com"
        },
        emailData: fs.readFileSync("test/assets/message.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.processed.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });

    it('should preserve an existing Reply-To header in emails', function(done) {
      var data = {
        config: {},
        email: {
          source: "betsy@example.com"
        },
        emailData:
          fs.readFileSync("test/assets/message.replyto.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.processed.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });

    it('should preserve an existing Reply-to header', function(done) {
      var data = {
        config: {},
        email: {
          source: "betsy@example.com"
        },
        emailData:
          fs.readFileSync("test/assets/message.replyto_case.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.replyto_case.processed.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });

    it('should allow overriding the From header in emails', function(done) {
      var data = {
        config: {
          fromEmail: "noreply@example.com"
        },
        email: {
          source: "betsy@example.com"
        },
        emailData:
          fs.readFileSync("test/assets/message.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.fromemail.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });

    it('should allow adding a prefix to the Subject in emails', function(done) {
      var data = {
        config: {
          subjectPrefix: "[PREFIX] "
        },
        email: {
          source: "betsy@example.com"
        },
        emailData: fs.readFileSync("test/assets/message.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.subjectprefix.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });

    it('should allow overriding the To header in emails', function(done) {
      var data = {
        config: {
          toEmail: "actualTarget@example.com"
        },
        email: {
          source: "betsy@example.com"
        },
        emailData:
          fs.readFileSync("test/assets/message.txt").toString(),
        log: console.log,
        recipients: ["jim@example.com"],
        originalRecipient: "info@example.com"
      };
      var emailDataProcessed = fs.readFileSync(
        "test/assets/message.toemail.txt").toString();
      index.processMessage(data)
        .then(function(data) {
          assert.equal(data.emailData,
            emailDataProcessed,
            "processEmail updated email data");
          done();
        });
    });
  });
});
<|endoftext|>


<commit_before>import React, { Component } from "react"
import { PropTypes } from "react"
import Helmet from "react-helmet"

import Header from "Header"
import Footer from "Footer"
import GoogleAnalyticsTracker from "GoogleAnalyticsTracker"

export default class Layout extends Component {

  static propTypes = {
    children: PropTypes.oneOfType([ PropTypes.array, PropTypes.object ]),
    params: PropTypes.object,
  }

  static contextTypes = {
    metadata: PropTypes.object.isRequired,
  }

  render() {
    const {
      pkg,
      i18n,
    } = this.context.metadata

    return (

        <GoogleAnalyticsTracker params={ this.props.params }>
          <Helmet
            meta={[
              { property: "og:site_name", content: pkg.name },
              { name: "twitter:site", content: `@${ i18n.twitterUsername }` },
            ]}
          />

          {this.props.children}

        </GoogleAnalyticsTracker>

    )
  }
}
<commit_msg>Fix og:site_name and add lang attribute on the content<commit_after>import React, { Component } from "react"
import { PropTypes } from "react"
import Helmet from "react-helmet"

import getLang from "i18n/getLang"
import getI18n from "i18n/get"
import Header from "Header"
import Footer from "Footer"
import GoogleAnalyticsTracker from "GoogleAnalyticsTracker"

export default class Layout extends Component {

  static propTypes = {
    children: PropTypes.oneOfType([ PropTypes.array, PropTypes.object ]),
    params: PropTypes.object,
  }

  static contextTypes = {
    metadata: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
  }

  render() {
    const i18n = getI18n(this.context)
    const locale = getLang(this.context)

    return (

        <GoogleAnalyticsTracker params={ this.props.params }>
          <Helmet
            meta={[
              { property: "og:site_name", content: i18n.title },
              { name: "twitter:site", content: `@${ i18n.twitterUsername }` },
            ]}
          />

          {this.props.children}

        </GoogleAnalyticsTracker>

    )
  }
}
<|endoftext|>


<commit_before>  /*******************************************************************************
*  Code contributed to the webinos project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2011 Habib Virji, Samsung Electronics (UK) Ltd
*******************************************************************************/
/**
* @author Habib Virji
* @description: Starts PZH farm and handles adding of new PZH
*/

var tls         = require("tls");
var path        = require("path");
var util        = require("util");
var fs          = require("fs");
var net         = require("net");
var webinos     = require("webinos")(__dirname);
var session     = webinos.global.require(webinos.global.pzp.location, "lib/session");
var log         = new session.common.debug("pzh_farm");
var pzh_session = require("./pzh_sessionHandling.js");

var farm = exports;

farm.pzhs = [];
farm.config = {};
farm.server;
farm.pzhWI  = webinos.global.require(webinos.global.pzh.location, "web/pzh_webserver");

function loadPzhs(config) {
  "use strict";
  var myKey;
  for ( myKey in config.pzhs) {
    if(typeof config.pzhs[myKey] !== "undefined") {
      farm.pzhs[myKey] = new pzh_session();
      farm.pzhs[myKey].addPzh(myKey, config.pzhs[myKey], function(res, instance) {
        if (res) {
          log.info("started pzh ... " + instance.config.name);
        } else {
          log.error("failed starting pzh ... ");
        }
      });
    }
  }
}

/**
* @description: Starts farm.
* @param {string} url: pzh farm url for e.g. pzh.webinos.org
* @param {function} callback: true in case successful or else false in case unsuccessful
*/
farm.startFarm = function (url, name, callback) {
  "use strict";
  var connectingAddress, hostname;
  // The directory structure which pzh_farms needs for putting in files
  session.configuration.createDirectoryStructure(function(){
    // Configuration setting for pzh, returns set values and connection key
    if (url === "") {
      session.common.fetchIP(function(resolvedAddress){
        connectingAddress = resolvedAddress;
        hostname = connectingAddress;
        farm.loadFarm(hostname, connectingAddress, callback);
      });
    } else {
      session.common.resolveIP(url, function(resolvedAddress) {
        connectingAddress = "0.0.0.0";
        hostname = url;
        farm.loadFarm(hostname, connectingAddress, callback);
      });
    }
  });
};

farm.loadFarm = function(hostname, connectingAddress, callback) {
  session.configuration.setConfiguration('',"PzhFarm", hostname, null, function (config, conn_key) {
    if (config === "undefined") {
      log.error("failed setting configuration, details are missing");
      return;
    }
    // Connection parameters for PZH pzh_farm TLS server.
    // Note this is the main server, pzh started are stored as SNIContext to this server
    farm.config = config;
    var options = {
      key  : conn_key,
      cert : farm.config.own.cert,
      ca   : farm.config.master.cert,
      requestCert       : true,
      rejectUnauthorised: false
    };

    //session.common.resolveIP(url, function(resolvedAddress) {
      // Main pzh_farm TLS server
      farm.server = tls.createServer (options, function (conn) {
        // if servername existes in conn and pzh_farm.pzhs has details about pzh instance, message will be routed to respective PZH authorization function
        if (conn.servername && farm.pzhs[conn.servername]) {
          farm.pzhs[conn.servername].handleConnectionAuthorization(conn);
        } else {
          log.error("server is not registered in "+conn.servername);
          conn.socket.end();
          return;
        }
        // In case data is received at pzh_farm
        conn.on("data", function(data){
          // forward message to respective PZH handleData function
          if(conn.servername && farm.pzhs[conn.servername]) {
            farm.pzhs[conn.servername].handleData(conn, data);
          } else {
            log.info("("+conn.servername+") is not registered in the farm");
          }
        });
          // In case of error
        conn.on("end", function(err) {
          log.info("("+conn.servername+") client ended connection");
        });

        // It calls removeClient to remove PZH from list.
        conn.on("close", function() {
          try {
            log.info("("+conn.servername+") Pzh/Pzp  closed");
            if(conn.servername && farm.pzhs[conn.servername]) {
              var cl = farm.pzhs[conn.servername];
              var removed = session.common.removeClient(cl, conn);
              if (removed !== null && typeof removed !== "undefined"){
                //farm.pzhWI.updateList(farm.pzhs[conn.servername]);
                cl.messageHandler.removeRoute(removed, conn.servername);
                cl.discovery.removeRemoteServiceObjects(removed);
              }
            }
          } catch (err) {
            log.error("("+conn.servername+") Remove client from connectedPzp/connectedPzh failed" + err);
          }
        });

        conn.on("error", function(err) {
          log.error("("+conn.servername+") General Error" + err);
        });
      });
      farm.server.on("error", function(error) {
        log.error(error);
      });

      farm.server.on("listening", function(){
        log.info("initialized at " + connectingAddress);
        // Load PZH"s that we already have registered ...
        loadPzhs(farm.config);
        // Start web interface, this webinterface will adapt depending on user who logins
        farm.pzhWI.start(hostname, connectingAddress, function (status) {
          if (status) {
            callback(true, farm.config);
          }
        });
      });
      farm.server.listen(session.configuration.farmPort, connectingAddress);
    });

};

/**
* @description: this function returns correct pzh id depending on user login details. If details are not present it adds information in config
* @param {string} url: pzh url
* @param {object} user: details fetched from openid about user
*/
farm.getOrCreatePzhInstance = function (host, user, callback) {
  "use strict";
  var name;

  if (typeof user.username === "undefined" || user.username === null ) {
    name = user.email;
  } else {
    name = user.username;
  }
  // Check for if user already existed and is stored
  var myKey = host+"/"+name;

  if ( farm.pzhs[myKey] && farm.pzhs[myKey].config.name === name ) {
    log.info("user already registered");
    callback(myKey, farm.pzhs[myKey]);
  } else if(farm.pzhs[myKey]) { // Cannot think of this case, but still might be useful
    log.info("user first time login");
    farm.pzhs[myKey].config.name     = name;
    farm.pzhs[myKey].config.email    = user.email;
    farm.pzhs[myKey].config.country  = user.country;
    farm.pzhs[myKey].config.image    = user.image;
    session.configuration.storeConfig(farm.pzhs[myKey].config, function() {
      callback(myKey, farm.pzhs[myKey]);
    });
  } else {
    callback(myKey, null);
  }
};

farm.createPzh = function(host, user, callback) {
  if (typeof host !== "undefined" && typeof user !== "undefined") {
    var name;
    if (typeof user.username === "undefined" || user.username === null ) {
      name = user.email;
    } else {
      name = user.username;
    }
    var pzhId = host+"/"+name;
    if (farm.pzhs[pzhId])  {
      log.info("pzh exists, with same id, cannot create add this instance");
      callback(pzhId, null);
    } else {
      log.info("adding new PZH - " + pzhId);
      var pzhModules = session.configuration.pzhDefaultServices;
      farm.pzhs[pzhId] = new pzh_session();
      farm.pzhs[pzhId].addPzh(pzhId, pzhModules, function(status){
        if (status) {
          farm.pzhs[pzhId].config.name     = name;
          farm.pzhs[pzhId].config.email    = user.email;
          farm.pzhs[pzhId].config.country  = user.country;
          farm.pzhs[pzhId].config.image    = user.image;
          farm.config.pzhs[pzhId]          = pzhModules;

          session.configuration.storeConfig(farm.config, function() {
            session.configuration.storeConfig(farm.pzhs[pzhId].config, function() {
              callback(pzhId, farm.pzhs[pzhId]);
            });
          });
        } else {
          callback(pzhId, null);
        }
      });
    }
  } else {
    callback(null, null);
  }
}

// This keeps pzh running but you cannot find where error occurred
process.on("uncaughtException", function(err) {
  log.error("uncaught exception " + err);
});

<commit_msg>Error message explicitly warning to use sudo when running PZH<commit_after>  /*******************************************************************************
*  Code contributed to the webinos project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2011 Habib Virji, Samsung Electronics (UK) Ltd
*******************************************************************************/
/**
* @author Habib Virji
* @description: Starts PZH farm and handles adding of new PZH
*/

var tls         = require("tls");
var path        = require("path");
var util        = require("util");
var fs          = require("fs");
var net         = require("net");
var webinos     = require("webinos")(__dirname);
var session     = webinos.global.require(webinos.global.pzp.location, "lib/session");
var log         = new session.common.debug("pzh_farm");
var pzh_session = require("./pzh_sessionHandling.js");

var farm = exports;

farm.pzhs = [];
farm.config = {};
farm.server;
farm.pzhWI  = webinos.global.require(webinos.global.pzh.location, "web/pzh_webserver");

function loadPzhs(config) {
  "use strict";
  var myKey;
  for ( myKey in config.pzhs) {
    if(typeof config.pzhs[myKey] !== "undefined") {
      farm.pzhs[myKey] = new pzh_session();
      farm.pzhs[myKey].addPzh(myKey, config.pzhs[myKey], function(res, instance) {
        if (res) {
          log.info("started pzh ... " + instance.config.name);
        } else {
          log.error("failed starting pzh ... ");
        }
      });
    }
  }
}

/**
* @description: Starts farm.
* @param {string} url: pzh farm url for e.g. pzh.webinos.org
* @param {function} callback: true in case successful or else false in case unsuccessful
*/
farm.startFarm = function (url, name, callback) {
  "use strict";
  var connectingAddress, hostname;
  // The directory structure which pzh_farms needs for putting in files
  session.configuration.createDirectoryStructure(function(){
    // Configuration setting for pzh, returns set values and connection key
    if (url === "") {
      session.common.fetchIP(function(resolvedAddress){
        connectingAddress = resolvedAddress;
        hostname = connectingAddress;
        farm.loadFarm(hostname, connectingAddress, callback);
      });
    } else {
      session.common.resolveIP(url, function(resolvedAddress) {
        connectingAddress = "0.0.0.0";
        hostname = url;
        farm.loadFarm(hostname, connectingAddress, callback);
      });
    }
  });
};

farm.loadFarm = function(hostname, connectingAddress, callback) {
  session.configuration.setConfiguration('',"PzhFarm", hostname, null, function (config, conn_key) {
    if (config === "undefined") {
      log.error("failed setting configuration, details are missing");
      return;
    }
    // Connection parameters for PZH pzh_farm TLS server.
    // Note this is the main server, pzh started are stored as SNIContext to this server
    farm.config = config;
    var options = {
      key  : conn_key,
      cert : farm.config.own.cert,
      ca   : farm.config.master.cert,
      requestCert       : true,
      rejectUnauthorised: false
    };

    //session.common.resolveIP(url, function(resolvedAddress) {
      // Main pzh_farm TLS server
      farm.server = tls.createServer (options, function (conn) {
        // if servername existes in conn and pzh_farm.pzhs has details about pzh instance, message will be routed to respective PZH authorization function
        if (conn.servername && farm.pzhs[conn.servername]) {
          farm.pzhs[conn.servername].handleConnectionAuthorization(conn);
        } else {
          log.error("server is not registered in "+conn.servername);
          conn.socket.end();
          return;
        }
        // In case data is received at pzh_farm
        conn.on("data", function(data){
          // forward message to respective PZH handleData function
          if(conn.servername && farm.pzhs[conn.servername]) {
            farm.pzhs[conn.servername].handleData(conn, data);
          } else {
            log.info("("+conn.servername+") is not registered in the farm");
          }
        });
          // In case of error
        conn.on("end", function(err) {
          log.info("("+conn.servername+") client ended connection");
        });

        // It calls removeClient to remove PZH from list.
        conn.on("close", function() {
          try {
            log.info("("+conn.servername+") Pzh/Pzp  closed");
            if(conn.servername && farm.pzhs[conn.servername]) {
              var cl = farm.pzhs[conn.servername];
              var removed = session.common.removeClient(cl, conn);
              if (removed !== null && typeof removed !== "undefined"){
                //farm.pzhWI.updateList(farm.pzhs[conn.servername]);
                cl.messageHandler.removeRoute(removed, conn.servername);
                cl.discovery.removeRemoteServiceObjects(removed);
              }
            }
          } catch (err) {
            log.error("("+conn.servername+") Remove client from connectedPzp/connectedPzh failed" + err);
          }
        });

        conn.on("error", function(err) {
          log.error("("+conn.servername+") General Error" + err);
        });
      });
      farm.server.on("error", function(error) {
        if(err && err.code ==="EACCES") {
          log.error("starting farm failed... try with sudo ");
          process.exit();
        }
        log.error(error);
      });

      farm.server.on("listening", function(){
        log.info("initialized at " + connectingAddress);
        // Load PZH"s that we already have registered ...
        loadPzhs(farm.config);
        // Start web interface, this webinterface will adapt depending on user who logins
        farm.pzhWI.start(hostname, connectingAddress, function (status) {
          if (status) {
            callback(true, farm.config);
          }
        });
      });
      farm.server.listen(session.configuration.farmPort, connectingAddress);
    });

};

/**
* @description: this function returns correct pzh id depending on user login details. If details are not present it adds information in config
* @param {string} url: pzh url
* @param {object} user: details fetched from openid about user
*/
farm.getOrCreatePzhInstance = function (host, user, callback) {
  "use strict";
  var name;

  if (typeof user.username === "undefined" || user.username === null ) {
    name = user.email;
  } else {
    name = user.username;
  }
  // Check for if user already existed and is stored
  var myKey = host+"/"+name;

  if ( farm.pzhs[myKey] && farm.pzhs[myKey].config.name === name ) {
    log.info("user already registered");
    callback(myKey, farm.pzhs[myKey]);
  } else if(farm.pzhs[myKey]) { // Cannot think of this case, but still might be useful
    log.info("user first time login");
    farm.pzhs[myKey].config.name     = name;
    farm.pzhs[myKey].config.email    = user.email;
    farm.pzhs[myKey].config.country  = user.country;
    farm.pzhs[myKey].config.image    = user.image;
    session.configuration.storeConfig(farm.pzhs[myKey].config, function() {
      callback(myKey, farm.pzhs[myKey]);
    });
  } else {
    callback(myKey, null);
  }
};

farm.createPzh = function(host, user, callback) {
  if (typeof host !== "undefined" && typeof user !== "undefined") {
    var name;
    if (typeof user.username === "undefined" || user.username === null ) {
      name = user.email;
    } else {
      name = user.username;
    }
    var pzhId = host+"/"+name;
    if (farm.pzhs[pzhId])  {
      log.info("pzh exists, with same id, cannot create add this instance");
      callback(pzhId, null);
    } else {
      log.info("adding new PZH - " + pzhId);
      var pzhModules = session.configuration.pzhDefaultServices;
      farm.pzhs[pzhId] = new pzh_session();
      farm.pzhs[pzhId].addPzh(pzhId, pzhModules, function(status){
        if (status) {
          farm.pzhs[pzhId].config.name     = name;
          farm.pzhs[pzhId].config.email    = user.email;
          farm.pzhs[pzhId].config.country  = user.country;
          farm.pzhs[pzhId].config.image    = user.image;
          farm.config.pzhs[pzhId]          = pzhModules;

          session.configuration.storeConfig(farm.config, function() {
            session.configuration.storeConfig(farm.pzhs[pzhId].config, function() {
              callback(pzhId, farm.pzhs[pzhId]);
            });
          });
        } else {
          callback(pzhId, null);
        }
      });
    }
  } else {
    callback(null, null);
  }
}

// This keeps pzh running but you cannot find where error occurred
process.on("uncaughtException", function(err) {
  log.error("uncaught exception " + err);
});

<|endoftext|>


<commit_before>angular.module('ProtractorMeetupApp').factory('mocks', function() {
  return {
    album: {
      getById: function() {
        return {
          name: 'Licensed to Ill',
          year: 1986
        };
      },
      getList: function() {
        return [
          {name: 'Check your head'},
          {name: 'Pauls boutique'}
        ];
      }
    },
    band: {
      getById: function() {
        return {
          name: 'Beastie boys',
          albums: [],
          members: []
        };
      },
      getList: function() {
        return [
          {name: 'Wu-Tang clan'},
          {name: 'Beastie boys'}
        ];
      }
    },
    member: {
      getById: function() {
        return {name: 'Method Man'};
      },
      getList: function() {
        return [
          {name: 'Michael "Mike D" Diamond'},
          {name: 'Adam "Ad-Rock" Horovitz'},
          {name: 'Adam "MCA" Yauch'}
        ];
      }
    }
  };
});
<commit_msg>return id in get by id<commit_after>angular.module('ProtractorMeetupApp').factory('mocks', function() {
  return {
    album: {
      getById: function() {
        return {
          name: 'Licensed to Ill',
          year: 1986
        };
      },
      getList: function() {
        return [
          {name: 'Check your head'},
          {name: 'Pauls boutique'}
        ];
      }
    },
    band: {
      getById: function() {
        return {
          name: 'Beastie boys',
          albums: [],
          members: []
        };
      },
      getList: function() {
        return [
          {name: 'Wu-Tang clan'},
          {name: 'Beastie boys'}
        ];
      }
    },
    member: {
      getById: function() {
        return {_id: '123', name: 'Method Man'};
      },
      getList: function() {
        return [
          {name: 'Michael "Mike D" Diamond'},
          {name: 'Adam "Ad-Rock" Horovitz'},
          {name: 'Adam "MCA" Yauch'}
        ];
      }
    }
  };
});
<|endoftext|>


<commit_before>/*global define:true*/
/*global describe:true */
/*global it:true */
/*global expect:true */
/*global beforeEach:true */
/*global sinon:true */
/* jshint strict: false */
define(['gpub'], function(Gpub) {

    describe('just checking', function() {

        it('Gpub should be loaded', function() {
            expect(Gpub).toBeTruthy();
            var gpub = new Gpub();
            expect(gpub).toBeTruthy();
        });
    });
    var item;
    beforeEach(function(){
        item = new Gpub();
    });

    describe('gpub', function(){
        it('We can extend simple objects with pubsub cap.',function(){
            expect(item).toBeTruthy();
            expect(item).toHaveMethods('publish','unsubscribe','subscribe');
        });

        it('objects can subscribe to a pubsub instance',function(){
            var subscriber = function(){};
            item.subscribe('topic',subscriber);
            expect(item.subscribers('topic')).toBeTruthy();
            expect(item.subscribers('non-topic')).toBeFalsy();
        });

        it('objects can unsubscribe from a pubsub instance',function(){
            var subscriber = function subscriber(){};
            item.subscribe('topic',subscriber);
            item.subscribe('topic-two',subscriber);
            expect(item.subscribers('topic')).toBeTruthy();
            item.unsubscribe('topic',subscriber);
            expect(item.subscribers('topic')).toBeFalsy();
        });

        it('objects get notified of publisehd topics',function(){
            var spy = sinon.spy();
            item.subscribe('topic',spy);
            item.publish('topic');
            expect(spy).toHaveBeenCalled();
        });

        it('objects get notified of publisehd topics',function(){
            var spy = sinon.spy();
            var options = {options:true};
            item.subscribe('topic',spy);
            item.publish('topic',options);
            expect(spy).toHaveBeenCalledWith(options);
            expect(spy).not.toHaveBeenCalledWith({});
        });

        it('a handler can abort the publish loop',function(){
            var test = {};
            test.handler = function(){return false;};

            var spyB = sinon.spy();
            var spyC = sinon.spy();
            var spyA = sinon.spy(test, 'handler');

            //here it works cose we have the same order.
            item.subscribe('topic',spyA);
            item.subscribe('topic',spyB);
            item.subscribe('topic',spyC);

            item.publish('topic');
            expect(spyA).toHaveBeenCalled();
            expect(spyB).not.toHaveBeenCalled();
            expect(spyC).not.toHaveBeenCalled();
        });

        it('should route all notices to a single handler if subscribed to the * channel',function(){
            var single   = sinon.spy();
            var multiple = sinon.spy();

            var options = {options:true};
            item.subscribe('all',multiple);
            item.subscribe('topic',single);

            item.publish('topic',options);
            item.publish('topic2',options);
            item.publish('topic3',options);

            expect(single).toHaveBeenCalledOnce();
            // expect(single).toHaveBeenCalledWith(options);

            var callbackArguments = multiple.args[0];
            expect(multiple).toHaveBeenCalledThrice();
            expect(callbackArguments[0]).toBe('topic');
            expect(callbackArguments[1]).toHaveProperties('event','options');
        });

        it('should have a fluid interface',function(){
            var single   = sinon.spy();
            var multiple = sinon.spy();
            var opt = {options:true};
            item.subscribe('all',multiple).subscribe('t2',single);

            item.publish('t1',opt).publish('t2',opt).publish('t3',opt);

            expect(single).toHaveBeenCalledOnce();
            expect(single).toHaveBeenCalledWith(opt);

            expect(multiple).toHaveBeenCalledThrice();
            expect(multiple.args[0]).toIncludeObject(['t1',opt]);
        });

        it('should execute in the context of the publisher',function(){
            var test = {};
            test.handler = function(){return this;};
            var spy = sinon.spy(test,'handler');
            item.subscribe('topic',test.handler);

            item.publish('topic');

            expect(spy.returned(item)).toBeTruthy();
        });

        it('shoud include all event props into the options parameter',function(){

            //SHOULD WE MAKE THIS A FEATURE OR A BUG?!
            //for now, we leave it as it is.
            var handler = {};
            handler.onTopic = function(options){
                // console.log('*****************');
                // console.log(arguments);
                // console.log('*****************');

                handler.id = this.id;
                options.age++;
                expect(options.age).toBe(2);
                return this;
            };
            handler.onTopicTwo = function(options){
                options.age++;
                expect(options.age).toBe(3);
                // console.log('*****************');
                // console.log(arguments);
                // console.log('*****************');
            };

            var spy = sinon.spy(handler, 'onTopic');

            item.id = 23;
            item.subscribe('topic', handler.onTopic);
            item.subscribe('topic',handler.onTopicTwo);

            item.publish('topic', {age:1});

            expect(spy).toHaveBeenCalledOnce();
            expect(spy.returned(item)).toBeTruthy();
            expect(spy.args[0]).toBeTruthy();
            expect(spy.args[0]).toHaveLength(2);//we have age + event.

            expect(handler.id).toBeTruthy();
            expect(handler.id).toEqual(item.id);
            expect(spy.args[0][0]).toHaveProperties('target','event');
        });
//////////////////////////////////////////////////////
/// NEW API
//////////////////////////////////////////////////////
        it('should execute in the context of the publisher',function(){
            var test = {};
            test.handler = function(){return this;};
            var spy = sinon.spy(test,'handler');
            item.on('topic',test.handler);

            item.emit('topic');

            expect(spy.returned(item)).toBeTruthy();
        });

        it('should have a fluid interface',function(){
            var single   = sinon.spy();
            var multiple = sinon.spy();
            var opt = {options:true};
            item.on('all',multiple).on('t2',single);

            item.emit('t1',opt).emit('t2',opt).emit('t3',opt);

            expect(single).toHaveBeenCalledOnce();
            expect(single).toHaveBeenCalledWith(opt);

            expect(multiple).toHaveBeenCalledThrice();
            expect(multiple.args[0]).toIncludeObject(['t1',opt]);
        });

        it('should route all notices to a single handler if subscribed to the * channel',function(){
            var single   = sinon.spy();
            var multiple = sinon.spy();

            var options = {options:true};
            item.on('all', multiple);
            item.on('topic', single);

            item.emit('topic',  options);
            item.emit('topic2', options);
            item.emit('topic3', options);

            expect(single).toHaveBeenCalledOnce();
            // expect(single).toHaveBeenCalledWith(options);

            var callbackArguments = multiple.args[0];
            expect(multiple).toHaveBeenCalledThrice();
            expect(callbackArguments[0]).toBe('topic');
            expect(callbackArguments[1]).toHaveProperties('event','options');
        });

        it('topics for handlers registered with the once method should only be triggered once', function(){
            var single = sinon.spy(),
                multiple = sinon.spy();

            item.on('update', multiple).once('update', single);

            item.emit('update').emit('update').emit('update');

            expect(single).toHaveBeenCalledOnce();
            expect(multiple).toHaveBeenCalledThrice();
        });

        it('once handlers should retain the dispatchers scope', function(){
            var single = sinon.spy(),
                multiple = sinon.spy();

            item.on('update', multiple).once('update', single);

            item.emit('update').emit('update').emit('update');

            expect(single.calledOn(item)).toBeTruthy();
        });

        it('once handlers should retain the dispatchers scope provided as an argument', function(){
            var single = sinon.spy(),
                multiple = sinon.spy(),
                Model = function(){},
                source = new Model();

            item.on('update', multiple).once('update', single, source);

            item.emit('update').emit('update').emit('update');

            expect(single.calledOn(source)).toBeTruthy();
        });
    });
});<commit_msg>Tests: Added debounce test, TODO more.<commit_after>/*global define:true*/
/*global describe:true */
/*global it:true */
/*global expect:true */
/*global beforeEach:true */
/*global sinon:true */
/* jshint strict: false */
define(['gpub'], function(Gpub) {

    describe('just checking', function() {

        it('Gpub should be loaded', function() {
            expect(Gpub).toBeTruthy();
            var gpub = new Gpub();
            expect(gpub).toBeTruthy();
        });
    });
    var item;
    beforeEach(function(){
        item = new Gpub();
    });

    describe('gpub', function(){
        it('We can extend simple objects with pubsub cap.',function(){
            expect(item).toBeTruthy();
            expect(item).toHaveMethods('publish','unsubscribe','subscribe');
        });

        it('objects can subscribe to a pubsub instance',function(){
            var subscriber = function(){};
            item.subscribe('topic',subscriber);
            expect(item.subscribers('topic')).toBeTruthy();
            expect(item.subscribers('non-topic')).toBeFalsy();
        });

        it('objects can unsubscribe from a pubsub instance',function(){
            var subscriber = function subscriber(){};
            item.subscribe('topic',subscriber);
            item.subscribe('topic-two',subscriber);
            expect(item.subscribers('topic')).toBeTruthy();
            item.unsubscribe('topic',subscriber);
            expect(item.subscribers('topic')).toBeFalsy();
        });

        it('objects get notified of published topics',function(){
            var spy = sinon.spy();
            item.subscribe('topic',spy);
            item.publish('topic');
            expect(spy).toHaveBeenCalled();
        });

        it('objects get notified of published topics',function(){
            var spy = sinon.spy();
            var options = {options:true};
            item.subscribe('topic',spy);
            item.publish('topic',options);
            expect(spy).toHaveBeenCalledWith(options);
            expect(spy).not.toHaveBeenCalledWith({});
        });

        it('a handler can abort the publish loop',function(){
            var test = {};
            test.handler = function(){return false;};

            var spyB = sinon.spy();
            var spyC = sinon.spy();
            var spyA = sinon.spy(test, 'handler');

            //here it works cose we have the same order.
            item.subscribe('topic',spyA);
            item.subscribe('topic',spyB);
            item.subscribe('topic',spyC);

            item.publish('topic');
            expect(spyA).toHaveBeenCalled();
            expect(spyB).not.toHaveBeenCalled();
            expect(spyC).not.toHaveBeenCalled();
        });

        it('should route all notices to a single handler if subscribed to the * channel',function(){
            var single   = sinon.spy();
            var multiple = sinon.spy();

            var options = {options:true};
            item.subscribe('all',multiple);
            item.subscribe('topic',single);

            item.publish('topic',options);
            item.publish('topic2',options);
            item.publish('topic3',options);

            expect(single).toHaveBeenCalledOnce();
            // expect(single).toHaveBeenCalledWith(options);

            var callbackArguments = multiple.args[0];
            expect(multiple).toHaveBeenCalledThrice();
            expect(callbackArguments[0]).toBe('topic');
            expect(callbackArguments[1]).toHaveProperties('event','options');
        });

        it('should have a fluid interface',function(){
            var single   = sinon.spy();
            var multiple = sinon.spy();
            var opt = {options:true};
            item.subscribe('all',multiple).subscribe('t2',single);

            item.publish('t1',opt).publish('t2',opt).publish('t3',opt);

            expect(single).toHaveBeenCalledOnce();
            expect(single).toHaveBeenCalledWith(opt);

            expect(multiple).toHaveBeenCalledThrice();
            expect(multiple.args[0]).toIncludeObject(['t1',opt]);
        });

        it('should execute in the context of the publisher',function(){
            var test = {};
            test.handler = function(){return this;};
            var spy = sinon.spy(test,'handler');
            item.subscribe('topic',test.handler);

            item.publish('topic');

            expect(spy.returned(item)).toBeTruthy();
        });

        it('should include all event props into the options parameter',function(){

            //SHOULD WE MAKE THIS A FEATURE OR A BUG?!
            //for now, we leave it as it is.
            var handler = {};
            handler.onTopic = function(options){
                // console.log('*****************');
                // console.log(arguments);
                // console.log('*****************');

                handler.id = this.id;
                options.age++;
                expect(options.age).toBe(2);
                return this;
            };
            handler.onTopicTwo = function(options){
                options.age++;
                expect(options.age).toBe(3);
                // console.log('*****************');
                // console.log(arguments);
                // console.log('*****************');
            };

            var spy = sinon.spy(handler, 'onTopic');

            item.id = 23;
            item.subscribe('topic', handler.onTopic);
            item.subscribe('topic',handler.onTopicTwo);

            item.publish('topic', {age:1});

            expect(spy).toHaveBeenCalledOnce();
            expect(spy.returned(item)).toBeTruthy();
            expect(spy.args[0]).toBeTruthy();
            expect(spy.args[0]).toHaveLength(2);//we have age + event.

            expect(handler.id).toBeTruthy();
            expect(handler.id).toEqual(item.id);
            expect(spy.args[0][0]).toHaveProperties('target','event');
        });
//////////////////////////////////////////////////////
/// NEW API
//////////////////////////////////////////////////////
        it('should execute in the context of the publisher',function(){
            var test = {};
            test.handler = function(){return this;};
            var spy = sinon.spy(test,'handler');
            item.on('topic',test.handler);

            item.emit('topic');

            expect(spy.returned(item)).toBeTruthy();
        });

        it('should have a fluid interface',function(){
            var single   = sinon.spy();
            var multiple = sinon.spy();
            var opt = {options:true};
            item.on('all',multiple).on('t2',single);

            item.emit('t1',opt).emit('t2',opt).emit('t3',opt);

            expect(single).toHaveBeenCalledOnce();
            expect(single).toHaveBeenCalledWith(opt);

            expect(multiple).toHaveBeenCalledThrice();
            expect(multiple.args[0]).toIncludeObject(['t1',opt]);
        });

        it('should route all notices to a single handler if subscribed to the * channel',function(){
            var single   = sinon.spy();
            var multiple = sinon.spy();

            var options = {options:true};
            item.on('all', multiple);
            item.on('topic', single);

            item.emit('topic',  options);
            item.emit('topic2', options);
            item.emit('topic3', options);

            expect(single).toHaveBeenCalledOnce();
            // expect(single).toHaveBeenCalledWith(options);

            var callbackArguments = multiple.args[0];
            expect(multiple).toHaveBeenCalledThrice();
            expect(callbackArguments[0]).toBe('topic');
            expect(callbackArguments[1]).toHaveProperties('event','options');
        });

        it('topics for handlers registered with the once method should only be triggered once', function(){
            var single = sinon.spy(),
                multiple = sinon.spy();

            item.on('update', multiple).once('update', single);

            item.emit('update').emit('update').emit('update');

            expect(single).toHaveBeenCalledOnce();
            expect(multiple).toHaveBeenCalledThrice();
        });

        it('once handlers should retain the dispatchers scope', function(){
            var single = sinon.spy(),
                multiple = sinon.spy();

            item.on('update', multiple).once('update', single);

            item.emit('update').emit('update').emit('update');

            expect(single.calledOn(item)).toBeTruthy();
        });

        it('once handlers should retain the dispatchers scope provided as an argument', function(){
            var single = sinon.spy(),
                multiple = sinon.spy(),
                Model = function(){},
                source = new Model();

            item.on('update', multiple).once('update', single, source);

            item.emit('update').emit('update').emit('update');

            expect(single.calledOn(source)).toBeTruthy();
        });

        it('should debounce multiple events triggered in rapid succession', function(){
            var single = sinon.spy(),
                multiple = sinon.spy();

            item.on('update', multiple).debounce('update', single);

            item.emit('update').emit('update').emit('update');

            expect(single).toHaveBeenCalledOnce();
            expect(multiple).toHaveBeenCalledThrice();
        });
    });
});<|endoftext|>


<commit_before>function Admin() {
	//hold the tab object
	this.tab = null;

	//hold signals for the page
	this._signals = new Array();

	//hold status message for the page
	this._prompt = null;
}

/* *****	Initialization code	***** */
Admin.prototype.init = function() {
	if(!user.can_admin()) {
		status_msg('You have not been granted IDE Admin privileges', LEVEL_WARN);
		return;
	}
	if(!this._inited) {
		logDebug("Admin: Initializing");

		/* Initialize a new tab for Admin - Do this only once */
		this.tab = new Tab( "Administration" );
		this._signals.push(connect( this.tab, "onfocus", bind( this._onfocus, this ) ));
		this._signals.push(connect( this.tab, "onblur", bind( this._onblur, this ) ));
		this._signals.push(connect( this.tab, "onclickclose", bind( this._close, this ) ));
		tabbar.add_tab( this.tab );

		/* Initialise indiviual page elements */
		this.ShowTeams();
		this.GetBlogFeeds();

		/* remember that we are initialised */
		this._inited = true;
	}

	/* now switch to it */
	tabbar.switch_to(this.tab);
}
/* *****	End Initialization Code 	***** */

/* *****	Tab events: onfocus, onblur and close	***** */
Admin.prototype._onfocus = function() {
	setStyle($("admin-page"), {'display':'block'});
}

Admin.prototype._onblur = function() {
	/* Clear any prompts */
	if( this._prompt != null ) {
		this._prompt.close();
		this._prompt = null;
	}
	/* hide Admin page */
	setStyle($("admin-page"), {'display':'none'});
}

Admin.prototype._close = function() {
	/* Disconnect all signals */
	for(var i = 0; i < this._signals.length; i++) {
		disconnect(this._signals[i]);
	}
	this._signals = new Array();

	/* Close tab */
	this._onblur();
	this.tab.close();
	this._inited = false;
}
/* *****	End Tab events	***** */

/* *****	Team editing Code	***** */
Admin.prototype.ShowTeams = function() {
	var i = 0;
	var td_id = TH(null, 'Team ID');
	var td_name = TH({'class':'name'}, 'Team Name');
	var td_button = TH(null);
	var oddeven = i++ % 2 == 0 ? 'even' : 'odd';
	replaceChildNodes('admin-teams-table');
	appendChildNodes('admin-teams-table', TR({'class':oddeven}, td_id, td_name, td_button));

	for( var id in user.team_names ) {
		td_id = TD(null, id);
		td_name = TD({'class':'name'}, user.team_names[id]);
		var button = BUTTON(null, 'Edit');
		this._signals.push(connect(button, 'onclick', bind(this._editTeam, this, {id:id,name:user.team_names[id]})));
		td_button = TD(null, button);
		oddeven = i++ % 2 == 0 ? 'even' : 'odd';
		appendChildNodes('admin-teams-table',
			TR({'class':oddeven,id:'admin-teams-table-'+id}, td_id, td_name, td_button)
		);
	}
}

Admin.prototype._editTeam = function(ref) {
	var row = $('admin-teams-table-'+ref.id);
	var cell = getFirstElementByTagAndClassName('td', 'name', row);
	var button = getFirstElementByTagAndClassName('button', null, row);
	if(button.innerHTML == 'Edit') {
		var input = INPUT({value:cell.innerHTML});
		replaceChildNodes(cell, input);
		button.innerHTML = 'Save';
	} else {	//save it
		IDE_backend_request("admin/team-name-put",
			{
			   'id': ref.id,
			 'name': cell.firstChild.value
			},
			bind( this._receiveEditTeam, this, ref ),
			bind( this._errorEditTeam, this, ref )
		);
		cell.firstChild.disabled = true;
		button.disabled = true;
	}
}
Admin.prototype._receiveEditTeam = function(ref, nodes) {
	var row = $('admin-teams-table-'+ref.id);
	if(nodes.success) {
		this._prompt = status_msg("Team name updated", LEVEL_OK);
		var cell = getFirstElementByTagAndClassName('td', 'name', row);
		var button = getFirstElementByTagAndClassName('button', null, row);
		cell.innerHTML = nodes.name;
		button.innerHTML = 'Edit';
		button.disabled = false;
		user.team_names[nodes.id] = nodes.name;
		if(nodes.id == team) {
			$('teamname').innerHTML = nodes.name;
		}
	} else {
		this._errorEditTeam(ref, nodes);
	}
}
Admin.prototype._errorEditTeam = function(ref, nodes) {
	this._prompt = status_msg("Failed to update team name", LEVEL_ERROR);
	var row = $('admin-teams-table-'+ref.id);
	var cell = getFirstElementByTagAndClassName('td', 'name', row);
	var button = getFirstElementByTagAndClassName('button', null, row);
	cell.firstChild.disabled = false;
	button.disabled = false;
}
/* *****	End Team editing Code 	***** */

/* *****	Student blog feed listing code	***** */
Admin.prototype._receiveGetBlogFeeds = function(nodes) {
	var td_user = TH({'class':'user'}, 'User ID');
	var td_url = TH({'class':'url'}, 'URL');
	var td_status = TH({'class':'status'}, 'Status');
	replaceChildNodes('admin-feeds-table');
	appendChildNodes('admin-feeds-table', TR({'class':'even'}, td_user, td_url, td_status));

	var make_selectbox = function(properties, options, def) {
		var s = SELECT(properties);
		for( value in options ) {
			var opt = OPTION({value:value}, options[value]);
			if(value == def)
				opt.selected = true;
			appendChildNodes(s, opt);
		}
		return s;
	};

	var options = {unchecked:'Unchecked',valid:'Valid',invalid:'Invalid'};
	//iterate over all feeds and append them to the table
	for( var i=0; i<nodes.feeds.length; i++ ) {
		// Add an id to each field to ensure that they're unique.
		// These used to be the DB ids, but there's no DB any more...
		nodes.feeds[i].id = i;
		var feed = nodes.feeds[i];
		td_user = TD({'class':'user'}, feed.user);
		td_url = TD({'class':'url'}, A({href:feed.url},feed.url));

		var status = (feed.checked ? (feed.valid ? 'valid' : 'invalid') : 'unchecked');
		var selectbox = make_selectbox({id:'admin-feeds-'+feed.id}, options, status);
		var ref = {id:feed.id, url:feed.url, status:status, selectbox:selectbox};
		this._signals.push(connect(selectbox, 'onchange', bind(this.setBlogStatus, this, ref)));
		td_status = TD({'class':'status'}, selectbox);

		var oddeven = i % 2 == 0 ? 'odd' : 'even';
		appendChildNodes('admin-feeds-table', TR({'class':oddeven}, td_user, td_url, td_status));
		this.showBlogStatus(ref);
	}
}
Admin.prototype._errorGetBlogFeeds = function() {
		this._prompt = status_msg("Unable to load blog feeds", LEVEL_ERROR);
		log("Admin: Failed to retrieve feed blog urls");
		return;
}
Admin.prototype.GetBlogFeeds = function() {
	log("Admin: Retrieving blog feeds");
	IDE_backend_request("admin/feed-status-get", {},
		bind(this._receiveGetBlogFeeds, this),
		bind(this._errorGetBlogFeeds, this)
	);
}
/* *****    End Student blog feed listing code	***** */

/* *****	RSS feed validation code	***** */
Admin.prototype._receiveBlogStatus = function(ref, nodes) {
	if(nodes.success > 0 ) {
		this._prompt = status_msg("Blog feed updated", LEVEL_OK);
	} else {
		this._errorBlogStatus(ref, nodes);
	}
}
Admin.prototype._errorBlogStatus = function(ref, nodes) {
	this._prompt = status_msg("Unable to update blog feed", LEVEL_ERROR);
	$('admin-feeds-'+ref.id).value = ref.status;
	this.showBlogStatus(ref);
}
Admin.prototype.setBlogStatus = function(ref) {
	log("Admin: Setting blog feed status");
	var status = $('admin-feeds-'+ref.id).value;
	var d = loadJSONDoc("./admin/setfeedstatus", {
			id:ref.id,
			url:ref.url,
			status:status
		});
	d.addCallback( bind( this._receiveBlogStatus, this, ref) );
	d.addErrback( bind( this._errorBlogStatus, this, ref) );

	ref.status = status;
	this.showBlogStatus(ref);
}
Admin.prototype.showBlogStatus = function(ref) {
	log("Admin: Showing blog feed status");
	var tr = getFirstParentByTagAndClassName('admin-feeds-'+ref.id, 'tr', null);
	removeElementClass(tr, 'unchecked-feed-url');
	removeElementClass(tr, 'valid-feed-url');
	removeElementClass(tr, 'invalid-feed-url');
	addElementClass(tr, ref.status+'-feed-url');
}
/* *****	End RSS feed validation code	***** */
<commit_msg>Hook up the admin page blog status setting to the new backend<commit_after>function Admin() {
	//hold the tab object
	this.tab = null;

	//hold signals for the page
	this._signals = new Array();

	//hold status message for the page
	this._prompt = null;
}

/* *****	Initialization code	***** */
Admin.prototype.init = function() {
	if(!user.can_admin()) {
		status_msg('You have not been granted IDE Admin privileges', LEVEL_WARN);
		return;
	}
	if(!this._inited) {
		logDebug("Admin: Initializing");

		/* Initialize a new tab for Admin - Do this only once */
		this.tab = new Tab( "Administration" );
		this._signals.push(connect( this.tab, "onfocus", bind( this._onfocus, this ) ));
		this._signals.push(connect( this.tab, "onblur", bind( this._onblur, this ) ));
		this._signals.push(connect( this.tab, "onclickclose", bind( this._close, this ) ));
		tabbar.add_tab( this.tab );

		/* Initialise indiviual page elements */
		this.ShowTeams();
		this.GetBlogFeeds();

		/* remember that we are initialised */
		this._inited = true;
	}

	/* now switch to it */
	tabbar.switch_to(this.tab);
}
/* *****	End Initialization Code 	***** */

/* *****	Tab events: onfocus, onblur and close	***** */
Admin.prototype._onfocus = function() {
	setStyle($("admin-page"), {'display':'block'});
}

Admin.prototype._onblur = function() {
	/* Clear any prompts */
	if( this._prompt != null ) {
		this._prompt.close();
		this._prompt = null;
	}
	/* hide Admin page */
	setStyle($("admin-page"), {'display':'none'});
}

Admin.prototype._close = function() {
	/* Disconnect all signals */
	for(var i = 0; i < this._signals.length; i++) {
		disconnect(this._signals[i]);
	}
	this._signals = new Array();

	/* Close tab */
	this._onblur();
	this.tab.close();
	this._inited = false;
}
/* *****	End Tab events	***** */

/* *****	Team editing Code	***** */
Admin.prototype.ShowTeams = function() {
	var i = 0;
	var td_id = TH(null, 'Team ID');
	var td_name = TH({'class':'name'}, 'Team Name');
	var td_button = TH(null);
	var oddeven = i++ % 2 == 0 ? 'even' : 'odd';
	replaceChildNodes('admin-teams-table');
	appendChildNodes('admin-teams-table', TR({'class':oddeven}, td_id, td_name, td_button));

	for( var id in user.team_names ) {
		td_id = TD(null, id);
		td_name = TD({'class':'name'}, user.team_names[id]);
		var button = BUTTON(null, 'Edit');
		this._signals.push(connect(button, 'onclick', bind(this._editTeam, this, {id:id,name:user.team_names[id]})));
		td_button = TD(null, button);
		oddeven = i++ % 2 == 0 ? 'even' : 'odd';
		appendChildNodes('admin-teams-table',
			TR({'class':oddeven,id:'admin-teams-table-'+id}, td_id, td_name, td_button)
		);
	}
}

Admin.prototype._editTeam = function(ref) {
	var row = $('admin-teams-table-'+ref.id);
	var cell = getFirstElementByTagAndClassName('td', 'name', row);
	var button = getFirstElementByTagAndClassName('button', null, row);
	if(button.innerHTML == 'Edit') {
		var input = INPUT({value:cell.innerHTML});
		replaceChildNodes(cell, input);
		button.innerHTML = 'Save';
	} else {	//save it
		IDE_backend_request("admin/team-name-put",
			{
			   'id': ref.id,
			 'name': cell.firstChild.value
			},
			bind( this._receiveEditTeam, this, ref ),
			bind( this._errorEditTeam, this, ref )
		);
		cell.firstChild.disabled = true;
		button.disabled = true;
	}
}
Admin.prototype._receiveEditTeam = function(ref, nodes) {
	var row = $('admin-teams-table-'+ref.id);
	if(nodes.success) {
		this._prompt = status_msg("Team name updated", LEVEL_OK);
		var cell = getFirstElementByTagAndClassName('td', 'name', row);
		var button = getFirstElementByTagAndClassName('button', null, row);
		cell.innerHTML = nodes.name;
		button.innerHTML = 'Edit';
		button.disabled = false;
		user.team_names[nodes.id] = nodes.name;
		if(nodes.id == team) {
			$('teamname').innerHTML = nodes.name;
		}
	} else {
		this._errorEditTeam(ref, nodes);
	}
}
Admin.prototype._errorEditTeam = function(ref, nodes) {
	this._prompt = status_msg("Failed to update team name", LEVEL_ERROR);
	var row = $('admin-teams-table-'+ref.id);
	var cell = getFirstElementByTagAndClassName('td', 'name', row);
	var button = getFirstElementByTagAndClassName('button', null, row);
	cell.firstChild.disabled = false;
	button.disabled = false;
}
/* *****	End Team editing Code 	***** */

/* *****	Student blog feed listing code	***** */
Admin.prototype._receiveGetBlogFeeds = function(nodes) {
	var td_user = TH({'class':'user'}, 'User ID');
	var td_url = TH({'class':'url'}, 'URL');
	var td_status = TH({'class':'status'}, 'Status');
	replaceChildNodes('admin-feeds-table');
	appendChildNodes('admin-feeds-table', TR({'class':'even'}, td_user, td_url, td_status));

	var make_selectbox = function(properties, options, def) {
		var s = SELECT(properties);
		for( value in options ) {
			var opt = OPTION({value:value}, options[value]);
			if(value == def)
				opt.selected = true;
			appendChildNodes(s, opt);
		}
		return s;
	};

	var options = {unchecked:'Unchecked',valid:'Valid',invalid:'Invalid'};
	//iterate over all feeds and append them to the table
	for( var i=0; i<nodes.feeds.length; i++ ) {
		// Add an id to each field to ensure that they're unique.
		// These used to be the DB ids, but there's no DB any more...
		nodes.feeds[i].id = i;
		var feed = nodes.feeds[i];
		td_user = TD({'class':'user'}, feed.user);
		td_url = TD({'class':'url'}, A({href:feed.url},feed.url));

		var status = (feed.checked ? (feed.valid ? 'valid' : 'invalid') : 'unchecked');
		var selectbox = make_selectbox({id:'admin-feeds-'+feed.id}, options, status);
		var ref = {id:feed.id, url:feed.url, status:status, selectbox:selectbox};
		this._signals.push(connect(selectbox, 'onchange', bind(this.setBlogStatus, this, ref)));
		td_status = TD({'class':'status'}, selectbox);

		var oddeven = i % 2 == 0 ? 'odd' : 'even';
		appendChildNodes('admin-feeds-table', TR({'class':oddeven}, td_user, td_url, td_status));
		this.showBlogStatus(ref);
	}
}
Admin.prototype._errorGetBlogFeeds = function() {
		this._prompt = status_msg("Unable to load blog feeds", LEVEL_ERROR);
		log("Admin: Failed to retrieve feed blog urls");
		return;
}
Admin.prototype.GetBlogFeeds = function() {
	log("Admin: Retrieving blog feeds");
	IDE_backend_request("admin/feed-status-get", {},
		bind(this._receiveGetBlogFeeds, this),
		bind(this._errorGetBlogFeeds, this)
	);
}
/* *****    End Student blog feed listing code	***** */

/* *****	RSS feed validation code	***** */
Admin.prototype._receiveBlogStatus = function(ref, nodes) {
	if(nodes.success) {
		this._prompt = status_msg("Blog feed updated", LEVEL_OK);
	} else {
		this._errorBlogStatus(ref, nodes);
	}
}
Admin.prototype._errorBlogStatus = function(ref, nodes) {
	this._prompt = status_msg("Unable to update blog feed", LEVEL_ERROR);
	$('admin-feeds-'+ref.id).value = ref.status;
	this.showBlogStatus(ref);
}
Admin.prototype.setBlogStatus = function(ref) {
	log("Admin: Setting blog feed status");
	var status = $('admin-feeds-'+ref.id).value;
	IDE_backend_request("admin/feed-status-put",
		{
		    url: ref.url,
		 status: status
		},
		bind( this._receiveBlogStatus, this, ref ),
		bind( this._errorBlogStatus, this, ref )
	);

	ref.status = status;
	this.showBlogStatus(ref);
}
Admin.prototype.showBlogStatus = function(ref) {
	log("Admin: Showing blog feed status");
	var tr = getFirstParentByTagAndClassName('admin-feeds-'+ref.id, 'tr', null);
	removeElementClass(tr, 'unchecked-feed-url');
	removeElementClass(tr, 'valid-feed-url');
	removeElementClass(tr, 'invalid-feed-url');
	addElementClass(tr, ref.status+'-feed-url');
}
/* *****	End RSS feed validation code	***** */
<|endoftext|>


<commit_before>"use strict";

describe("luga", function() {

	it("Requires jQuery in order to work", function() {
		expect(jQuery).toBeDefined();
	});

	it("Uses jQuery.extend() as simple/crude inheritance solution", function() {

		/* Class Person. */
		function Person(gender) {
			this.gender = gender;
			this.getGender = function() {
				return this.gender;
			};
		}
		/* Class Superstar */
		function Superstar(gender, name) {
			jQuery.extend(this, new Person());
			this.gender = gender;
			this.name = name;
			this.getName = function() {
				return this.name;
			};
		}
		var nicole = new Superstar("female", "Nicole Kidman");

		expect(nicole.getGender()).toEqual("female");
		expect(nicole.getName()).toEqual("Nicole Kidman");
	});

	it("Lives inside its own namespace", function() {
		expect(luga).toBeDefined();
	});

	it("Uses a separated namespace to store utility methods", function() {
		expect(luga).toBeDefined();
		expect(luga.utils).toBeDefined();
	});

	describe("namespace()", function() {

		it("Must be defined", function() {
			expect(luga.namespace).toBeDefined();
			expect(jQuery.isFunction(luga.namespace)).toBeTruthy();
		});

		it("Does not overwrite existing namespaces", function() {
			var testRoot = {};
			testRoot.child = {};
			testRoot.child.grandChild = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child.grandChild).toBeDefined();
		});

		it("By default uses window as root object", function() {
			luga.namespace("ciccio");
			expect(ciccio).toBeDefined();
		});

		it("But works on any arbitrary root object", function() {
			var testRoot = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child).toBeDefined();
		});

	});

	describe("utils.formatString()", function() {
		it("Given a string containing placeholders, assembles a new string", function() {
			expect(luga.utils.formatString("My name is {0} {1}", ["Ciccio", "Pasticcio"])).toEqual("My name is Ciccio Pasticcio");
			expect(luga.utils.formatString("This {0} is just a {0}", ["test"])).toEqual("This test is just a test");
			expect(luga.utils.formatString("My name is {firstName} {lastName}", {firstName: "Ciccio", lastName: "Pasticcio"})).toEqual("My name is Ciccio Pasticcio");
			expect(luga.utils.formatString("This {str} is just a {str}", { str:"test"} )).toEqual("This test is just a test");
		});
	});

});<commit_msg>Better copy text<commit_after>"use strict";

describe("luga", function() {

	it("Requires jQuery in order to work", function() {
		expect(jQuery).toBeDefined();
	});

	it("Uses jQuery.extend() as simple/crude inheritance solution", function() {

		/* Class Person. */
		function Person(gender) {
			this.gender = gender;
			this.getGender = function() {
				return this.gender;
			};
		}
		/* Class Superstar */
		function Superstar(gender, name) {
			jQuery.extend(this, new Person());
			this.gender = gender;
			this.name = name;
			this.getName = function() {
				return this.name;
			};
		}
		var nicole = new Superstar("female", "Nicole Kidman");

		expect(nicole.getGender()).toEqual("female");
		expect(nicole.getName()).toEqual("Nicole Kidman");
	});

	it("Lives inside its own namespace", function() {
		expect(luga).toBeDefined();
	});

	it("Uses a separated namespace to store utility methods", function() {
		expect(luga).toBeDefined();
		expect(luga.utils).toBeDefined();
	});

	describe("namespace()", function() {

		it("Must be defined", function() {
			expect(luga.namespace).toBeDefined();
			expect(jQuery.isFunction(luga.namespace)).toBeTruthy();
		});

		it("Does not override existing namespaces", function() {
			var testRoot = {};
			testRoot.child = {};
			testRoot.child.grandChild = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child.grandChild).toBeDefined();
		});

		it("By default uses window as root object", function() {
			luga.namespace("ciccio");
			expect(ciccio).toBeDefined();
		});

		it("But works on any arbitrary root object", function() {
			var testRoot = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child).toBeDefined();
		});

	});

	describe("utils.formatString()", function() {
		it("Given a string containing placeholders, assembles a new string", function() {
			expect(luga.utils.formatString("My name is {0} {1}", ["Ciccio", "Pasticcio"])).toEqual("My name is Ciccio Pasticcio");
			expect(luga.utils.formatString("This {0} is just a {0}", ["test"])).toEqual("This test is just a test");
			expect(luga.utils.formatString("My name is {firstName} {lastName}", {firstName: "Ciccio", lastName: "Pasticcio"})).toEqual("My name is Ciccio Pasticcio");
			expect(luga.utils.formatString("This {str} is just a {str}", { str:"test"} )).toEqual("This test is just a test");
		});
	});

});<|endoftext|>


<commit_before>module.exports = (env) => {
  return {
    // Values
    '$HREF': env === 'development' ? 'http://noticias.r1b.local:3000' : 'https://noticias.r1b.solutions',
    '/privacy': '/privacidad',
    '/terms': '/términos',
    '/legal': '/legal',
    '/contribute': '/contribuir',
    '/thanks': '/gracias',

    // copy / strings
    'news': 'noticias',
    'More': 'Más',
    'Error': 'Error',
    'Invalid page': 'Página no válida',
    'Wire services for everyone': 'Servicios de cable para todos',
    'terms': 'términos',
    'terms of service': 'términos de servicio',
    'terms of service copy': 'News Wires es un proyecto de hobby que no recibe financiamiento externo. Puede utilizar el sitio en cualquier sitio que desee - solo tenga en cuenta que las solicitudes automatizadas excesivas al sitio incurrirán en un costo para el autor y degradarán el servicio para otros.',
    'privacy': 'privacidad',
    'privacy policy': 'política de privacidad',
    'privacy policy copy': 'La única información que News Wires almacena sobre usted es su dirección IP pública. News Wires nunca utilizará ningún software analítico para realizar el seguimiento. En el momento de redactar este informe, News Wires no se ha visto obligado a entregar ninguna de sus datos a las fuerzas del orden',
    'Not Found': 'Extraviado',
    'legal': 'legal',
    'disclaimer copy': 'El autor no ofrece ninguna garantía con respecto a la exactitud, la imparcialidad o la integridad de cualquier informe vinculado aquí. Muchas agencias de prensa en todo el mundo están controladas por gobiernos, oligarcas y organizaciones de inteligencia. El lector debe tener cuidado de entender el clima político que rodea cada fuente de noticias que escogen leer.',
    'fair use copy': 'Los titulares y los vínculos que se muestran aquí están protegidos bajo un uso justo.',
    'contribute': 'contribuir',
    'code': 'código',
    'code copy': 'Puede encontrar el código fuente para los cables de noticias en GitHub. Todas las contribuciones son bienvenidas.',
    'donate': 'donar',
    'donate copy': 'Actualmente cuesta ~$25 / mes para ejecutar noticias-cables. Si tiene algún dinero que no desea, por favor, finalícelo a 137YADrzRA8hKt3CQs4TQUANz7THnVPiSn (BTC) o 0x7Df1c2cA14bdFC222cB044251505E4Ee44d0a4cF (ETH). Si desea donar de otra manera (hardware, bebidas alcohólicas, gracias personales), póngase en contacto con el autor directamente en sysop+donate@r1b.solutions',
    'translate': 'traducir',
    'translate copy': 'El autor está buscando traductores para la copia del sitio. Si sabes español, mandarín, hindi o árabe y le gustaría que te paguen para traducir una copia, por favor ponte en contacto con sysop+translation@r1b.solutions.',
    'suggest a source': 'sugerir una fuente',
    'suggest copy': 'Si desea ver una fuente de noticias en particular en el sitio, póngase en contacto con sysop+tip@r1b.solutions. Entre las fuentes aceptables están las agencias de prensa que están en el negocio de las noticias (por ejemplo: Reuters, Associated Press, Agence France Presse) o agencias internacionales independientes (por ejemplo: Red de Noticias del Movimiento de los No Alineados, Inter Press Service). Las fuentes inaceptables incluyen radiodifusores (por ejemplo, CNN, NPR, BBC), periódicos (por ejemplo: Washington Post, New York Times, USA Today) y publicaciones políticas (por ejemplo, Socialist Worker, Reason). Se dará prioridad a las fuentes de noticias no occidentales con una perspectiva internacional.',
    'thanks': 'gracias',
    'alyssa': 'Alyssa Keil Apoyo general',
    'patrick': 'Patrick Steadman Consejo UX',
    'victoria': 'Victoria Campbell Consejo UX',
    'cryptome': 'Cryptome Inspiración UX',
    'bkkava': 'Brooklyn Kava Apoyo general',
    'rebeccas': 'Rebecca\'s Apoyo general'
  };
};
<commit_msg>Round 1 for real<commit_after>module.exports = (env) => {
  return {
    // Values
    '$HREF': env === 'development' ? 'http://noticias.r1b.local:3000' : 'https://noticias.r1b.solutions',
    '/privacy': '/privacidad',
    '/terms': '/términos',
    '/legal': '/legal',
    '/contribute': '/contribuir',
    '/thanks': '/gracias',

    // copy / strings
    'news': 'noticias',
    'More': 'Más',
    'Error': 'Error',
    'Invalid page': 'Página inválida',
    'Wire services for everyone': 'Servicios de cable para todos',
    'terms': 'términos',
    'terms of service': 'términos de servicio',
    'terms of service copy': 'News Wires es un trabajo de aficionados que no recibe financiamiento externo. Puede utilizar el sitio de cualquier manera que desee - hay que tener en cuenta que las solicitudes automatizadas excesivas al sitio incurrirán en un costo para el autor y degradarán el servicio para otros.',
    'privacy': 'privacidad',
    'privacy policy': 'pólizas de privacidad',
    'privacy policy copy': 'La única información que News Wires guarda sobre usted es su dirección IP pública. News Wires nunca utilizará ningún software analítico para rastrearlo. En el momento de redactar este informe, News Wires no está obligado a entregar ninguna de sus datos a las fuerzas policiales',
    'Not Found': 'No Encontrado/a',
    'legal': 'legal',
    'disclaimer copy': 'El autor no garantiza la exactitud, la imparcialidad o la integridad de cualquier informe vinculado aquí. Muchas agencias de prensa en todo el mundo están controladas por gobiernos, oligarcas y organizaciones de inteligencia. El lector debe de tener en cuenta el ambiente político que rodea cada fuente de noticias que escoga leer.',
    'fair use copy': 'Los titulares y los vínculos que se muestran aquí están protegidos bajo un uso justo.',
    'contribute': 'contribuir',
    'code': 'código',
    'code copy': 'Puede encontrar el código fuente para los cables de noticias en GitHub. Todas las contribuciones son bienvenidas.',
    'donate': 'donar',
    'donate copy': 'Actualmente cuesta ~$25 / mes para ejecutar noticias-cables. Si tiene algún dinero que no desea, por favor, envíelo a 137YADrzRA8hKt3CQs4TQUANz7THnVPiSn (BTC) o 0x7Df1c2cA14bdFC222cB044251505E4Ee44d0a4cF (ETH). Si desea donar de otra manera (hardware, bebidas alcohólicas, gracias personales), por favor póngase en contacto con el autor directamente en sysop+donate@r1b.solutions',
    'translate': 'traducir',
    'translate copy': 'El autor está buscando traductores para la copia del sitio. Si sabe español, mandarín, hindi o árabe y le gustaría que te paguen para traducir una copia, por favor ponte en contacto con sysop+translation@r1b.solutions.',
    'suggest a source': 'sugerir una fuente',
    'suggest copy': 'Si desea ver una fuente de noticias particular en este sitio, por favor póngase en contacto con sysop+tip@r1b.solutions. Fuentes aceptables son las agencias de prensa que están en el negocio de las noticias (por ejemplo: Reuters, Associated Press, Agence France Presse) o agencias internacionales independientes (por ejemplo: Red de Noticias del Movimiento de los No Alineados, Inter Press Service). Las fuentes inaceptables incluyen las difusoras de radio y television (por ejemplo, CNN, NPR, BBC), periódicos (por ejemplo: Washington Post, New York Times, USA Today) y publicaciones políticas (por ejemplo, Socialist Worker, Reason). Se dará prioridad a las fuentes de noticias no occidentales con perspectiva internacional.',
    'thanks': 'gracias',
    'alyssa': 'Alyssa Keil Apoyo general',
    'patrick': 'Patrick Steadman Consejos UX',
    'victoria': 'Victoria Campbell Consejos UX',
    'cryptome': 'Cryptome Inspiración UX',
    'bkkava': 'Brooklyn Kava Apoyo general',
    'rebeccas': 'Rebecca\'s Apoyo general'
  };
};
<|endoftext|>


<commit_before>sfMediaBrowserWindowManager = {
  getInstance: function() {
    alert('init');
  },
  open: function(params) {
    var width = window.innerWidth ? window.innerWidth*.8 : 500;
    var popup = window.open(params['url'], 'sfMediaBrowser', 'width='+width+',addressbar=0,scrollbars=1');
    params['popup'] = popup;
    window.window_manager = new sfMediaBrowserWindowManagerObject(params);
  },

  addListerner: function(params) {
    document.getElementById(params['target']).onclick = function() {
      sfMediaBrowserWindowManager.open(params);
    }
  }

};

function sfMediaBrowserWindowManagerObject(params) {
  this.target = params['target'];
  this.popup = params['popup'];
}

sfMediaBrowserWindowManagerObject.prototype.callback = function(value) {
  document.getElementById(this.target).value = value;
  this.popup.close();
}
<commit_msg>add tinymce support<commit_after>sfMediaBrowserWindowManager = {

  open: function(params) {
    var width = window.innerWidth ? window.innerWidth*.8 : 500;
    params['popup'] = window.open(params['url'], 'sfMediaBrowser', 'width='+width+',addressbar=0,scrollbars=1');
    return new sfMediaBrowserWindowManagerObject(params);
  },

  addListerner: function(params) {
    var event = params['event'] ? params['event'] : 'onclick';
    params['target'] = document.getElementById(params['target']);
    params['target'][event] = function() {
      var window_manager = sfMediaBrowserWindowManager.open(params);
      window.window_manager = window_manager;
    }
  },

  tinymceCallback: function(field_name, url, type, win) {
    var window_manager = sfMediaBrowserWindowManager.open({
      target: win.document.getElementById(field_name),
      url:    '/backend_dev.php/sf_media_browser_select'
    });
    win.onunload = function() {
      window_manager.popup.close();
    }
    window_manager.popup.opener = win;
    window_manager.popup.opener.window_manager = window_manager;
  }

};

function sfMediaBrowserWindowManagerObject(params) {
  this.target = params['target'];
  this.popup = params['popup'];
}

sfMediaBrowserWindowManagerObject.prototype.callback = function(value) {
  this.getTarget().value = value;
  this.popup.close();
}
sfMediaBrowserWindowManagerObject.prototype.getTarget = function() {
  if(this.target.getAttribute)
    return this.target;
  else
    return this.popup.opener.document.getElementById(this.target);
}

<|endoftext|>