<commit_before>/*jslint indent: 2, plusplus: true*/
/*globals $, pretty*/
"use strict";

function UsersList(chat, listElement) {
  var
    currentPositionForId,
    currentIndexForId,
    ANIMATION_DURATION = 300;

  function pullOut() {
    var children = listElement.children();

    currentPositionForId = {};
    currentIndexForId = {};

    children.each(function (index) {
      var id = $(this).attr('id');
      currentPositionForId[id] = $(this).position();
      currentIndexForId[id] = index;
    });

    children.each(function (index) {
      var
        element = $(this),
        pos = currentPositionForId[element.attr('id')],
        width = element.width();

      element
        .css('position', 'absolute')
        .css('left', pos.left)
        .css('top', pos.top)
        .width(width);
    });
  }

  function putBack() {
    var
      yPositions = [],
      i,
      len;

    listElement.children().each(function (index) {
      var o = {
        y: $(this).position().top,
        element: $(this)
      };
      yPositions.push(o);
    });

    yPositions.sort(function (a, b) {
      return a.y - b.y;
    });

    len = yPositions.length;
    for (i = 0; i < len; ++i) {
      listElement.append(yPositions[i].element);
    }

    listElement.children().each(function (index) {
      $(this).css('position', 'static');
    });
  }

  function getClients() {
    var
      result = [],
      i,
      len = chat.getNumberOfClients(),
      client;

    for (i = 0; i < len; ++i) {
      client = chat.getClient(i);
      result.push(client);
    }

    result.sort(function (a, b) {
      return b.score - a.score;
    });
    return result;
  }

  this.updateList = function () {
    var
      clients = getClients(),
      clientsLength = clients.length,
      i,
      currentPosition,
      currentIndex,
      listLayout,
      element,
      yPos,
      children = listElement.children(),
      width,
      removedIndex,
      fullClient;

    if (children.length === 0) {
      for (i = 0; i < clientsLength; ++i) {
        listElement.append(pretty.fullClient(clients[i]));
      }
    } else {
      pullOut();

      element = $(children[0]);
      listLayout = {
        pos: element.position(),
        height: element.outerHeight(true)
      };

      children.each(function (index) {
        var id = $(this).attr('id');
        if (chat.getClient(id) === undefined) {
          removedIndex = index;
          $(this).fadeOut(ANIMATION_DURATION, function () { $(this).remove(); });
        }
      });

      for (i = 0; i < clientsLength; ++i) {
        currentPosition = currentPositionForId[clients[i].id];
        currentIndex = currentIndexForId[clients[i].id];

        yPos = listLayout.pos.top + listLayout.height * i;

        fullClient = pretty.fullClient(clients[i]);

        if (currentIndex === undefined) { // this entry was added
          element = $(fullClient);
          if (i === clientsLength - 1) {
            listElement.append(element);
          } else {
            $(children[i]).before(element);
          }
          width = element.width();
          element
            .css('position', 'absolute')
            .css('top', yPos)
            .css('display', 'none')
            .css('left', listLayout.pos.left)
            .width(width)
            .fadeIn(ANIMATION_DURATION);
        } else {
          $(children[currentIndex])
            .html($(fullClient).html())
            .animate({ top: yPos }, ANIMATION_DURATION);
        }
      }

      setTimeout(putBack, ANIMATION_DURATION);
    }
  };
}

<commit_msg>Fixes bug in users list<commit_after>/*jslint indent: 2, plusplus: true*/
/*globals $, pretty*/
"use strict";

function UsersList(chat, listElement) {
  var
    currentPositionForId,
    currentIndexForId,
    ANIMATION_DURATION = 300;

  function pullOut() {
    var children = listElement.children();

    currentPositionForId = {};
    currentIndexForId = {};

    children.each(function (index) {
      var id = $(this).attr('id');
      currentPositionForId[id] = $(this).position();
      currentIndexForId[id] = index;
    });

    children.each(function (index) {
      var
        element = $(this),
        pos = currentPositionForId[element.attr('id')],
        width = element.width();

      element
        .css('position', 'absolute')
        .css('left', pos.left)
        .css('top', pos.top)
        .width(width);
    });
  }

  function putBack() {
    var
      yPositions = [],
      i,
      len;

    listElement.children().each(function (index) {
      var o = {
        y: $(this).position().top,
        element: $(this)
      };
      yPositions.push(o);
    });

    console.log(yPositions);

    yPositions.sort(function (a, b) {
      return a.y - b.y;
    });

    len = yPositions.length;
    for (i = 0; i < len; ++i) {
      listElement.append(yPositions[i].element);
    }

    listElement.children().each(function (index) {
      $(this).css('position', 'static');
    });
  }

  function getClients() {
    var
      result = [],
      i,
      len = chat.getNumberOfClients(),
      client;

    for (i = 0; i < len; ++i) {
      client = chat.getClient(i);
      result.push(client);
    }

    result.sort(function (a, b) {
      return b.score - a.score;
    });
    return result;
  }

  this.updateList = function () {
    var
      clients = getClients(),
      clientsLength = clients.length,
      i,
      currentPosition,
      currentIndex,
      listLayout,
      element,
      yPos,
      children = listElement.children(),
      width,
      removedIndex,
      fullClient;

    if (children.length === 0) {
      for (i = 0; i < clientsLength; ++i) {
        listElement.append(pretty.fullClient(clients[i]));
      }
    } else {
      pullOut();

      element = $(children[0]);
      listLayout = {
        pos: element.position(),
        height: element.outerHeight(true)
      };

      children.each(function (index) {
        var id = $(this).attr('id');
        if (chat.getClient(id).id === undefined) {
          removedIndex = index;
          $(this).fadeOut(ANIMATION_DURATION, function () { $(this).remove(); });
        }
      });

      for (i = 0; i < clientsLength; ++i) {
        currentPosition = currentPositionForId[clients[i].id];
        currentIndex = currentIndexForId[clients[i].id];

        yPos = listLayout.pos.top + listLayout.height * i;

        fullClient = pretty.fullClient(clients[i]);

        if (currentIndex === undefined) { // this entry was added
          element = $(fullClient);
          if (i === clientsLength - 1) {
            listElement.append(element);
          } else {
            $(children[i]).before(element);
          }
          width = element.width();
          element
            .css('position', 'absolute')
            .css('top', yPos)
            .css('display', 'none')
            .css('left', listLayout.pos.left)
            .width(width)
            .fadeIn(ANIMATION_DURATION);
        } else {
          $(children[currentIndex])
            .html($(fullClient).html())
            .animate({ top: yPos }, ANIMATION_DURATION);
        }
      }

      setTimeout(putBack, ANIMATION_DURATION);
    }
  };
}

<|endoftext|>


<commit_before>/* eslint-disable global-require, import/no-extraneous-dependencies, import/no-unresolved */
const configure = require('enzyme').configure;

let Adapter;

[16, 15, 14, 13].forEach((adapterVersion) => {
	if (Adapter) return;

	try {
		// eslint-disable-next-line import/no-dynamic-require
		Adapter = require(`enzyme-adapter-react-${adapterVersion}`);
	} catch (ex) {
		// Ignore failure, try the next version
	}
});

configure({ adapter: new Adapter() });
<commit_msg>Base Enzyme adapter on current React version<commit_after>/* eslint-disable global-require, import/no-extraneous-dependencies, import/no-unresolved */
const configure = require('enzyme').configure;
const reactVersion = require('react').version;

const [majorReactVersion, minorReactVersion] = reactVersion.split('.');
const adapterVersion = (majorReactVersion !== '0') ? majorReactVersion : minorReactVersion;

// eslint-disable-next-line import/no-dynamic-require
const Adapter = require(`enzyme-adapter-react-${adapterVersion}`);

configure({ adapter: new Adapter() });
<|endoftext|>


<commit_before>/**
 * @fileoverview The WebExposed Chrome extension allows users to
 * organize WebKit bugs labeled with the "WebExposed" keyword.
 * @author meh@chromium.org (Max Heinritz)
 */

(function() {
  var storage = chrome.storage.local;
  // Stores incremental updates to the input elements.
  var update = {};
  var SYNC_INTERVAL = 15; // in minutes
  var SEND_UPDATE_INTERVAL = 2; // in seconds

  /**
   * Adds a column corresponding an attribute of a bug.
   * @param {string} name This is the name of the column to add. Corresponds
   *     to the name of an attribute in the model (eg 'hidden' or 'crbug id').
   * @param {string} type This is the {@code HTML} {@code input} type for the
   *     column. (eg {@code checkbox} for 'hidden')
   */
  function addColumn(name,type) {
    // Create cols for matching CSS to columns
    // Note: Bugzilla buglist tables contain at most 100 bugs per table,
    // so there are multiple tables & colgroups per page with >100 bugs.
    var colGroups = document.getElementsByTagName('colgroup');

    for (var i = colGroups.length - 1; i >= 0; i--) {
      var col = document.createElement('col');
      col.className = 'webexposed colgroup ' + name;
      colGroups[i].appendChild(col);
    }

    // Create headers from the attribute's name
    var headerLists = document.getElementsByClassName('bz_buglist_header');
    for (var i = headerLists.length - 1; i >= 0; i--) {
      var th = document.createElement('th');
      th.className = 'webexposed header ' + name;
      th.innerHTML = name.replace(/-/g," ");
      headerLists[i].appendChild(th);
    }

    addUIElementToAllRows(name,type);
  }

  /**
   * Adds a UI element on each row that corresponds to an attribute of the bug
   * on that row. Different attributes require different input types.
   * @param {string} name This is the name of the column to add. Corresponds
   *     to the name of an attribute in the model (eg 'hidden' or 'crbug id').
   * @param {string} type This is the {@code HTML} {@code input} type for the
   *     attribute. (eg {@code checkbox} for 'hidden')
   */
  function addUIElementToAllRows(name,type) {
  	// Add UI elements to modify the attribute
    var rows = document.getElementsByClassName('bz_bugitem');

    for (var i = rows.length - 1; i >= 0; i--) {
      // Extract the bug id from the link in the first cell in this row
      var bugId = rows[i].cells[0].getElementsByTagName('a')[0].innerHTML;

      var td = document.createElement('td');
      td.className = 'webexposed column ' + name;

      var input;

      // Most inputs will be input elements, except for textareas.
      if (type === 'textarea') {
        input = document.createElement('textarea');
      } else {
        input = document.createElement('input');
        input.type = type;
        // Bug ids are typically <=7 digits.
        if (name === 'crbug id') {
          input.size = 7;
        }
      }

      input.onchange = storeUpdate;
      input.oninput = storeUpdate;

      input.id = bugId + ' ' + name;
      input.name = name;
      input.className = 'webexposed input ' + bugId;

      td.appendChild(input);
      rows[i].appendChild(td);
    }
  }

  /**
   * Adds a save button.
   */
  function addSaveButton() {
    var header = document.getElementById('header');
    var button = document.createElement('input');
    button.type = 'submit';
    button.value = 'Save WebExposed Attributes';
    button.id = 'webexposed_button';
    button.class = 'webexposed button';
    button.disabled = true;

    button.addEventListener('click',sendUpdate);

    // Add the button to the header
    header.appendChild(button);
  }

  /**
   * Saves changes to input elements to a local array in preparation for POST to
   * server.
   */
  function storeUpdate() {
    var bugId = this.id.split(' ',1);
    var name = this.name;
    var value = '';

    // For each checkbox, the value is a boolean (where true means checked).
    if (this.type == 'checkbox')
      value = this.checked;
    // For other types of input (right now only textarea and text), the value
    // is the content in the input element.
    else
      value = this.value;

    // Create an entry in the update for this bug if none exists.
    if (typeof (update[bugId] == 'undefined'))
      update[bugId] = {};

    // Store the value to the array.
    update[bugId][name] = value;

    // Write the update to the console, for reference.
    console.log(JSON.stringify(update[bugId]));
    console.log(JSON.stringify(update));

    document.getElementById('webexposed_button').disabled = false;
  }

  /**
   * Save the changes to the bug states to the AppEngine backend.
   */
  function sendUpdate() {
    // Check if we have updates to send. If not, quit.
    if (update.length=0)
      return;

    // Load secret word using the Chrome extension storage API.
    storage.get('secretword',function(items) {
      // Check if the user didn't enter the secret word on the options page
      if (!items['secretword']) {
        alert('Please enter the secret word on the options page.');
      }
      else {
        // TODO: Send POST request to server
        // TODO: Disable save button once changes are made
      }
    });
  }

  /**
   * Downloads updates from the server and adjusts UI accordingly.
   */
  function loadJsonUpdate() {
    var rows = document.getElementsByClassName('bz_bugitem');

    jsonUpdate = []; // TODO: make request to App Engine Backend

    // Examine each bug and find its entry in the JSON object, if any
    for (var i = rows.length - 1; i >= 0; i--) {
      // Extract the bug id from the link in the first cell in this row
      var bugId = rows[i].cells[0].getElementsByTagName('a')[0].innerHTML;

      // If the update has no information on this bug, we can skip it
      if (typeof (jsonUpdate[bugId]) == 'undefined')
        continue;

      var updatesForThisBug = jsonUpdate[bugId];

      // Print the JSON to the console, for reference
      console.log(bugId + ': ');
      console.log(jsonUpdate[bugId]);

      // Parse the update from the server and update the corresponding UI
      // elements
      for (var j = 0; j < updatesForThisBug.length; j++) {
        var fieldName = updatesForThisBug[j][0];
        var fieldValue = updatesForThisBug[j][1];
        var inputElement = document.getElementById(fieldName + ' ' + bugId);

        // For each checkbox, the value is a boolean (where true means checked)
        if (inputElement.tagName === 'INPUT'
          && inputElement.type === 'checkbox')
            inputElement.checked = fieldValue;
        // For other types of input (right now only textarea and text), the
        // value is the content in the input element.
        else
          if(inputElement.value)
            inputElement.value = fieldValue;
      }
    }
  }

  // only run the extension on the WebExposed bug list
  if (document.URL.indexOf('keywords=WebExposed') != -1) {
    addSaveButton();
    addColumn('hidden','checkbox');
    addColumn('important','checkbox');
    addColumn('crbug_id','text');
    addColumn('comment','textarea');
    setInterval(sendUpdate, SEND_UPDATE_INTERVAL*1000);
    //TODO:Dectect idle time and reload JSON
    //setInterval(loadJsonUpdate, SYNC_INTERVAL*60*1000);
  }
})();
// TODO: Only allow one of hidden/important to be selected at a time
<commit_msg>correctly check if there are updates before sending POST request to server<commit_after>/**
 * @fileoverview The WebExposed Chrome extension allows users to
 * organize WebKit bugs labeled with the "WebExposed" keyword.
 * @author meh@chromium.org (Max Heinritz)
 */

// Note: this extension script is injected into page's existing script.
// To avoid conflating namespaces, we wrap the whole script in a self-executing
// anonymous function.
(function() {
  var storage = chrome.storage.local;
  // Stores incremental updates to the input elements.
  var update = {};
  var SYNC_INTERVAL = 15; // in minutes
  var SEND_UPDATE_INTERVAL = 2; // in seconds

  /**
   * Adds a column corresponding an attribute of a bug.
   * @param {string} name This is the name of the column to add. Corresponds
   *     to the name of an attribute in the model (eg 'hidden' or 'crbug id').
   * @param {string} type This is the {@code HTML} {@code input} type for the
   *     column. (eg {@code checkbox} for 'hidden')
   */
  function addColumn(name,type) {
    // Create cols for matching CSS to columns
    // Note: Bugzilla buglist tables contain at most 100 bugs per table,
    // so there are multiple tables & colgroups per page with >100 bugs.
    var colGroups = document.getElementsByTagName('colgroup');

    for (var i = colGroups.length - 1; i >= 0; i--) {
      var col = document.createElement('col');
      col.className = 'webexposed colgroup ' + name;
      colGroups[i].appendChild(col);
    }

    // Create headers from the attribute's name
    var headerLists = document.getElementsByClassName('bz_buglist_header');
    for (var i = headerLists.length - 1; i >= 0; i--) {
      var th = document.createElement('th');
      th.className = 'webexposed header ' + name;
      th.innerHTML = name.replace(/-/g," ");
      headerLists[i].appendChild(th);
    }

    addUIElementToAllRows(name,type);
  }

  /**
   * Adds a UI element on each row that corresponds to an attribute of the bug
   * on that row. Different attributes require different input types.
   * @param {string} name This is the name of the column to add. Corresponds
   *     to the name of an attribute in the model (eg 'hidden' or 'crbug id').
   * @param {string} type This is the {@code HTML} {@code input} type for the
   *     attribute. (eg {@code checkbox} for 'hidden')
   */
  function addUIElementToAllRows(name,type) {
  	// Add UI elements to modify the attribute
    var rows = document.getElementsByClassName('bz_bugitem');

    for (var i = rows.length - 1; i >= 0; i--) {
      // Extract the bug id from the link in the first cell in this row
      var bugId = rows[i].cells[0].getElementsByTagName('a')[0].innerHTML;

      var td = document.createElement('td');
      td.className = 'webexposed column ' + name;

      var input;

      // Most inputs will be input elements, except for textareas.
      if (type === 'textarea') {
        input = document.createElement('textarea');
      } else {
        input = document.createElement('input');
        input.type = type;
        // Bug ids are typically <=7 digits.
        if (name === 'crbug id') {
          input.size = 7;
        }
      }

      input.onchange = storeUpdate;
      input.oninput = storeUpdate;

      input.id = bugId + ' ' + name;
      input.name = name;
      input.className = 'webexposed input ' + bugId;

      td.appendChild(input);
      rows[i].appendChild(td);
    }
  }

  /**
   * Adds a save button.
   */
  function addSaveButton() {
    var header = document.getElementById('header');
    var button = document.createElement('input');
    button.type = 'submit';
    button.value = 'Save WebExposed Attributes';
    button.id = 'webexposed_button';
    button.class = 'webexposed button';
    button.disabled = true;

    button.addEventListener('click',sendUpdate);

    // Add the button to the header
    header.appendChild(button);
  }

  /**
   * Saves changes to input elements to a local array in preparation for POST to
   * server.
   */
  function storeUpdate() {
    var bugId = this.id.split(' ',1);
    var name = this.name;
    var value = '';

    // For each checkbox, the value is a boolean (where true means checked).
    if (this.type == 'checkbox')
      value = this.checked;
    // For other types of input (right now only textarea and text), the value
    // is the content in the input element.
    else
      value = this.value;

    // Create an entry in the update for this bug if none exists.
    if (typeof (update[bugId] == 'undefined'))
      update[bugId] = {};

    // Store the value to the array.
    update[bugId][name] = value;

    // Write the update to the console, for reference.
    console.log(JSON.stringify(update[bugId]));
    console.log(JSON.stringify(update));

    document.getElementById('webexposed_button').disabled = false;
  }

  /**
   * Save the changes to the bug states to the AppEngine backend.
   */
  function sendUpdate() {
    // Check if we have updates to send. If not, don't send the update.
    if ( Object.keys(update).length === 0 )
      return;

    // Load secret word using the Chrome extension storage API.
    storage.get('secretword',function(items) {
      // Check if the user didn't enter the secret word on the options page
      if (!items['secretword']) {
        alert('Please enter the secret word on the options page.');
      }
      else {
        update = {};
        alert('update');
        // TODO: Send POST request to server
        // TODO: Disable save button once changes are made
      }
    });
  }

  /**
   * Downloads updates from the server and adjusts UI accordingly.
   */
  function loadJsonUpdate() {
    var rows = document.getElementsByClassName('bz_bugitem');

    jsonUpdate = []; // TODO: make request to App Engine Backend

    // Examine each bug and find its entry in the JSON object, if any
    for (var i = rows.length - 1; i >= 0; i--) {
      // Extract the bug id from the link in the first cell in this row
      var bugId = rows[i].cells[0].getElementsByTagName('a')[0].innerHTML;

      // If the update has no information on this bug, we can skip it
      if (typeof (jsonUpdate[bugId]) == 'undefined')
        continue;

      var updatesForThisBug = jsonUpdate[bugId];

      // Print the JSON to the console, for reference
      console.log(bugId + ': ');
      console.log(jsonUpdate[bugId]);

      // Parse the update from the server and update the corresponding UI
      // elements
      for (var j = 0; j < updatesForThisBug.length; j++) {
        var fieldName = updatesForThisBug[j][0];
        var fieldValue = updatesForThisBug[j][1];
        var inputElement = document.getElementById(fieldName + ' ' + bugId);

        // For each checkbox, the value is a boolean (where true means checked)
        if (inputElement.tagName === 'INPUT'
          && inputElement.type === 'checkbox')
            inputElement.checked = fieldValue;
        // For other types of input (right now only textarea and text), the
        // value is the content in the input element.
        else
          if(inputElement.value)
            inputElement.value = fieldValue;
      }
    }
  }

  // only run the extension on the WebExposed bug list
  if (document.URL.indexOf('keywords=WebExposed') != -1) {
    addSaveButton();
    addColumn('hidden','checkbox');
    addColumn('important','checkbox');
    addColumn('crbugId','text');
    addColumn('comment','textarea');
    setInterval(sendUpdate, SEND_UPDATE_INTERVAL*1000);
    //TODO:Dectect idle time and reload JSON
    //setInterval(loadJsonUpdate, SYNC_INTERVAL*60*1000);
  }
})();
// TODO: Only allow one of hidden/important to be selected at a time
<|endoftext|>


<commit_before>/*global define*/
/**
 * To load this extension, add the following to your custom.js:
 *
 * IPython.load_extensions('nbgrader');
 *
**/

define([
    'require',
    'jquery',
    'base/js/namespace',
    'notebook/js/celltoolbar',

], function (require, $, IPython, celltoolbar) {
    "use strict";

    var CellToolbar = celltoolbar.CellToolbar;

    /**
     * Is the cell a solution cell?
     */
    var is_solution = function (cell) {
        if (cell.metadata.nbgrader === undefined) {
            cell.metadata.nbgrader = {};
            return false;
        } else if (cell.metadata.nbgrader.solution === undefined) {
            return false;
        } else {
            return cell.metadata.nbgrader.solution;
        }
    };

    /**
     * Is the cell a grader cell?
     */
    var is_grader = function (cell) {
        if (cell.metadata.nbgrader === undefined) {
            cell.metadata.nbgrader = {};
            return false;
        } else if (cell.metadata.nbgrader.grade === undefined) {
            return false;
        } else {
            return cell.metadata.nbgrader.grade;
        }
    };

    /**
     * Add a display class to the cell element, depending on the
     * nbgrader cell type.
     */
    var display_cell = function (cell) {
        var grader = is_grader(cell),
            grade_cls = "nbgrader-grade-cell",
            elem = cell.element;

        if (!elem) {
            return;
        }

        if (grader && !elem.hasClass(grade_cls)) {
            elem.addClass(grade_cls);
        } else if (!grader && elem.hasClass(grade_cls)) {
            elem.removeClass(grade_cls);
        }
    };

    /**
     * Create a checkbox to mark whether the cell is a grader cell or
     * not.
     */
    var create_grader_checkbox = function (div, cell, celltoolbar) {
        var chkb = $('<input/>').attr('type', 'checkbox');
        var lbl = $('<label/>').append($('<span/>').text("Grade? "));
        lbl.append(chkb);
        chkb.attr("checked", is_grader(cell));
        chkb.click(function () {
            cell.metadata.nbgrader.grade = !is_grader(cell);
            celltoolbar.rebuild();
            display_cell(cell);
        });
        display_cell(cell);
        $(div).append($('<span/>').append(lbl));
    };

    /**
     * Create a checkbox to mark whether the cell is a solution cell
     * or not.
     */
    var create_solution_checkbox = CellToolbar.utils.checkbox_ui_generator(
        "Solution? ",
        function (cell, val) {
            cell.metadata.nbgrader.solution = val;
        },
        is_solution);

    /**
     * Create the input text box for the problem or test id.
     */
    var create_id_input = function (div, cell, celltoolbar) {
        if (!is_grader(cell)) {
            return;
        }

        var local_div = $('<div/>');
        var text = $('<input/>').attr('type', 'text');
        var lbl = $('<label/>').append($('<span/>').text('ID: '));
        lbl.append(text);

        text.addClass('nbgrader-id-input');
        text.attr("value", cell.metadata.nbgrader.grader_id);
        text.keyup(function () {
            cell.metadata.nbgrader.grader_id = text.val();
        });

        local_div.addClass('nbgrader-id');
        $(div).append(local_div.append($('<span/>').append(lbl)));

        IPython.keyboard_manager.register_events(text);
    };

    /**
     * Create the input text box for the number of points the problem
     * is worth.
     */
    var create_points_input = function (div, cell, celltoolbar) {
        if (!is_grader(cell)) {
            return;
        }

        var local_div = $('<div/>');
        var text = $('<input/>').attr('type', 'number');
        var lbl = $('<label/>').append($('<span/>').text('Points: '));
        lbl.append(text);

        text.addClass('nbgrader-points-input');
        text.attr("value", cell.metadata.nbgrader.points);
        text.keyup(function () {
            cell.metadata.nbgrader.points = text.val();
        });

        local_div.addClass('nbgrader-points');
        $(div).append(local_div.append($('<span/>').append(lbl)));

        IPython.keyboard_manager.register_events(text);
    };

    /**
     * Load custom css for the nbgrader toolbar.
     */
    var load_css = function () {
        var link = document.createElement('link');
        link.type = 'text/css';
        link.rel = 'stylesheet';
        link.href = require.toUrl('./nbgrader.css');
        document.getElementsByTagName('head')[0].appendChild(link);
    };

    /**
     * Load the nbgrader toolbar extension.
     */
    var load_ipython_extension = function () {
        load_css();
        CellToolbar.register_callback('create_assignment.solution_checkbox', create_solution_checkbox);
        CellToolbar.register_callback('create_assignment.grader_checkbox', create_grader_checkbox);
        CellToolbar.register_callback('create_assignment.id_input', create_id_input);
        CellToolbar.register_callback('create_assignment.points_input', create_points_input);

        var preset = [
            'create_assignment.solution_checkbox',
            'create_assignment.grader_checkbox',
            'create_assignment.points_input',
            'create_assignment.id_input'
        ];
        CellToolbar.register_preset('Create Assignment', preset, IPython.notebook);
        console.log('nbgrader extension for metadata editing loaded.');
    };

    return {
        'load_ipython_extension': load_ipython_extension
    };
});
<commit_msg>Rename load_ipython_extension to load_extension<commit_after>/*global define*/
/**
 * To load this extension, add the following to your custom.js:
 *
 * IPython.load_extensions('nbgrader');
 *
**/

define([
    'require',
    'jquery',
    'base/js/namespace',
    'notebook/js/celltoolbar',

], function (require, $, IPython, celltoolbar) {
    "use strict";

    var CellToolbar = celltoolbar.CellToolbar;

    /**
     * Is the cell a solution cell?
     */
    var is_solution = function (cell) {
        if (cell.metadata.nbgrader === undefined) {
            cell.metadata.nbgrader = {};
            return false;
        } else if (cell.metadata.nbgrader.solution === undefined) {
            return false;
        } else {
            return cell.metadata.nbgrader.solution;
        }
    };

    /**
     * Is the cell a grader cell?
     */
    var is_grader = function (cell) {
        if (cell.metadata.nbgrader === undefined) {
            cell.metadata.nbgrader = {};
            return false;
        } else if (cell.metadata.nbgrader.grade === undefined) {
            return false;
        } else {
            return cell.metadata.nbgrader.grade;
        }
    };

    /**
     * Add a display class to the cell element, depending on the
     * nbgrader cell type.
     */
    var display_cell = function (cell) {
        var grader = is_grader(cell),
            grade_cls = "nbgrader-grade-cell",
            elem = cell.element;

        if (!elem) {
            return;
        }

        if (grader && !elem.hasClass(grade_cls)) {
            elem.addClass(grade_cls);
        } else if (!grader && elem.hasClass(grade_cls)) {
            elem.removeClass(grade_cls);
        }
    };

    /**
     * Create a checkbox to mark whether the cell is a grader cell or
     * not.
     */
    var create_grader_checkbox = function (div, cell, celltoolbar) {
        var chkb = $('<input/>').attr('type', 'checkbox');
        var lbl = $('<label/>').append($('<span/>').text("Grade? "));
        lbl.append(chkb);
        chkb.attr("checked", is_grader(cell));
        chkb.click(function () {
            cell.metadata.nbgrader.grade = !is_grader(cell);
            celltoolbar.rebuild();
            display_cell(cell);
        });
        display_cell(cell);
        $(div).append($('<span/>').append(lbl));
    };

    /**
     * Create a checkbox to mark whether the cell is a solution cell
     * or not.
     */
    var create_solution_checkbox = CellToolbar.utils.checkbox_ui_generator(
        "Solution? ",
        function (cell, val) {
            cell.metadata.nbgrader.solution = val;
        },
        is_solution);

    /**
     * Create the input text box for the problem or test id.
     */
    var create_id_input = function (div, cell, celltoolbar) {
        if (!is_grader(cell)) {
            return;
        }

        var local_div = $('<div/>');
        var text = $('<input/>').attr('type', 'text');
        var lbl = $('<label/>').append($('<span/>').text('ID: '));
        lbl.append(text);

        text.addClass('nbgrader-id-input');
        text.attr("value", cell.metadata.nbgrader.grader_id);
        text.keyup(function () {
            cell.metadata.nbgrader.grader_id = text.val();
        });

        local_div.addClass('nbgrader-id');
        $(div).append(local_div.append($('<span/>').append(lbl)));

        IPython.keyboard_manager.register_events(text);
    };

    /**
     * Create the input text box for the number of points the problem
     * is worth.
     */
    var create_points_input = function (div, cell, celltoolbar) {
        if (!is_grader(cell)) {
            return;
        }

        var local_div = $('<div/>');
        var text = $('<input/>').attr('type', 'number');
        var lbl = $('<label/>').append($('<span/>').text('Points: '));
        lbl.append(text);

        text.addClass('nbgrader-points-input');
        text.attr("value", cell.metadata.nbgrader.points);
        text.keyup(function () {
            cell.metadata.nbgrader.points = text.val();
        });

        local_div.addClass('nbgrader-points');
        $(div).append(local_div.append($('<span/>').append(lbl)));

        IPython.keyboard_manager.register_events(text);
    };

    /**
     * Load custom css for the nbgrader toolbar.
     */
    var load_css = function () {
        var link = document.createElement('link');
        link.type = 'text/css';
        link.rel = 'stylesheet';
        link.href = require.toUrl('./nbgrader.css');
        document.getElementsByTagName('head')[0].appendChild(link);
    };

    /**
     * Load the nbgrader toolbar extension.
     */
    var load_extension = function () {
        load_css();
        CellToolbar.register_callback('create_assignment.solution_checkbox', create_solution_checkbox);
        CellToolbar.register_callback('create_assignment.grader_checkbox', create_grader_checkbox);
        CellToolbar.register_callback('create_assignment.id_input', create_id_input);
        CellToolbar.register_callback('create_assignment.points_input', create_points_input);

        var preset = [
            'create_assignment.solution_checkbox',
            'create_assignment.grader_checkbox',
            'create_assignment.points_input',
            'create_assignment.id_input'
        ];
        CellToolbar.register_preset('Create Assignment', preset, IPython.notebook);
        console.log('nbgrader extension for metadata editing loaded.');
    };

    return {
        'load_ipython_extension': load_extension
    };
});
<|endoftext|>


<commit_before>var jsp  = require('uglify-js').parser,
    pro  = require('uglify-js').uglify,
    fs   = require('fs'),
    path = require('path'),
    util = require('util');

var REQUIRE = 'require';
var REQUIRE_TEXT = 'requireText';
var PREFIX  = '__module_';

var JS_EXTS = ['.js'];
var CSS_EXTS = ['.css'];

function new_sate () {
    return {
        included: {},
        includedCount: 0,
        currentPath: '',
        searchPaths: [],
        moduleAsts: [],
        options: {}
    };
}

var sate = exports.state = null;

var walker = pro.ast_walker(),
    walkers = {
       "call": function(expr, args) {
           if (expr[0] === 'name' && expr[1] === REQUIRE) {
               var file = resolve_real_path(args[0][1], JS_EXTS);

               if (!state.included[file]) {
                   file_to_ast(file, true);
               }
               return [this[0], expr, [['num', state.included[file]]] ];
           } else if (expr[0] === 'name' && expr[1] === REQUIRE_TEXT) {
               var file = resolve_real_path(args[0][1], CSS_EXTS);
               return ['string', fs.readFileSync(file, 'utf8')];
           }
           return null;
       }
    };

function path_to_file (filePath, extNames) {
    var ext = path.extname(filePath);
    if (ext) {
        return path.existsSync(filePath) ? filePath : false;
    } else {
        for (var i=0; i < extNames.length; i++) {
            var tryPath = filePath + extNames[i];
            if (path.existsSync(tryPath)) return tryPath;
        };
    }
    return false;
}

function resolve_path (filePath, extNames) {
    if (filePath.charAt(0) === '.') {
        return path_to_file( path.join(path.dirname(state.currentPath), filePath), extNames );
    } else {
        var searchPaths = [path.dirname(state.currentPath)].concat(state.searchPaths);
        for (var i=0; i < searchPaths.length; i++) {
            var tryPath = path_to_file( path.join(searchPaths[i], filePath), extNames);
            if (tryPath) return tryPath;
        };
    }
    return false;
}

function resolve_real_path (filePath, extNames) {
    var resolvedPath = resolve_path(filePath, extNames);
    if (!resolvedPath) throw new Error('Path ' + filePath + ' not found.');
    return fs.realpathSync(resolvedPath);
}

function file_to_ast (filePath, wrap) {
    state.included[filePath] = state.includedCount++;
    var oldPath = state.currentPath;
    state.currentPath = filePath;
    var text = fs.readFileSync(filePath, 'utf8');
    if (wrap) {
        text = '(function(global, module) {var exports = this;' + text + '\nreturn module.exports;})';
    }
    var ast = jsp.parse(text);
    var newAst = walker.with_walkers(walkers, function() {
        return walker.walk(ast);
    });
    state.currentPath = oldPath;
    state.moduleAsts[state.included[filePath]] = newAst;
}

function static_require (filePath, options) {
    state = exports.state = new_sate();
    filePath = fs.realpathSync(filePath);
    state.currentPath = filePath;
    state.options = options || {};
    state.searchPaths = state.options.searchPaths || [path.dirname(state.currentPath)];
    state.moduleAsts = [];

    file_to_ast(filePath, true);

    var code = 'var global = this;';
    code    += 'function require(index) { if (!require.cache[index]) {var module = {exports: {}}; require.cache[index] = require.modules[index].call(module.exports, global, module);} return require.cache[index]; }\n';
    code    += 'require.modules = []; require.cache = [];';
    var body = jsp.parse(code)[1];

    for (var i=0; i < state.includedCount; i++) {
        body[body.length] =
            [ 'stat',
                ['assign',
                    true,
                    ['sub',
                        ['dot', ['name', 'require'], 'modules' ],
                        ['num', i]
                    ],
                    state.moduleAsts[i][1][0][1]
                ]
            ];
    };
    body.push(['stat', ['call', ['name', 'require'], [['num', '0']]]]);

    return [ 'toplevel',
      [ [ 'stat',
          [ 'call',
            [ 'function',
              null,
              [],
              body
            ],
            [] ] ] ] ];

}

exports.parse = static_require;
exports.gen_code = function(filePath, options) {
    return pro.gen_code(
        static_require(filePath, options),
        options && options.beautify
    );
};<commit_msg>shorter module declaration<commit_after>var jsp  = require('uglify-js').parser,
    pro  = require('uglify-js').uglify,
    fs   = require('fs'),
    path = require('path'),
    util = require('util');

var REQUIRE = 'require';
var REQUIRE_TEXT = 'requireText';
var PREFIX  = '__module_';

var JS_EXTS = ['.js'];
var CSS_EXTS = ['.css'];

function new_sate () {
    return {
        included: {},
        includedCount: 0,
        currentPath: '',
        searchPaths: [],
        moduleAsts: [],
        options: {}
    };
}

var sate = exports.state = null;

var walker = pro.ast_walker(),
    walkers = {
       "call": function(expr, args) {
           if (expr[0] === 'name' && expr[1] === REQUIRE) {
               var file = resolve_real_path(args[0][1], JS_EXTS);

               if (!state.included[file]) {
                   file_to_ast(file, true);
               }
               return [this[0], expr, [['num', state.included[file]]] ];
           } else if (expr[0] === 'name' && expr[1] === REQUIRE_TEXT) {
               var file = resolve_real_path(args[0][1], CSS_EXTS);
               return ['string', fs.readFileSync(file, 'utf8')];
           }
           return null;
       }
    };

function path_to_file (filePath, extNames) {
    var ext = path.extname(filePath);
    if (ext) {
        return path.existsSync(filePath) ? filePath : false;
    } else {
        for (var i=0; i < extNames.length; i++) {
            var tryPath = filePath + extNames[i];
            if (path.existsSync(tryPath)) return tryPath;
        };
    }
    return false;
}

function resolve_path (filePath, extNames) {
    if (filePath.charAt(0) === '.') {
        return path_to_file( path.join(path.dirname(state.currentPath), filePath), extNames );
    } else {
        var searchPaths = [path.dirname(state.currentPath)].concat(state.searchPaths);
        for (var i=0; i < searchPaths.length; i++) {
            var tryPath = path_to_file( path.join(searchPaths[i], filePath), extNames);
            if (tryPath) return tryPath;
        };
    }
    return false;
}

function resolve_real_path (filePath, extNames) {
    var resolvedPath = resolve_path(filePath, extNames);
    if (!resolvedPath) throw new Error('Path ' + filePath + ' not found.');
    return fs.realpathSync(resolvedPath);
}

function file_to_ast (filePath, wrap) {
    state.included[filePath] = state.includedCount++;
    var oldPath = state.currentPath;
    state.currentPath = filePath;
    var text = fs.readFileSync(filePath, 'utf8');
    if (wrap) {
        text = '(function(global, module) {var exports = this;' + text + '})';
    }
    var ast = jsp.parse(text);
    var newAst = walker.with_walkers(walkers, function() {
        return walker.walk(ast);
    });
    state.currentPath = oldPath;
    state.moduleAsts[state.included[filePath]] = newAst;
}

function static_require (filePath, options) {
    state = exports.state = new_sate();
    filePath = fs.realpathSync(filePath);
    state.currentPath = filePath;
    state.options = options || {};
    state.searchPaths = state.options.searchPaths || [path.dirname(state.currentPath)];
    state.moduleAsts = [];

    file_to_ast(filePath, true);

    var code = 'var global = this;';
    code    += 'function require(index) { if (!require.cache[index]) {var module = {exports: {}}; require.modules[index].call(module.exports, global, module); require.cache[index] = module.exports} return require.cache[index]; }\n';
    code    += 'require.modules = []; require.cache = [];';
    var body = jsp.parse(code)[1];

    for (var i=0; i < state.includedCount; i++) {
        body[body.length] =
            [ 'stat',
                ['assign',
                    true,
                    ['sub',
                        ['dot', ['name', 'require'], 'modules' ],
                        ['num', i]
                    ],
                    state.moduleAsts[i][1][0][1]
                ]
            ];
    };
    body.push(['stat', ['call', ['name', 'require'], [['num', '0']]]]);

    return [ 'toplevel',
      [ [ 'stat',
          [ 'call',
            [ 'function',
              null,
              [],
              body
            ],
            [] ] ] ] ];

}

exports.parse = static_require;
exports.gen_code = function(filePath, options) {
    return pro.gen_code(
        static_require(filePath, options),
        options && options.beautify
    );
};<|endoftext|>


<commit_before>var Solver = require('..');

var s = new Solver();

var context = {
    name:'Pepe',
    hola: 'Hello',
    user:{
        name: 'Peperone',
        address:{
            city:'New York'
        }
    },
    greeting: '${name}, how are you?'
};

var solved = s.solve({
    message:'${name}, how are you?',
    greet: '${hola} ${user.name}!!! Still living in ${user.address.city}?',
    testing: {
       nested: {
           templates: '${user.address.city}'
       }
    }
}, context);
console.log('***********')
console.log(solved.greet)
console.log(solved.message)
console.log(solved.testing)

s.template.openTag = '@{';
solved = s.solve({
    message:'@{name} was here!',
    greet: '@{hola}, @{user.address.city}?'
}, context);

console.log('|||||||||||||||||')
console.log(solved.greet)
console.log(solved.message)

s.template.openTag = '${';
solved = s.solve(context);
console.log('///////////////////')
console.log(solved.name)
console.log(solved.hola)
console.log(solved.greeting)
<commit_msg>Add missing colons<commit_after>var Solver = require('..');

var s = new Solver();

var context = {
    name:'Pepe',
    hola: 'Hello',
    user: {
        name: 'Peperone',
        address: {
            city:'New York'
        }
    },
    greeting: '${name}, how are you?'
};

var solved = s.solve({
    message:'${name}, how are you?',
    greet: '${hola} ${user.name}!!! Still living in ${user.address.city}?',
    testing: {
       nested: {
           templates: '${user.address.city}'
       }
   }
}, context);

console.log('***********');
console.log(solved.greet);
console.log(solved.message);
console.log(solved.testing);

/*
 *
 */
s.template.openTag = '@{';
solved = s.solve({
    message:'@{name} was here!',
    greet: '@{hola}, @{user.address.city}?'
}, context);

console.log('|||||||||||||||||');
console.log(solved.greet);
console.log(solved.message);

s.template.openTag = '${';
solved = s.solve(context);
console.log('///////////////////');
console.log(solved.name);
console.log(solved.hola);
console.log(solved.greeting);
<|endoftext|>


<commit_before>if (document.location.hostname == "localhost") {
    var baseurl = "";
} else {
    var baseurl = "https://noconfidencevote.openup.org.za";
}

var initContactMPsPymParent = function() {
  var pymParent = new pym.Parent('contactmps-embed-parent', baseurl + '/campaign/newsmedia/', {});
};

document.write('

');
document.write('');
<commit_msg>Revert "Remove hack for news24 android app since loading pym async fixes it"<commit_after>if (document.location.hostname == "localhost") {
    var baseurl = "";
} else {
    var baseurl = "https://noconfidencevote.openup.org.za";
}

var initContactMPsPymParent = function() {
  var pymParent = new pym.Parent('contactmps-embed-parent', baseurl + '/campaign/newsmedia/', {});
};

var agent = navigator.userAgent.toLowerCase();
if (agent.includes("mobile") && agent.includes("android")) {
  // addEventListener only available in later chrome versions
  window.addEventListener("load",function(){
    window.addEventListener('error', function(e) {
      ga('send', 'event', 'JavaScript Error Parent', e.filename + ':  ' + e.lineno, e.message);
    });

  });
  // Don't initialise pymParent! we iframe it ourselves!
  document.write('
Loading...
');
} else {
  document.write('

');
  document.write('');
}
<|endoftext|>


<commit_before>'use strict';

import { async } from 'metal';
import dom from 'metal-dom';
import Modal from '../src/Modal';

describe('Modal', function() {
	var modal;

	afterEach(function() {
		if (modal) {
			modal.dispose();
		}
	});

	it('should render with default state', function() {
		modal = new Modal().render();
		var header = modal.element.querySelector('.modal-header');
		var body = modal.element.querySelector('.modal-body');
		var footer = modal.element.querySelector('.modal-footer');
		assert.strictEqual('', header.innerHTML);
		assert.strictEqual('', body.innerHTML);
		assert.strictEqual('', footer.innerHTML);
		assert.strictEqual('block', modal.element.style.display);
	});

	it('should render with header only', function() {
		modal = new Modal({
			header: 'header'
		}).render();
		var headerElement = modal.element.querySelector('.modal-header');
		var body = modal.element.querySelector('.modal-body').innerHTML;
		var footer = modal.element.querySelector('.modal-footer').innerHTML;
		assert.strictEqual('header', headerElement.childNodes[1].textContent);
		assert.ok(dom.hasClass(headerElement.childNodes[0], 'close'));
		assert.strictEqual('', body);
		assert.strictEqual('', footer);
	});

	it('should render with body only', function() {
		modal = new Modal({
			body: 'body'
		}).render();
		var header = modal.element.querySelector('.modal-header').innerHTML;
		var body = modal.element.querySelector('.modal-body').innerHTML;
		var footer = modal.element.querySelector('.modal-footer').innerHTML;
		assert.strictEqual('', header);
		assert.strictEqual('body', body);
		assert.strictEqual('', footer);
	});

	it('should render with footer only', function() {
		modal = new Modal({
			footer: 'footer'
		}).render();
		var header = modal.element.querySelector('.modal-header').innerHTML;
		var body = modal.element.querySelector('.modal-body').innerHTML;
		var footer = modal.element.querySelector('.modal-footer').innerHTML;
		assert.strictEqual('', header);
		assert.strictEqual('', body);
		assert.strictEqual('footer', footer);
	});

	it('should cause dom repaint when body state changes', function(done) {
		modal = new Modal().render();
		modal.body = 'body';
		async.nextTick(function() {
			var body = modal.element.querySelector('.modal-body').innerHTML;
			assert.strictEqual('body', body);
			done();
		});
	});

	it('should cause dom repaint when header state changes', function(done) {
		modal = new Modal().render();
		modal.header = 'header';
		async.nextTick(function() {
			var header = modal.element.querySelector('.modal-header');
			assert.strictEqual('header', header.childNodes[1].textContent);
			assert.ok(dom.hasClass(header.childNodes[0], 'close'));
			done();
		});
	});

	it('should cause dom repaint when footer state changes', function(done) {
		modal = new Modal().render();
		modal.footer = 'footer';
		async.nextTick(function() {
			var footer = modal.element.querySelector('.modal-footer').innerHTML;
			assert.strictEqual('footer', footer);
			done();
		});
	});

	it('should cause dom repaint when visible state changes', function(done) {
		modal = new Modal().render();
		modal.visible = false;
		async.nextTick(function() {
			assert.strictEqual('', modal.element.style.display);

			modal.visible = true;
			async.nextTick(function() {
				assert.strictEqual('block', modal.element.style.display);
				done();
			});
		});
	});

	it('should show and hide overlay when overlay state changes', function(done) {
		modal = new Modal().render();
		modal.overlay = false;
		async.nextTick(function() {
			assert.ok(!modal.overlayElement.parentNode);
			modal.overlay = true;
			async.nextTick(function() {
				assert.ok(modal.overlayElement.parentNode);
				done();
			});
		});
	});

	it('should close on clicking close icon', function(done) {
		modal = new Modal({
			header: 'header'
		}).render();
		dom.triggerEvent(modal.element.querySelector('.close'), 'click');
		async.nextTick(function() {
			assert.ok(!modal.visible);
			done();
		});
	});

	it('should close modal when press the escape key', function(done) {
		modal = new Modal().render();
		dom.triggerEvent(document, 'keyup');
		async.nextTick(function() {
			assert.ok(modal.visible);
			dom.triggerEvent(document, 'keyup', {
				keyCode: 27
			});
			async.nextTick(function() {
				assert.ok(!modal.visible);
				done();
			});
		});
	});

	it('should not close modal when press escape key and the hideOnEscape state is not true', function(done) {
		modal = new Modal({
			hideOnEscape: false
		}).render();
		dom.triggerEvent(document, 'keyup', {
			keyCode: 27
		});
		async.nextTick(function() {
			assert.ok(modal.visible);
			done();
		});
	});

	it('should close modal overlay when modal closes', function(done) {
		modal = new Modal().render();
		modal.hide();
		async.nextTick(function() {
			assert.ok(!modal.overlayElement.parentNode);
			done();
		});
	});

	it('should only show modal overlay when modal is visible', function(done) {
		modal = new Modal({
			visible: false
		}).render();
		assert.ok(!modal.overlayElement.parentNode);
		modal.visible = true;
		async.nextTick(function() {
			assert.ok(modal.overlayElement.parentNode);
			done();
		});
	});

	it('should set the "role" HTML attribute to "dialog" by default', function() {
		modal = new Modal().render();
		assert.strictEqual('dialog', modal.element.getAttribute('role'));
	});

	it('should set the "role" HTML attribute to value specified by the "role" state', function() {
		modal = new Modal({
			role: 'alertdialog'
		}).render();
		assert.strictEqual('alertdialog', modal.element.getAttribute('role'));
	});

	describe('Automatic Focus', function() {
		it('should automatically focus close button', function() {
			modal = new Modal({
				header: 'My Header'
			}).render();
			assert.strictEqual(modal.element.querySelector('.close'), document.activeElement);
		});

		it('should not automatically focus any element if "autoFocus" state is set to "false"', function() {
			var prevActiveElement = document.activeElement;
			modal = new Modal({
				autoFocus: false,
				header: 'My Header'
			}).render();
			assert.strictEqual(prevActiveElement, document.activeElement);
		});

		it('should automatically focus internal element that matches selector specified by "autoFocus" state', function() {
			modal = new Modal({
				autoFocus: '.body-btn',
				body: '<button class="body-btn">Body Button</button>'
			}).render();
			assert.strictEqual(modal.element.querySelector('.body-btn'), document.activeElement);
		});

		it('should not automatically focus "autoFocus" element if modal is rendered invisible', function() {
			var prevActiveElement = document.activeElement;
			modal = new Modal({
				autoFocus: '.body-btn',
				body: '<button class="body-btn">Body Button</button>',
				visible: false
			}).render();
			assert.strictEqual(prevActiveElement, document.activeElement);
		});

		it('should automatically focus "autoFocus" element when modal becomes visible', function(done) {
			var element = document.createElement('button');
			dom.enterDocument(element);
			modal = new Modal({
				autoFocus: '.body-btn',
				body: '<button class="body-btn">Body Button</button>',
				visible: false
			}).render();

			modal.visible = true;
			modal.once('stateChanged', function() {
				assert.strictEqual(modal.element.querySelector('.body-btn'), document.activeElement);
				dom.exitDocument(element);
				done();
			});
		});
	});

	describe('Refocus Last Element', function() {
		it('should refocus the previously active element when modal is hidden', function(done) {
			var element = document.createElement('button');
			dom.enterDocument(element);
			element.focus();

			modal = new Modal({
				header: 'My Header',
				visible: false
			}).render();

			modal.visible = true;
			modal.once('stateChanged', function() {
				assert.notStrictEqual(element, document.activeElement);
				modal.visible = false;
				modal.once('stateChanged', function() {
					assert.strictEqual(element, document.activeElement);
					dom.exitDocument(element);
					done();
				});
			});
		});
	});

	describe('Restrict Focus', function() {
		var outsideElement;

		beforeEach(function() {
			outsideElement = document.createElement('button');
			dom.enterDocument(outsideElement);
		});

		afterEach(function() {
			dom.exitDocument(outsideElement);
		});

		it('should not allow focusing elements outside the modal while it\'s visible', function() {
			modal = new Modal().render();

			// Focus and call the `focus` event manually, since the test browser's window may not have
			// focus at the moment, and it's not guaranteed that the event will fire autimatically.
			outsideElement.focus();
			dom.triggerEvent(outsideElement, 'focus');
			assert.notStrictEqual(outsideElement, document.activeElement);
			assert.strictEqual(modal.element.querySelector('.modal-dialog'), document.activeElement);
		});

		it('should not restrict focusing outside modal if not visible', function() {
			modal = new Modal({
				visible: false
			}).render();

			outsideElement.focus();
			dom.triggerEvent(outsideElement, 'focus');
			assert.strictEqual(outsideElement, document.activeElement);
		});

		it('should restrict/unrestrict focusing outside modal as visibility changes', function(done) {
			modal = new Modal().render();

			modal.visible = false;
			modal.once('stateChanged', function() {
				outsideElement.focus();
				dom.triggerEvent(outsideElement, 'focus');
				assert.strictEqual(outsideElement, document.activeElement);

				modal.visible = true;
				modal.once('stateChanged', function() {
					outsideElement.focus();
					dom.triggerEvent(outsideElement, 'focus');
					assert.notStrictEqual(outsideElement, document.activeElement);
					assert.strictEqual(modal.element.querySelector('.modal-dialog'), document.activeElement);
					done();
				});
			});
		});

		it('should not restrict focusing outside modal if "overlay" is false', function() {
			modal = new Modal({
				overlay: false
			}).render();

			outsideElement.focus();
			dom.triggerEvent(outsideElement, 'focus');
			assert.strictEqual(outsideElement, document.activeElement);
		});

		it('should not restrict focusing inside modal', function() {
			modal = new Modal({
				body: '<button class="body-btn">Body Button</button>',
			}).render();

			var element = modal.element.querySelector('.body-btn');
			element.focus();
			dom.triggerEvent(element, 'focus');
			assert.strictEqual(element, document.activeElement);
		});
	});

	it('should modal progressive enchance always as hidden', function() {
		var data = {
			id: 'modal',
			elementClasses: 'centered',
			header: () => IncrementalDOM.text('header'),
			body: () => IncrementalDOM.text('body'),
			footer: () => IncrementalDOM.text('footer'),
			overlay: true,
			role: 'dialog'
		};
		var element = document.createElement('div');
		IncrementalDOM.patch(element, () => Modal.TEMPLATE(data));
		var outerHTML = element.innerHTML;

		modal = new Modal({
			element: element.childNodes[0],
			elementClasses: 'centered',
			header: 'header',
			body: 'body',
			footer: 'footer',
			visible: false
		}).decorate();

		assert.strictEqual(modal.element.outerHTML, outerHTML);
	});

	it('should change to visible when decorated and visible is true', function() {
		var data = {
			id: 'modal',
			elementClasses: 'centered',
			header: () => IncrementalDOM.text('header'),
			body: () => IncrementalDOM.text('body'),
			footer: () => IncrementalDOM.text('footer'),
			overlay: true
		};
		var element = document.createElement('div');
		IncrementalDOM.patch(element, () => Modal.TEMPLATE(data));

		modal = new Modal({
			element: element.childNodes[0],
			header: 'header',
			body: 'body',
			footer: 'footer',
			visible: true
		}).render();

		assert.ok(!dom.hasClass(modal.element, 'hidden'));
	});

	it('should handle calling template without passing "elementClasses"', function() {
		var data = {
			header: () => IncrementalDOM.text('header'),
			body: () => IncrementalDOM.text('body'),
			footer: () => IncrementalDOM.text('footer'),
			overlay: true
		};
		var element = document.createElement('div');
		IncrementalDOM.patch(element, () => Modal.TEMPLATE(data));

		element = element.childNodes[0];
		assert.strictEqual('modal', element.className);
	});

	it('should set "visible" state to false when "hide" method is called', function() {
		modal = new Modal().render();
		modal.hide();
		assert.ok(!modal.visible);
	});

	it('should set "visible" state to true when "show" method is called', function() {
		modal = new Modal({
			visible: false
		}).render();
		modal.show();
		assert.ok(modal.visible);
	});
});
<commit_msg>Replaces "decorate" call with "render"<commit_after>'use strict';

import { async } from 'metal';
import dom from 'metal-dom';
import Modal from '../src/Modal';

describe('Modal', function() {
	var modal;

	afterEach(function() {
		if (modal) {
			modal.dispose();
		}
	});

	it('should render with default state', function() {
		modal = new Modal().render();
		var header = modal.element.querySelector('.modal-header');
		var body = modal.element.querySelector('.modal-body');
		var footer = modal.element.querySelector('.modal-footer');
		assert.strictEqual('', header.innerHTML);
		assert.strictEqual('', body.innerHTML);
		assert.strictEqual('', footer.innerHTML);
		assert.strictEqual('block', modal.element.style.display);
	});

	it('should render with header only', function() {
		modal = new Modal({
			header: 'header'
		}).render();
		var headerElement = modal.element.querySelector('.modal-header');
		var body = modal.element.querySelector('.modal-body').innerHTML;
		var footer = modal.element.querySelector('.modal-footer').innerHTML;
		assert.strictEqual('header', headerElement.childNodes[1].textContent);
		assert.ok(dom.hasClass(headerElement.childNodes[0], 'close'));
		assert.strictEqual('', body);
		assert.strictEqual('', footer);
	});

	it('should render with body only', function() {
		modal = new Modal({
			body: 'body'
		}).render();
		var header = modal.element.querySelector('.modal-header').innerHTML;
		var body = modal.element.querySelector('.modal-body').innerHTML;
		var footer = modal.element.querySelector('.modal-footer').innerHTML;
		assert.strictEqual('', header);
		assert.strictEqual('body', body);
		assert.strictEqual('', footer);
	});

	it('should render with footer only', function() {
		modal = new Modal({
			footer: 'footer'
		}).render();
		var header = modal.element.querySelector('.modal-header').innerHTML;
		var body = modal.element.querySelector('.modal-body').innerHTML;
		var footer = modal.element.querySelector('.modal-footer').innerHTML;
		assert.strictEqual('', header);
		assert.strictEqual('', body);
		assert.strictEqual('footer', footer);
	});

	it('should cause dom repaint when body state changes', function(done) {
		modal = new Modal().render();
		modal.body = 'body';
		async.nextTick(function() {
			var body = modal.element.querySelector('.modal-body').innerHTML;
			assert.strictEqual('body', body);
			done();
		});
	});

	it('should cause dom repaint when header state changes', function(done) {
		modal = new Modal().render();
		modal.header = 'header';
		async.nextTick(function() {
			var header = modal.element.querySelector('.modal-header');
			assert.strictEqual('header', header.childNodes[1].textContent);
			assert.ok(dom.hasClass(header.childNodes[0], 'close'));
			done();
		});
	});

	it('should cause dom repaint when footer state changes', function(done) {
		modal = new Modal().render();
		modal.footer = 'footer';
		async.nextTick(function() {
			var footer = modal.element.querySelector('.modal-footer').innerHTML;
			assert.strictEqual('footer', footer);
			done();
		});
	});

	it('should cause dom repaint when visible state changes', function(done) {
		modal = new Modal().render();
		modal.visible = false;
		async.nextTick(function() {
			assert.strictEqual('', modal.element.style.display);

			modal.visible = true;
			async.nextTick(function() {
				assert.strictEqual('block', modal.element.style.display);
				done();
			});
		});
	});

	it('should show and hide overlay when overlay state changes', function(done) {
		modal = new Modal().render();
		modal.overlay = false;
		async.nextTick(function() {
			assert.ok(!modal.overlayElement.parentNode);
			modal.overlay = true;
			async.nextTick(function() {
				assert.ok(modal.overlayElement.parentNode);
				done();
			});
		});
	});

	it('should close on clicking close icon', function(done) {
		modal = new Modal({
			header: 'header'
		}).render();
		dom.triggerEvent(modal.element.querySelector('.close'), 'click');
		async.nextTick(function() {
			assert.ok(!modal.visible);
			done();
		});
	});

	it('should close modal when press the escape key', function(done) {
		modal = new Modal().render();
		dom.triggerEvent(document, 'keyup');
		async.nextTick(function() {
			assert.ok(modal.visible);
			dom.triggerEvent(document, 'keyup', {
				keyCode: 27
			});
			async.nextTick(function() {
				assert.ok(!modal.visible);
				done();
			});
		});
	});

	it('should not close modal when press escape key and the hideOnEscape state is not true', function(done) {
		modal = new Modal({
			hideOnEscape: false
		}).render();
		dom.triggerEvent(document, 'keyup', {
			keyCode: 27
		});
		async.nextTick(function() {
			assert.ok(modal.visible);
			done();
		});
	});

	it('should close modal overlay when modal closes', function(done) {
		modal = new Modal().render();
		modal.hide();
		async.nextTick(function() {
			assert.ok(!modal.overlayElement.parentNode);
			done();
		});
	});

	it('should only show modal overlay when modal is visible', function(done) {
		modal = new Modal({
			visible: false
		}).render();
		assert.ok(!modal.overlayElement.parentNode);
		modal.visible = true;
		async.nextTick(function() {
			assert.ok(modal.overlayElement.parentNode);
			done();
		});
	});

	it('should set the "role" HTML attribute to "dialog" by default', function() {
		modal = new Modal().render();
		assert.strictEqual('dialog', modal.element.getAttribute('role'));
	});

	it('should set the "role" HTML attribute to value specified by the "role" state', function() {
		modal = new Modal({
			role: 'alertdialog'
		}).render();
		assert.strictEqual('alertdialog', modal.element.getAttribute('role'));
	});

	describe('Automatic Focus', function() {
		it('should automatically focus close button', function() {
			modal = new Modal({
				header: 'My Header'
			}).render();
			assert.strictEqual(modal.element.querySelector('.close'), document.activeElement);
		});

		it('should not automatically focus any element if "autoFocus" state is set to "false"', function() {
			var prevActiveElement = document.activeElement;
			modal = new Modal({
				autoFocus: false,
				header: 'My Header'
			}).render();
			assert.strictEqual(prevActiveElement, document.activeElement);
		});

		it('should automatically focus internal element that matches selector specified by "autoFocus" state', function() {
			modal = new Modal({
				autoFocus: '.body-btn',
				body: '<button class="body-btn">Body Button</button>'
			}).render();
			assert.strictEqual(modal.element.querySelector('.body-btn'), document.activeElement);
		});

		it('should not automatically focus "autoFocus" element if modal is rendered invisible', function() {
			var prevActiveElement = document.activeElement;
			modal = new Modal({
				autoFocus: '.body-btn',
				body: '<button class="body-btn">Body Button</button>',
				visible: false
			}).render();
			assert.strictEqual(prevActiveElement, document.activeElement);
		});

		it('should automatically focus "autoFocus" element when modal becomes visible', function(done) {
			var element = document.createElement('button');
			dom.enterDocument(element);
			modal = new Modal({
				autoFocus: '.body-btn',
				body: '<button class="body-btn">Body Button</button>',
				visible: false
			}).render();

			modal.visible = true;
			modal.once('stateChanged', function() {
				assert.strictEqual(modal.element.querySelector('.body-btn'), document.activeElement);
				dom.exitDocument(element);
				done();
			});
		});
	});

	describe('Refocus Last Element', function() {
		it('should refocus the previously active element when modal is hidden', function(done) {
			var element = document.createElement('button');
			dom.enterDocument(element);
			element.focus();

			modal = new Modal({
				header: 'My Header',
				visible: false
			}).render();

			modal.visible = true;
			modal.once('stateChanged', function() {
				assert.notStrictEqual(element, document.activeElement);
				modal.visible = false;
				modal.once('stateChanged', function() {
					assert.strictEqual(element, document.activeElement);
					dom.exitDocument(element);
					done();
				});
			});
		});
	});

	describe('Restrict Focus', function() {
		var outsideElement;

		beforeEach(function() {
			outsideElement = document.createElement('button');
			dom.enterDocument(outsideElement);
		});

		afterEach(function() {
			dom.exitDocument(outsideElement);
		});

		it('should not allow focusing elements outside the modal while it\'s visible', function() {
			modal = new Modal().render();

			// Focus and call the `focus` event manually, since the test browser's window may not have
			// focus at the moment, and it's not guaranteed that the event will fire autimatically.
			outsideElement.focus();
			dom.triggerEvent(outsideElement, 'focus');
			assert.notStrictEqual(outsideElement, document.activeElement);
			assert.strictEqual(modal.element.querySelector('.modal-dialog'), document.activeElement);
		});

		it('should not restrict focusing outside modal if not visible', function() {
			modal = new Modal({
				visible: false
			}).render();

			outsideElement.focus();
			dom.triggerEvent(outsideElement, 'focus');
			assert.strictEqual(outsideElement, document.activeElement);
		});

		it('should restrict/unrestrict focusing outside modal as visibility changes', function(done) {
			modal = new Modal().render();

			modal.visible = false;
			modal.once('stateChanged', function() {
				outsideElement.focus();
				dom.triggerEvent(outsideElement, 'focus');
				assert.strictEqual(outsideElement, document.activeElement);

				modal.visible = true;
				modal.once('stateChanged', function() {
					outsideElement.focus();
					dom.triggerEvent(outsideElement, 'focus');
					assert.notStrictEqual(outsideElement, document.activeElement);
					assert.strictEqual(modal.element.querySelector('.modal-dialog'), document.activeElement);
					done();
				});
			});
		});

		it('should not restrict focusing outside modal if "overlay" is false', function() {
			modal = new Modal({
				overlay: false
			}).render();

			outsideElement.focus();
			dom.triggerEvent(outsideElement, 'focus');
			assert.strictEqual(outsideElement, document.activeElement);
		});

		it('should not restrict focusing inside modal', function() {
			modal = new Modal({
				body: '<button class="body-btn">Body Button</button>',
			}).render();

			var element = modal.element.querySelector('.body-btn');
			element.focus();
			dom.triggerEvent(element, 'focus');
			assert.strictEqual(element, document.activeElement);
		});
	});

	it('should modal progressive enchance always as hidden', function() {
		var data = {
			id: 'modal',
			elementClasses: 'centered',
			header: () => IncrementalDOM.text('header'),
			body: () => IncrementalDOM.text('body'),
			footer: () => IncrementalDOM.text('footer'),
			overlay: true,
			role: 'dialog'
		};
		var element = document.createElement('div');
		IncrementalDOM.patch(element, () => Modal.TEMPLATE(data));
		var outerHTML = element.innerHTML;

		modal = new Modal({
			element: element.childNodes[0],
			elementClasses: 'centered',
			header: 'header',
			body: 'body',
			footer: 'footer',
			visible: false
		}).render();

		assert.strictEqual(modal.element.outerHTML, outerHTML);
	});

	it('should change to visible when decorated and visible is true', function() {
		var data = {
			id: 'modal',
			elementClasses: 'centered',
			header: () => IncrementalDOM.text('header'),
			body: () => IncrementalDOM.text('body'),
			footer: () => IncrementalDOM.text('footer'),
			overlay: true
		};
		var element = document.createElement('div');
		IncrementalDOM.patch(element, () => Modal.TEMPLATE(data));

		modal = new Modal({
			element: element.childNodes[0],
			header: 'header',
			body: 'body',
			footer: 'footer',
			visible: true
		}).render();

		assert.ok(!dom.hasClass(modal.element, 'hidden'));
	});

	it('should handle calling template without passing "elementClasses"', function() {
		var data = {
			header: () => IncrementalDOM.text('header'),
			body: () => IncrementalDOM.text('body'),
			footer: () => IncrementalDOM.text('footer'),
			overlay: true
		};
		var element = document.createElement('div');
		IncrementalDOM.patch(element, () => Modal.TEMPLATE(data));

		element = element.childNodes[0];
		assert.strictEqual('modal', element.className);
	});

	it('should set "visible" state to false when "hide" method is called', function() {
		modal = new Modal().render();
		modal.hide();
		assert.ok(!modal.visible);
	});

	it('should set "visible" state to true when "show" method is called', function() {
		modal = new Modal({
			visible: false
		}).render();
		modal.show();
		assert.ok(modal.visible);
	});
});
<|endoftext|>


<commit_before>import { Observable as $ } from 'rx'
import { run } from '@cycle/core'

import { makeTelegramDriver, entityIs } from 'cycle-telegram'
import { matchPlugin } from 'cycle-telegram/plugins'

import { plugins } from './plugins'

let main = ({bot}) => {
  let intents = {
    uptime: bot.observable
      .first()
      .share(),

    messages: bot.events('message')
      .share(),

    inlineQuery: bot.events('inline_query')
      .share(),

    commands: bot.events('message')
      .filter(entityIs('bot_command'))
      .share()
  }

  let request = $.from([
    intents.commands::matchPlugin(plugins)
      .map(x => x.bot),
    intents.inlineQuery::matchPlugin(plugins)
      .map(x => x.bot)
  ])

  let log = $.merge(
    intents.uptime,
    intents.messages,
    intents.inlineQuery
  )

  return {
    bot: request,
    log: log
  }
}

run(main, {
  bot: makeTelegramDriver('<YOUR_TOKEN_HERE>'),
  log: (m) => m.forEach(console.log)
})
<commit_msg>fix(example): function bind<commit_after>import { Observable as $ } from 'rx'
import { run } from '@cycle/core'

import { makeTelegramDriver, entityIs } from 'cycle-telegram'
import { matchPlugin } from 'cycle-telegram/plugins'

import { plugins } from './plugins'

let main = ({bot}) => {
  let intents = {
    uptime: bot.observable
      .first()
      .share(),

    messages: bot.events('message')
      .share(),

    inlineQuery: bot.events('inline_query')
      .share(),

    commands: bot.events('message')
      .filter(entityIs('bot_command'))
      .share()
  }

  let request = $.from([
    intents.commands::matchPlugin(plugins)
      .map(x => x.bot),
    intents.inlineQuery::matchPlugin(plugins)
      .map(x => x.bot)
  ])

  let log = $.merge(
    intents.uptime,
    intents.messages,
    intents.inlineQuery
  )

  return {
    bot: request,
    log: log
  }
}

run(main, {
  bot: makeTelegramDriver('<YOUR_TOKEN_HERE>'),
  log: (m) => m.forEach(::console.log)
})
<|endoftext|>


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

let instance = null;

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

const request = require('request');
const SysManager = require('./SysManager.js');
const sysManager = new SysManager('info');

const rclient = require('../util/redis_manager.js').getRedisClient()

const bone = require("../lib/Bone.js");
const IntelTool = require('./IntelTool');
const intelTool = new IntelTool();

const A_WEEK = 3600 * 24 * 7;

/* malware, botnet, spam, phishing, malicious activity, blacklist, dnsbl */
const IGNORED_TAGS = ['dnsbl', 'spam'];

module.exports = class {
    constructor(loglevel) {
        if (instance == null) {
            instance = this;
        }
        return instance;
    }

    action(action, ip, callback) {
        if (action === "ignore") {
            rclient.hmset("intel:action:"+ip, {'ignore':true}, (err)=> {
                callback(err,null);
            });
        } else if (action === "unignore") {
            rclient.hmset("intel:action:"+ip, {'ignore':false}, (err)=> {
                callback(err,null);
            });
        } else if (action === "block") {
        } else if (action === "unblock") {
        } else if (action === "support") {
        }

      bone.intel(ip, "", action, {});
    }

    async cacheLookupAsync(dest, origin) {
        return await rclient.getAsync("cache.intel:" + origin + ":" + dest);
    }

    cachelookup(ip, origin, callback) {
        rclient.get("cache.intel:" + origin + ":" + ip, (err, result) => {
            callback(err, result);
        });
    }

    cacheAdd(ip, origin, value) {
        if (value == null) {
            value = "none";
        }
        rclient.set("cache.intel:" + origin + ":" + ip, value, (err, result) => {
          rclient.expireat("cache.intel:" + origin + ":" + ip, this.currentTime() + A_WEEK);
        });
    }

    currentTime() {
      return Math.round(Date.now() / 1000);
    }

    async lookupDomain(domain, ip, flowObj) {
      if (!domain || domain === "firewalla.com") {
        return;
      }

      if (await this.isIgnored(domain)) {
        log.info("Ignored domain:", domain, "skip...");
        return;
      }

      log.info("FlowObj:", flowObj);

      let intel = flowObj.intel;

      if (!intel.category) {
        log.info("No intel for domain", domain, "look up from cloud...");
        intel = await this._lookupDomain(domain, ip);
      } else {
        log.info("Intel for domain", domain, " exists in flowObj");
        if (intel.cc) {
          try {
            intel.cc = JSON.parse(intel.cc)[0];
          } catch (err) {
            log.warn("Error when parsing info.cc:", intel.cc, err);
          }
        }
      }
      log.info(`Intel for domain ${domain} is`, intel);

      return intel;
    }

    async isIgnored(target) {
      let data = await rclient.hgetallAsync("intel:action:" + target);
      log.info("Ignore check for domain:", target, " is", data);
      return data && data.ignore;
    }

    async _lookupDomain(domain, ip) {
      let cloudIntel;
      try {
        cloudIntel = await intelTool.checkIntelFromCloud([ip], [domain], 'out');
      } catch (err) {
        log.info("Error when check intel from cloud", err);
      }
      log.info("Cloud intel for ", domain, "is: ", cloudIntel);

      return this.processCloudIntel(cloudIntel[0]);
    }

  processCloudIntel(cloudIntel) {
    if (!cloudIntel) {
      return;
    }

    let intel = {};
    // check if the host matches the result from cloud
    // FIXME: ignore IP check because intel result from cloud does
    // NOT have "ip" all the time.
    if (cloudIntel.apps) {
      intel.apps = JSON.stringify(cloudIntel.apps);
      let keys = Object.keys(cloudIntel.apps);
      if (keys && keys[0]) {
        intel.app = keys[0];
      }
    }

    if (cloudIntel.c) {
      intel.category = cloudIntel.c;
    }

    if (cloudIntel.action && cloudIntel.action.block) {
      intel.action = "block"
    }

    if (cloudIntel.s) {
      intel.s = cloudIntel.s;
    }

    if (cloudIntel.t) {
      intel.t = cloudIntel.t;
    }

    if (cloudIntel.cc) {
      try {
        cloudIntel.cc = JSON.parse(cloudIntel.cc);
        intel.cc = cloudIntel.cc[0];
      } catch (err) {
        intel.cc = cloudIntel.cc;
        log.warn("Error when parsing info.cc:", cloudIntel.cc, err);
      }
    }
    return intel;
  }

  lookup(ip, intel, callback) {
        if (!ip || ip === "8.8.8.8" || sysManager.isLocalIP(ip)) {
            callback(null, null, null);
            return;
        }

        rclient.hgetall("intel:action:"+ip, (err,data) => {
            if (data && data.ignore) {
                log.info("Intel:Lookup:Ignored",ip);
                callback(null,null,null);
            }

            this.cachelookup(ip, "cymon", (err, result) => {
                if (result && result !== "none") {
                    this._location(ip, (err, lobj)=>{
                        let obj = JSON.parse(result);
                        if (!obj || obj.count === 0) {
                            obj = {}
                            obj.lobj = lobj;
                            obj = this._packageIntel(ip, obj, intel);
                            callback(null, obj);
                        } else {
                            obj.lobj = lobj;
                            log.info("Intel:Location",ip,obj.lobj);
                            this._packageCymon(ip, obj);
                            callback(err, obj);
                        }
                    });
                } else {
                    this._lookup(ip, intel, (err, obj)=>{
                        callback(err,obj);
                    });
                }
            });
        });
    }

    _packageIntel(ip, obj, intel) {
        let weburl = "https://intel.firewalla.com/";
        log.info("IntelManger:PackageIntel:",ip,JSON.stringify(intel,null,2));
        if (intel == null) {
            return null;
        }
        if (intel.t) {
            obj.count = Math.abs(intel.t/10);
        } else {
            obj.count = 4;
        }
        if (intel.s) {
            obj.severityscore = intel.s;
        } else {
            obj.severityscore = 20;
        }
        obj.summary = "";
        obj.weburl = weburl;
        if (intel.cc) {
            try {
                obj.tags = JSON.parse(intel.cc);
            } catch(e) {
            }
        }
        log.info("IntelManger:PackageIntel:Done",ip,JSON.stringify(intel,null,2),JSON.stringify(obj,null,2));
        return obj;
    }

    _packageCymon(ip, obj) {
        let weburl = "https://cymon.io/" + ip;
        log.info("INFO:------ Intel Information", obj.count);

        let results = obj.results.filter(x => !IGNORED_TAGS.includes(x.tag));
        obj.count = results.length;
        let summary = obj.count + " reported this IP.\n";

        let tags = {};
        for (let r of obj.results) {
            if (r.tag) {
                if (tags[r.tag] == null) {
                    tags[r.tag] = {
                        tag: r.tag,
                        count: 1
                    };
                } else {
                    tags[r.tag].count += 1;
                }
            }
        }

        let tagArray = [], tagCount = 0, severity = 0;
        for (let i in tags) {
            let tag = tags[i];
            tagArray.push(tag);
            if (i.includes("malicious")) {
              severity += tag.count * 3;
            } else if (i.includes("malware")) {
              severity += tag.count * 3;
            } else if (i == "blacklist") {
              severity += tag.count * 3;
            } else {
              severity += 1;
            }
            tagCount += tag.count;
        }

        tagArray.sort((a, b) => b.count - a.count);

        obj.severityscore = severity;
        obj.summary = summary;
        obj.weburl = weburl;
        obj.tags = tagArray;

        if (obj.tags && obj.tags.length > 0) {
          const reasonize = (tag) => `${tag.tag} - ${Math.round(tag.count / tagCount * 100)}%`;
          let reason = "Possibility: ";
            let first = true;
            for (let tag of obj.tags) {
                if (first) {
                    reason += reasonize(tag);
                    first = false;
                } else {
                    reason += ", " + reasonize(tag);
                }
            }
            obj.reason = reason;
        }
    }

    /* curl ipinfo.io/98.124.243.43/
{
  "ip": "98.124.243.43",
  "hostname": "No Hostname",
  "city": "Kirkland",
  "region": "Washington",
  "country": "US",
  "loc": "47.6727,-122.1873",
  "org": "AS21740 eNom, Incorporated",
  "postal": "98033"
 */
    _location(ip, callback) {
      log.info("Looking up location:",ip);
      this.cachelookup(ip, "ipinfo", (err,data)=>{
        if (data!=null) {
            callback(null, JSON.parse(data));
            return;
        }
        let weburl = "https://ipinfo.io/" + ip;

        var options = {
            uri: weburl,
            method: 'GET',
            family: 4
            // Authorization: 'Token dc30fcd03eddbd95b90bacaea5e5a44b1b60d2f5',
        };

        request(options, (err, httpResponse, body) => {
            if (err != null) {
                let stack = new Error().stack;
                log.info("Error while requesting ", err, stack);
                callback(err, null, null);
                return;
            }
            if (httpResponse == null) {
                let stack = new Error().stack;
                log.info("Error while response ", err, stack);
                callback(500, null, null);
                return;
            }
            if (httpResponse.statusCode < 200 ||
                httpResponse.statusCode > 299) {
                log.error("**** Error while response HTTP ", httpResponse.statusCode);
                callback(httpResponse.statusCode, null, null);
                return;
            }
            if (err === null && body != null) {
                this.cacheAdd(ip, "ipinfo", body);
                let obj = JSON.parse(body);
                if (obj != null) {
                    callback(null,obj);
                } else {
                    callback(null,null);
                }
            }
        });
      });
    }

    _lookup(ip, intel, callback) {
        let url = "https://cymon.io/api/nexus/v1/ip/" + ip + "/events?limit=100";

        let options = {
            uri: url,
            method: 'GET',
            family: 4
        };

        this._location(ip, (err,lobj)=>{
            let obj = {lobj};
            log.info("Intel:Location",ip,lobj);
            obj = this._packageIntel(ip,obj,intel);
            request(options, (err, resp, body) => {
                if (err) {
                    log.info(`Error while requesting ${url}`, err);
                    callback(err, null, null);
                    return;
                }
                if (!resp) {
                    log.info("Error while response ", err);
                    callback(500, null, null);
                    return;
                }
                if (resp.statusCode < 200 || resp.statusCode > 299) {
                    log.error("**** Error while response HTTP ", resp.statusCode);
                    callback(resp.statusCode, null, null);
                    return;
                }
                if (body) {
                    this.cacheAdd(ip, "cymon", body);
                    let cobj = JSON.parse(body);
                    if (cobj) {
                        if (cobj.count === 0) {
                            log.info("INFO:====== No Intel Information!!", ip, obj);
                            callback(null,obj);
                        } else {
                            cobj.lobj = lobj;
                            this._packageCymon(ip, cobj);
                            callback(err, cobj, cobj.weburl);
                        }
                    } else {
                        callback(null,obj);
                    }
                } else {
                    callback(null,obj);
                }
            });
        });
    }
}
<commit_msg>formatting improvement<commit_after>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

let instance = null;

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

const request = require('request');
const SysManager = require('./SysManager.js');
const sysManager = new SysManager('info');

const rclient = require('../util/redis_manager.js').getRedisClient()

const bone = require("../lib/Bone.js");
const IntelTool = require('./IntelTool');
const intelTool = new IntelTool();

const A_WEEK = 3600 * 24 * 7;

/* malware, botnet, spam, phishing, malicious activity, blacklist, dnsbl */
const IGNORED_TAGS = ['dnsbl', 'spam'];

module.exports = class {
    constructor(loglevel) {
        if (instance == null) {
            instance = this;
        }
        return instance;
    }

    action(action, ip, callback) {
        if (action === "ignore") {
            rclient.hmset("intel:action:"+ip, {'ignore':true}, (err)=> {
                callback(err,null);
            });
        } else if (action === "unignore") {
            rclient.hmset("intel:action:"+ip, {'ignore':false}, (err)=> {
                callback(err,null);
            });
        } else if (action === "block") {
        } else if (action === "unblock") {
        } else if (action === "support") {
        }

      bone.intel(ip, "", action, {});
    }

    async cacheLookupAsync(dest, origin) {
        return await rclient.getAsync("cache.intel:" + origin + ":" + dest);
    }

    cachelookup(ip, origin, callback) {
        rclient.get("cache.intel:" + origin + ":" + ip, (err, result) => {
            callback(err, result);
        });
    }

    cacheAdd(ip, origin, value) {
        if (value == null) {
            value = "none";
        }
        rclient.set("cache.intel:" + origin + ":" + ip, value, (err, result) => {
          rclient.expireat("cache.intel:" + origin + ":" + ip, this.currentTime() + A_WEEK);
        });
    }

    currentTime() {
      return Math.round(Date.now() / 1000);
    }

    async lookupDomain(domain, ip, flowObj) {
      if (!domain || domain === "firewalla.com") {
        return;
      }

      if (await this.isIgnored(domain)) {
        log.info("Ignored domain:", domain, "skip...");
        return;
      }

      log.info("FlowObj:", flowObj);

      let intel = flowObj.intel;

      if (!intel.category) {
        log.info("No intel for domain", domain, "look up from cloud...");
        intel = await this._lookupDomain(domain, ip);
      } else {
        log.info("Intel for domain", domain, " exists in flowObj");
        if (intel.cc) {
          try {
            intel.cc = JSON.parse(intel.cc)[0];
          } catch (err) {
            log.warn("Error when parsing info.cc:", intel.cc, err);
          }
        }
      }
      log.info(`Intel for domain ${domain} is`, intel);

      return intel;
    }

    async isIgnored(target) {
      let data = await rclient.hgetallAsync("intel:action:" + target);
      log.info("Ignore check for domain:", target, " is", data);
      return data && data.ignore;
    }

    async _lookupDomain(domain, ip) {
      let cloudIntel;
      try {
        cloudIntel = await intelTool.checkIntelFromCloud([ip], [domain], 'out');
      } catch (err) {
        log.info("Error when check intel from cloud", err);
      }
      log.info("Cloud intel for ", domain, "is: ", cloudIntel);

      return this.processCloudIntel(cloudIntel[0]);
    }

  processCloudIntel(cloudIntel) {
    if (!cloudIntel) {
      return;
    }

    let intel = {};
    // check if the host matches the result from cloud
    // FIXME: ignore IP check because intel result from cloud does
    // NOT have "ip" all the time.
    if (cloudIntel.apps) {
      intel.apps = JSON.stringify(cloudIntel.apps);
      let keys = Object.keys(cloudIntel.apps);
      if (keys && keys[0]) {
        intel.app = keys[0];
      }
    }

    if (cloudIntel.c) {
      intel.category = cloudIntel.c;
    }

    if (cloudIntel.action && cloudIntel.action.block) {
      intel.action = "block"
    }

    if (cloudIntel.s) {
      intel.s = cloudIntel.s;
    }

    if (cloudIntel.t) {
      intel.t = cloudIntel.t;
    }

    if (cloudIntel.cc) {
      try {
        cloudIntel.cc = JSON.parse(cloudIntel.cc);
        intel.cc = cloudIntel.cc[0];
      } catch (err) {
        intel.cc = cloudIntel.cc;
        log.warn("Error when parsing info.cc:", cloudIntel.cc, err);
      }
    }
    return intel;
  }

    lookup(ip, intel, callback) {
      if (!ip || ip === "8.8.8.8" || sysManager.isLocalIP(ip)) {
        callback(null, null, null);
        return;
      }

      rclient.hgetall("intel:action:" + ip, (err, data) => {
        if (data && data.ignore) {
          log.info("Intel:Lookup:Ignored", ip);
          callback(null, null, null);
        }

        this.cachelookup(ip, "cymon", (err, result) => {
          if (result && result !== "none") {
            this._location(ip, (err, lobj) => {
              let obj = JSON.parse(result);
              if (!obj || obj.count === 0) {
                obj = {}
                obj.lobj = lobj;
                obj = this._packageIntel(ip, obj, intel);
                callback(null, obj);
              } else {
                obj.lobj = lobj;
                log.info("Intel:Location", ip, obj.lobj);
                this._packageCymon(ip, obj);
                callback(err, obj);
              }
            });
          } else {
            this._lookup(ip, intel, (err, obj) => {
              callback(err, obj);
            });
          }
        });
      });
    }

    _packageIntel(ip, obj, intel) {
        let weburl = "https://intel.firewalla.com/";
        log.info("IntelManger:PackageIntel:",ip,JSON.stringify(intel,null,2));
        if (intel == null) {
            return null;
        }
        if (intel.t) {
            obj.count = Math.abs(intel.t/10);
        } else {
            obj.count = 4;
        }
        if (intel.s) {
            obj.severityscore = intel.s;
        } else {
            obj.severityscore = 20;
        }
        obj.summary = "";
        obj.weburl = weburl;
        if (intel.cc) {
            try {
                obj.tags = JSON.parse(intel.cc);
            } catch(e) {
            }
        }
        log.info("IntelManger:PackageIntel:Done",ip,JSON.stringify(intel,null,2),JSON.stringify(obj,null,2));
        return obj;
    }

    _packageCymon(ip, obj) {
        let weburl = "https://cymon.io/" + ip;
        log.info("INFO:------ Intel Information", obj.count);

        let results = obj.results.filter(x => !IGNORED_TAGS.includes(x.tag));
        obj.count = results.length;
        let summary = obj.count + " reported this IP.\n";

        let tags = {};
        for (let r of obj.results) {
            if (r.tag) {
                if (tags[r.tag] == null) {
                    tags[r.tag] = {
                        tag: r.tag,
                        count: 1
                    };
                } else {
                    tags[r.tag].count += 1;
                }
            }
        }

        let tagArray = [], tagCount = 0, severity = 0;
        for (let i in tags) {
            let tag = tags[i];
            tagArray.push(tag);
            if (i.includes("malicious")) {
              severity += tag.count * 3;
            } else if (i.includes("malware")) {
              severity += tag.count * 3;
            } else if (i == "blacklist") {
              severity += tag.count * 3;
            } else {
              severity += 1;
            }
            tagCount += tag.count;
        }

        tagArray.sort((a, b) => b.count - a.count);

        obj.severityscore = severity;
        obj.summary = summary;
        obj.weburl = weburl;
        obj.tags = tagArray;

        if (obj.tags && obj.tags.length > 0) {
          const reasonize = (tag) => `${tag.tag} - ${Math.round(tag.count / tagCount * 100)}%`;
          let reason = "Possibility: ";
            let first = true;
            for (let tag of obj.tags) {
                if (first) {
                    reason += reasonize(tag);
                    first = false;
                } else {
                    reason += ", " + reasonize(tag);
                }
            }
            obj.reason = reason;
        }
    }

    /* curl ipinfo.io/98.124.243.43/
{
  "ip": "98.124.243.43",
  "hostname": "No Hostname",
  "city": "Kirkland",
  "region": "Washington",
  "country": "US",
  "loc": "47.6727,-122.1873",
  "org": "AS21740 eNom, Incorporated",
  "postal": "98033"
 */
    _location(ip, callback) {
      log.info("Looking up location:",ip);
      this.cachelookup(ip, "ipinfo", (err,data)=>{
        if (data!=null) {
            callback(null, JSON.parse(data));
            return;
        }
        let weburl = "https://ipinfo.io/" + ip;

        var options = {
            uri: weburl,
            method: 'GET',
            family: 4
            // Authorization: 'Token dc30fcd03eddbd95b90bacaea5e5a44b1b60d2f5',
        };

        request(options, (err, httpResponse, body) => {
            if (err != null) {
                let stack = new Error().stack;
                log.info("Error while requesting ", err, stack);
                callback(err, null, null);
                return;
            }
            if (httpResponse == null) {
                let stack = new Error().stack;
                log.info("Error while response ", err, stack);
                callback(500, null, null);
                return;
            }
            if (httpResponse.statusCode < 200 ||
                httpResponse.statusCode > 299) {
                log.error("**** Error while response HTTP ", httpResponse.statusCode);
                callback(httpResponse.statusCode, null, null);
                return;
            }
            if (err === null && body != null) {
                this.cacheAdd(ip, "ipinfo", body);
                let obj = JSON.parse(body);
                if (obj != null) {
                    callback(null,obj);
                } else {
                    callback(null,null);
                }
            }
        });
      });
    }

    _lookup(ip, intel, callback) {
        let url = "https://cymon.io/api/nexus/v1/ip/" + ip + "/events?limit=100";

        let options = {
            uri: url,
            method: 'GET',
            family: 4
        };

        this._location(ip, (err,lobj)=>{
            let obj = {lobj};
            log.info("Intel:Location",ip,lobj);
            obj = this._packageIntel(ip,obj,intel);
            request(options, (err, resp, body) => {
                if (err) {
                    log.info(`Error while requesting ${url}`, err);
                    callback(err, null, null);
                    return;
                }
                if (!resp) {
                    log.info("Error while response ", err);
                    callback(500, null, null);
                    return;
                }
                if (resp.statusCode < 200 || resp.statusCode > 299) {
                    log.error("**** Error while response HTTP ", resp.statusCode);
                    callback(resp.statusCode, null, null);
                    return;
                }
                if (body) {
                    this.cacheAdd(ip, "cymon", body);
                    let cobj = JSON.parse(body);
                    if (cobj) {
                        if (cobj.count === 0) {
                            log.info("INFO:====== No Intel Information!!", ip, obj);
                            callback(null,obj);
                        } else {
                            cobj.lobj = lobj;
                            this._packageCymon(ip, cobj);
                            callback(err, cobj, cobj.weburl);
                        }
                    } else {
                        callback(null,obj);
                    }
                } else {
                    callback(null,obj);
                }
            });
        });
    }
}
<|endoftext|>