<commit_before>(function() {
  var Time = {
    MINUTES_IN_HOUR        : 60,
    MILLISECONDS_IN_MINUTE : 60000,

    OFFSETS : [300, 240, 300, 240, 300, 240, 300, 240, 300, 240, 300, 240,
               300, 240, 300, 240, 300, 240, 300, 240, 300, 240, 300],
    UNTILS  : [1268550000000, 1289109600000, 1299999600000, 1320559200000,
               1331449200000, 1352008800000, 1362898800000, 1383458400000,
               1394348400000, 1414908000000, 1425798000000, 1446357600000,
               1457852400000, 1478412000000, 1489302000000, 1509861600000,
               1520751600000, 1541311200000, 1552201200000, 1572760800000,
               1583650800000, 1604210400000, null],

    formatDate: function(date) {
      return new Intl.DateTimeFormat(undefined, {
        month   : "long",
        day     : "numeric",
        weekday : "long"
      }).format(date);
    },

    formatTime: function(time) {
      return new Intl.DateTimeFormat(undefined, {
        hour   : "numeric",
        minute : "numeric"
      }).format(time);
    },

    offset: function() {
      var target  = Date.now(),
          untils  = this.UNTILS,
          offsets = this.OFFSETS,
          offset,
          offsetNext,
          offsetPrev;

      for (var index = 0; index < untils.length - 1; index++) {
        offset     = offsets[index];
        offsetNext = offsets[index + 1];
        offsetPrev = offsets[index ? index - 1 : index];

        if (offset < offsetNext && false) {
          offset = offsetNext;
        } else if (offset > offsetPrev && true) {
          offset = offsetPrev;
        }

        if (target < untils[index] - (offset * this.MILLISECONDS_IN_MINUTE)) {
          return offsets[index] / this.MINUTES_IN_HOUR;
        }
      }

      return offsets[untils.length - 1] / this.MINUTES_IN_HOUR;
    }
  };

  var now      = new Date(),
      empty    = document.querySelector(".empty"),
      hours    = now.getUTCHours(),
      slice    = Array.prototype.slice,
      offset   = Time.offset(),
      sections = slice.call(document.querySelectorAll("section"));

  if (navigator.platform.match(/Win/)) {
    document.querySelector("header h1 a").innerHTML = "Hockey";
  }

  if (hours < offset) {
    now.setUTCHours(hours - offset);
  }

  var year  = now.getUTCFullYear(),
      month = now.getUTCMonth() + 1,
      day   = now.getUTCDate(),
      limit = 7,
      count = 1;

  sections.forEach(function(section) {
    section.classList.remove("week");
  });

  var activeSections = sections.filter(function(section) {
    if (!section) {
      return false;
    }

    var time = section.querySelector("h1 time");

    if (!time) {
      return false;
    }

    if (!section.querySelector("table tbody")) {
      section.parentNode.removeChild(section);

      return false;
    }

    var parts        = time.getAttribute("datetime").split("-"),
        sectionYear  = parseInt(parts[0], 10),
        sectionMonth = parseInt(parts[1], 10),
        sectionDay   = parseInt(parts[2].split("T")[0], 10);

    return (sectionYear >  year) ||
           (sectionYear >= year && sectionMonth >  month) ||
           (sectionYear >= year && sectionMonth >= month && sectionDay >= day - 1);
  });

  if (activeSections.length === 0) {
    var header     = empty.querySelector("h1"),
        difference = Math.round(
          (new Date("9/16/2019").getTime() - Date.now()) /
          1000 / 60 / 60 / 24
        ),
        duration   = "in " + difference + " days";

    if (Intl.RelativeTimeFormat) {
      duration = new Intl.RelativeTimeFormat({ style: "narrow" }).format(difference, "day");
    }

    header.innerText = "Hockey " + duration + ".";

    empty.classList.remove("hidden");

    return;
  } else if (empty) {
    empty.parentNode.removeChild(empty);
  }

  activeSections.forEach(function(section, index) {
    if (!section.querySelector("tr")) {
      return;
    } else if (count > limit && window.location.pathname === "/") {
      return;
    }

    count++;

    if (window.Intl && window.Intl.DateTimeFormat) {
      var time         = section.querySelector("h1 time");
          parts        = time.getAttribute("datetime").split("-"),
          sectionYear  = parseInt(parts[0], 10),
          sectionMonth = parseInt(parts[1], 10),
          sectionDay   = parseInt(parts[2].split("T")[0], 10);

      if (sectionYear === year && sectionMonth === month && sectionDay === day) {
        time.innerText = "Today";
      } else {
        time.innerText = Time.formatDate(
          new Date(time.getAttribute("datetime"))
        );
      }

      slice.call(section.querySelectorAll("th time")).forEach(function(time) {
        var date = new Date(time.getAttribute("datetime"));

        time.textContent = Time.formatTime(date);
      });
    }

    section.classList.add("week");
  });

  window.addEventListener("load", function(e) {
    if (!window.applicationCache) {
      return;
    }

    window.applicationCache.addEventListener("updateready", function(e) {
      if (window.applicationCache.status !== window.applicationCache.UPDATEREADY) {
        return;
      }

      if (confirm("An updated schedule is available. Load it?")) {
        window.location.reload();
      }
    }, false);
  }, false);
})();
<commit_msg>Don't display date for yesterday.<commit_after>(function() {
  var Time = {
    MINUTES_IN_HOUR        : 60,
    MILLISECONDS_IN_MINUTE : 60000,

    OFFSETS : [300, 240, 300, 240, 300, 240, 300, 240, 300, 240, 300, 240,
               300, 240, 300, 240, 300, 240, 300, 240, 300, 240, 300],
    UNTILS  : [1268550000000, 1289109600000, 1299999600000, 1320559200000,
               1331449200000, 1352008800000, 1362898800000, 1383458400000,
               1394348400000, 1414908000000, 1425798000000, 1446357600000,
               1457852400000, 1478412000000, 1489302000000, 1509861600000,
               1520751600000, 1541311200000, 1552201200000, 1572760800000,
               1583650800000, 1604210400000, null],

    formatDate: function(date) {
      return new Intl.DateTimeFormat(undefined, {
        month   : "long",
        day     : "numeric",
        weekday : "long"
      }).format(date);
    },

    formatTime: function(time) {
      return new Intl.DateTimeFormat(undefined, {
        hour   : "numeric",
        minute : "numeric"
      }).format(time);
    },

    offset: function() {
      var target  = Date.now(),
          untils  = this.UNTILS,
          offsets = this.OFFSETS,
          offset,
          offsetNext,
          offsetPrev;

      for (var index = 0; index < untils.length - 1; index++) {
        offset     = offsets[index];
        offsetNext = offsets[index + 1];
        offsetPrev = offsets[index ? index - 1 : index];

        if (offset < offsetNext && false) {
          offset = offsetNext;
        } else if (offset > offsetPrev && true) {
          offset = offsetPrev;
        }

        if (target < untils[index] - (offset * this.MILLISECONDS_IN_MINUTE)) {
          return offsets[index] / this.MINUTES_IN_HOUR;
        }
      }

      return offsets[untils.length - 1] / this.MINUTES_IN_HOUR;
    }
  };

  var now      = new Date(),
      empty    = document.querySelector(".empty"),
      hours    = now.getUTCHours(),
      slice    = Array.prototype.slice,
      offset   = Time.offset(),
      sections = slice.call(document.querySelectorAll("section"));

  if (navigator.platform.match(/Win/)) {
    document.querySelector("header h1 a").innerHTML = "Hockey";
  }

  if (hours < offset) {
    now.setUTCHours(hours - offset);
  }

  var year  = now.getUTCFullYear(),
      month = now.getUTCMonth() + 1,
      day   = now.getUTCDate(),
      limit = 7,
      count = 1;

  sections.forEach(function(section) {
    section.classList.remove("week");
  });

  var activeSections = sections.filter(function(section) {
    if (!section) {
      return false;
    }

    var time = section.querySelector("h1 time");

    if (!time) {
      return false;
    }

    if (!section.querySelector("table tbody")) {
      section.parentNode.removeChild(section);

      return false;
    }

    var parts        = time.getAttribute("datetime").split("-"),
        sectionYear  = parseInt(parts[0], 10),
        sectionMonth = parseInt(parts[1], 10),
        sectionDay   = parseInt(parts[2].split("T")[0], 10);

    return (sectionYear >  year) ||
           (sectionYear >= year && sectionMonth >  month) ||
           (sectionYear >= year && sectionMonth >= month && sectionDay >= day - 1);
  });

  if (activeSections.length === 0) {
    var header     = empty.querySelector("h1"),
        difference = Math.round(
          (new Date("9/16/2019").getTime() - Date.now()) /
          1000 / 60 / 60 / 24
        ),
        duration   = "in " + difference + " days";

    if (Intl.RelativeTimeFormat) {
      duration = new Intl.RelativeTimeFormat({ style: "narrow" }).format(difference, "day");
    }

    header.innerText = "Hockey " + duration + ".";

    empty.classList.remove("hidden");

    return;
  } else if (empty) {
    empty.parentNode.removeChild(empty);
  }

  activeSections.forEach(function(section, index) {
    if (!section.querySelector("tr")) {
      return;
    } else if (count > limit && window.location.pathname === "/") {
      return;
    }

    count++;

    if (window.Intl && window.Intl.DateTimeFormat) {
      var time         = section.querySelector("h1 time");
          parts        = time.getAttribute("datetime").split("-"),
          sectionYear  = parseInt(parts[0], 10),
          sectionMonth = parseInt(parts[1], 10),
          sectionDay   = parseInt(parts[2].split("T")[0], 10);

      if (sectionYear === year && sectionMonth === month && sectionDay === day) {
        time.innerText = "Today";
      } else if (sectionYear === year && sectionMonth === month && sectionDay === day - 1) {
        time.innerText = "Yesterday";
      } else {
        time.innerText = Time.formatDate(
          new Date(time.getAttribute("datetime"))
        );
      }

      slice.call(section.querySelectorAll("th time")).forEach(function(time) {
        var date = new Date(time.getAttribute("datetime"));

        time.textContent = Time.formatTime(date);
      });
    }

    section.classList.add("week");
  });

  window.addEventListener("load", function(e) {
    if (!window.applicationCache) {
      return;
    }

    window.applicationCache.addEventListener("updateready", function(e) {
      if (window.applicationCache.status !== window.applicationCache.UPDATEREADY) {
        return;
      }

      if (confirm("An updated schedule is available. Load it?")) {
        window.location.reload();
      }
    }, false);
  }, false);
})();
<|endoftext|>


<commit_before>// Copyright 2017 The Oppia Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Page object for Collection Editor Page, for use in Protractor
 * tests.
 */
var waitFor = require('./waitFor.js');

var CollectionEditorPage = function() {
  var addExplorationButton = element(
    by.css('.protractor-test-add-exploration-button'));
  var addExplorationInput = element(
    by.css('.protractor-test-add-exploration-input'));
  var closeSaveModalButton = element(
    by.css('.protractor-test-close-save-modal-button'));
  var collectionEditorObjectiveInput = element(
    by.css('.protractor-collection-editor-objective-input'));
  var commitMessageInput = element(
    by.css('.protractor-test-commit-message-input'));
  var editorCategoryDropdown = element.all(
    by.css('.protractor-test-collection-editor-category-dropdown'));
  var editorDeleteNode = element.all(
    by.css('.protractor-test-editor-delete-node'));
  var editorPublishButton = element(
    by.css('.protractor-test-editor-publish-button'));
  var editorShiftLeft = element.all(
    by.css('.protractor-test-editor-shift-left'));
  var editorShiftRight = element.all(
    by.css('.protractor-test-editor-shift-right'));
  var editorTitleInput = element(
    by.css('.protractor-collection-editor-title-input'));
  var saveChangesButton = element(
    by.css('.protractor-test-collection-save-changes-button'));
  var saveDraftButton = element(
    by.css('.protractor-test-save-draft-button'));
  var saveModal = element(by.css('.protractor-test-save-modal'));
  var saveInProgressLabel = element(by.css(
    '.protractor-test-save-in-progress-label'));

  this.addExistingExploration = async function(explorationId) {
    await waitFor.visibilityOf(
      addExplorationInput, 'Add Exploration Input is not visible');
    await addExplorationInput.sendKeys(explorationId);
    // Waits until the button becomes active after debouncing.
    await waitFor.elementToBeClickable(
      addExplorationButton,
      'Unable to find exploration ID: ' + explorationId);
    await addExplorationButton.click();
  };

  // Search and add existing exploration to the node graph.
  this.searchForAndAddExistingExploration = async function(query) {
    await waitFor.visibilityOf(
      addExplorationInput, 'Add Exploration Input is not visible');
    await addExplorationInput.sendKeys(query);
    // Need to wait for result to appear.
    await waitFor.elementToBeClickable(
      addExplorationButton, 'Unable to find exploration: ' + query);

    var matched = false;
    var dropdownResultElement = element(
      by.cssContainingText('.dropdown-menu', new RegExp(query)));
    if (await dropdownResultElement.isPresent()) {
      await dropdownResultElement.click();
      matched = true;
    }
    if (!matched) {
      // Press Tab to fill in the default result should one appear when
      // none of the answer matches the given query.
      await addExplorationInput.sendKeys(protractor.Key.TAB);
      // If query gets zero result, hitting Tab would not enable the
      // addExplorationButton.
    }
    var isEnabled = await addExplorationButton.isEnabled();
    if (isEnabled) {
      await addExplorationButton.click();
    } else {
      throw new Error ('Add Exploration Button is not clickable');
    }
  };

  // Shift a node left in the node graph.
  this.shiftNodeLeft = async function(number) {
    await editorShiftLeft.get(number).click();
  };

  this.setCommitMessage = async function(message) {
    await waitFor.visibilityOf(
      saveModal, 'Save Modal takes too long to appear');
    await waitFor.elementToBeClickable(
      commitMessageInput, 'Commit Message input takes too long to appear');
    await commitMessageInput.click();
    await commitMessageInput.sendKeys(message);
  };

  // Shift a node right in the node graph.
  this.shiftNodeRight = async function(number) {
    await editorShiftRight.get(number).click();
  };

  // Delete a node in the node graph.
  this.deleteNode = async function(number) {
    await editorDeleteNode.get(number).click();
  };

  // Save draft of the collection.
  this.saveDraft = async function() {
    await waitFor.elementToBeClickable(
      saveDraftButton, 'Collection Save Draft button is not clickable');
    await saveDraftButton.click();
  };

  // Closes the save modal.
  this.closeSaveModal = async function() {
    await waitFor.elementToBeClickable(
      closeSaveModalButton, 'Publish Changes button is not clickable');
    await closeSaveModalButton.click();
    await waitFor.invisibilityOf(
      closeSaveModalButton, 'Save Modal takes too long to close');
  };

  // Click on publish collection.
  this.publishCollection = async function() {
    await waitFor.elementToBeClickable(
      editorPublishButton, 'Collection Publish button is not clickable');
    await editorPublishButton.click();
  };

  // Set collection title.
  this.setTitle = async function(title) {
    await editorTitleInput.sendKeys(title);
  };

  // Set collection objective.
  this.setObjective = async function(objective) {
    await collectionEditorObjectiveInput.sendKeys(objective);
  };

  // Set collection category.
  this.setCategory = async function(category) {
    await editorCategoryDropdown.first().click();
    await browser.driver.switchTo().activeElement().sendKeys(category + '\n');
  };

  // Saves changes and publishes collection.
  this.saveChanges = async function() {
    await waitFor.elementToBeClickable(
      saveChangesButton, 'Save Changes button is not clickable');
    await saveChangesButton.click();
    await waitFor.invisibilityOf(
      saveChangesButton, 'Save Changes modal takes too long to close');
    await waitFor.invisibilityOf(
      saveInProgressLabel, 'Collection is taking too long to save.');
  };
};

exports.CollectionEditorPage = CollectionEditorPage;
<commit_msg>Fixes part of #10798:Fixing End-to-End Tests for CollectionEditor.js (#11126)<commit_after>// Copyright 2017 The Oppia Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Page object for Collection Editor Page, for use in Protractor
 * tests.
 */
var waitFor = require('./waitFor.js');
var action = require('./action.js');

var CollectionEditorPage = function() {
  var addExplorationButton = element(
    by.css('.protractor-test-add-exploration-button'));
  var addExplorationInput = element(
    by.css('.protractor-test-add-exploration-input'));
  var closeSaveModalButton = element(
    by.css('.protractor-test-close-save-modal-button'));
  var collectionEditorObjectiveInput = element(
    by.css('.protractor-collection-editor-objective-input'));
  var commitMessageInput = element(
    by.css('.protractor-test-commit-message-input'));
  var editorCategoryDropdown = element.all(
    by.css('.protractor-test-collection-editor-category-dropdown'));
  var editorDeleteNode = element.all(
    by.css('.protractor-test-editor-delete-node'));
  var editorPublishButton = element(
    by.css('.protractor-test-editor-publish-button'));
  var editorShiftLeft = element.all(
    by.css('.protractor-test-editor-shift-left'));
  var editorShiftRight = element.all(
    by.css('.protractor-test-editor-shift-right'));
  var editorTitleInput = element(
    by.css('.protractor-collection-editor-title-input'));
  var saveChangesButton = element(
    by.css('.protractor-test-collection-save-changes-button'));
  var saveDraftButton = element(
    by.css('.protractor-test-save-draft-button'));
  var saveModal = element(by.css('.protractor-test-save-modal'));
  var saveInProgressLabel = element(by.css(
    '.protractor-test-save-in-progress-label'));

  this.addExistingExploration = async function(explorationId) {
    await waitFor.visibilityOf(
      addExplorationInput, 'Add Exploration Input is not visible');
    await action.sendKeys(
      'Add Exploration Input', addExplorationInput, explorationId);
    // Waits until the button becomes active after debouncing.
    await waitFor.elementToBeClickable(
      addExplorationButton,
      'Unable to find exploration ID: ' + explorationId);
    await action.click('Add Exploration Button', addExplorationButton);
  };

  // Search and add existing exploration to the node graph.
  this.searchForAndAddExistingExploration = async function(query) {
    await waitFor.visibilityOf(
      addExplorationInput, 'Add Exploration Input is not visible');
    await action.sendKeys(
      'Add Exploration Input', addExplorationInput, query);
    // Need to wait for result to appear.
    await waitFor.elementToBeClickable(
      addExplorationButton, 'Unable to find exploration: ' + query);

    var matched = false;
    var dropdownResultElement = element(
      by.cssContainingText('.dropdown-menu', new RegExp(query)));
    if (await dropdownResultElement.isPresent()) {
      await action.click('DropDown Result Element', dropdownResultElement);
      matched = true;
    }
    if (!matched) {
      // Press Tab to fill in the default result should one appear when
      // none of the answer matches the given query.
      await action.sendKeys(
        'Add Exploration Input', addExplorationInput, protractor.Key.TAB);
      // If query gets zero result, hitting Tab would not enable the
      // addExplorationButton.
    }
    var isEnabled = await addExplorationButton.isEnabled();
    if (isEnabled) {
      await action.click('Add Exploration Button', addExplorationButton);
    } else {
      throw new Error ('Add Exploration Button is not clickable');
    }
  };

  // Shift a node left in the node graph.
  this.shiftNodeLeft = async function(number) {
    await action.click('Editor Shift Left', editorShiftLeft.get(number));
  };

  this.setCommitMessage = async function(message) {
    await waitFor.visibilityOf(
      saveModal, 'Save Modal takes too long to appear');
    await waitFor.elementToBeClickable(
      commitMessageInput, 'Commit Message input takes too long to appear');
    await action.click('Commit Message Input', commitMessageInput);
    await action.sendKeys(
      'Commit Message Input', commitMessageInput, message);
  };

  // Shift a node right in the node graph.
  this.shiftNodeRight = async function(number) {
    await action.click('Editor Shift Right', editorShiftRight.get(number));
  };

  // Delete a node in the node graph.
  this.deleteNode = async function(number) {
    await action.click('Editor Delete Node', editorDeleteNode.get(number));
  };

  // Save draft of the collection.
  this.saveDraft = async function() {
    await waitFor.elementToBeClickable(
      saveDraftButton, 'Collection Save Draft button is not clickable');
    await action.click('Save Draft Button', saveDraftButton);
  };

  // Closes the save modal.
  this.closeSaveModal = async function() {
    await waitFor.elementToBeClickable(
      closeSaveModalButton, 'Publish Changes button is not clickable');
    await action.click('Close Save Modal Button', closeSaveModalButton);
    await waitFor.invisibilityOf(
      closeSaveModalButton, 'Save Modal takes too long to close');
  };

  // Click on publish collection.
  this.publishCollection = async function() {
    await waitFor.elementToBeClickable(
      editorPublishButton, 'Collection Publish button is not clickable');
    await action.click('Editor Publish Button', editorPublishButton);
  };

  // Set collection title.
  this.setTitle = async function(title) {
    await action.sendKeys('Editor Title Input', editorTitleInput, title);
  };

  // Set collection objective.
  this.setObjective = async function(objective) {
    await action.sendKeys(
      'Collection Editor Objective Input',
      collectionEditorObjectiveInput, objective);
  };

  // Set collection category.
  this.setCategory = async function(category) {
    await action.click(
      'Editor Category Drop Down', editorCategoryDropdown.first());
    // TODO(#11289): sendKeys statement cannot be
    // converted to action.sendKeys(..)
    // because doing so causes the e2e test to fail with the error
    // "Failed: Cannot read property 'bind' of undefined".
    await browser.driver.switchTo().activeElement().sendKeys(category + '\n');
  };

  // Saves changes and publishes collection.
  this.saveChanges = async function() {
    await waitFor.elementToBeClickable(
      saveChangesButton, 'Save Changes button is not clickable');
    await action.click('Save Changes Button', saveChangesButton);
    await waitFor.invisibilityOf(
      saveChangesButton, 'Save Changes Modal takes too long to close');
    await waitFor.invisibilityOf(
      saveInProgressLabel, 'Collection is taking too long to save.');
  };
};

exports.CollectionEditorPage = CollectionEditorPage;
<|endoftext|>


<commit_before>;(function() {

    var map_url = $SCRIPT_ROOT+'/data'+$INITIAL_LOAD;
    console.log(map_url);

    var width = 2000,
        height = 1200;

    var parties_map = {
        "Conservative":"#0575c9",
        "Labour":"#ed1e0e",
        "Liberal Democrat":"#fe8300",
        "UKIP":"#712f87",
        "Green":"#78c31e",
        "Scottish National Party":"#EBC31C",
        "Social Democratic and Labour Party":"#65a966",
        "DUP":"#c0153d",
        "Sinn Fein":"#00623f",
        "Alliance":"#e1c21e",
        "Respect":"#31b56a",
        "Plaid Cymru":"#4e9f2f",
        "Independent":"#4e9f2f",
        "UUP":"#4e9f2f"
    }

    var force,
        node,
        link,
        mark;

    var svg;

    var visibleNodes = [],
        visibleEdges = [],
        invisibleNodes = [],
        invisibleEdges= [],
        allNodes = [],
        allEdges = [];

    // used to generate links based on handles
    // not place in node array.
    var visibleNodesHandleMap = {};

    var clickedNodes=[];
    var lastClickedNode;

    var toggle = {
        radius:false,
        highlight:false,
        click_all:false
    };

    function keyController(){
        var key = d3.event.keyCode;

        if  (key == 119 || key == 87 )
        {
            toggle.radius = (!toggle.radius);
            radiusTransition();
        }
        else if (key == 115 || key == 83)
        {
            toggle.highlight = (!toggle.highlight);
            highlightTransition();
        }
        else if (key == 120 || key == 88)
        {
            toggle.click_all = (!toggle.click_all);
            clickAllTransition();
        }
    }

    function requestMapToD3Map(backend_map){
        return {
            visibleNodes:backend_map.nodes,
            visibleEdges:[]
        }
    }

    function generateMap(error, server_map){
        var map = requestMapToD3Map(server_map);

        visibleNodes = map.visibleNodes.slice();
        visibleEdges = map.visibleEdges.slice();

        allNodes = map.visibleNodes.slice();
        allEdges = map.visibleEdges.slice();

        visibleNodes.forEach(function(d){
            visibleNodesHandleMap[d.handle] = visibleNodes.indexOf(d)
        });

        force = d3.layout.force()
            .charge(-200)
            .gravity(.5)
            .linkDistance(Math.sqrt(150000/visibleNodes.length))
            .linkStrength(2)
            .chargeDistance(10000)
            .size([width, height])
            .start();

        redrawMap();

        d3.select("body").on("keypress", keyController)

        force.on("tick", function(){
            moveItems();
        });

    }

    function redrawMap(){
        d3.selectAll('svg').remove();
        drawMap();
    }

    function drawMap(){
        svg = d3.select("body").append("svg")
                .attr("width", width)
                .attr("height", height);

        force
            .nodes(allNodes)
            .links(allEdges)
            .start()

        // draw nodes
        node = svg.selectAll(".node")
             .data(visibleNodes)
           .enter().append("g")
             .attr("class", "node")
             .attr("id", function(d){return d.handle})
             .call(force.drag)
        node.append("circle")
             .attr("clicked", function(d){return d.clicked})
             .attr("fill", function(d){ return parties_map[d.party]})
             .attr("party", function(d){return d.party})
             .style("stroke", "white")
             .on("click", clickNode)
        node.append("text")
             .text(function(d){return })
             .attr("dx", -25)
             .attr("dy", 5)
             .style("stroke", "black")
        node.append("title")
             .text(function(d){return d.name})

        // draw links
        link = svg.selectAll(".link")
              .data(visibleEdges)
             .enter().append("path")
              .attr("class", "link")
              .style("stroke-width", 1)
              .style("stroke-opacity", 0.5)
              .style("fill", "none")
              .style("stroke", function(d){return (d.contact=='mentions')?'grey':'black'})
              .style("marker-end", "url(#end)")

        // draw arrowheads
        mark = svg.append("defs").selectAll("marker")
              .data( visibleEdges.map(function(d){return d.source.handle}))
             .enter().append("marker")
              .attr("id", "end")
              .attr("viewBox", "0 -5 10 10")
              .attr("refX", 20)
              .attr("refY", -5)
              .attr("markerWidth", 6)
              .attr("markerHeight", 6)
              .attr("orient", "auto")
             .append("path")
              .attr("d", "M0,-5L10,0L0,5 L10,0 L0, -5")
              .attr("class", function(d){return d}) // the link's source's handle
              .style("stroke", function(d){ return (d.contact=='mentions')?'grey':'black'})
              .style("stroke-opacity", 0.5)

        // draw customiseable features
        node.selectAll("circle")
             .attr("r", function(d){return (d.clicked==1 && toggle.radius==true)?5+Math.sqrt(d.tweets/100):5;})
             .style("stroke-opacity", function(d){return (d.clicked==0)?0.5:1;})
             .style("stroke-width", function(d){return (d.clicked==0)?3:1.5;})
        node.selectAll("text")
             .text(function(d){return (d.clicked==1 && toggle.radius==true)?d.name:undefined;})

        highlightTransition();
    }

    function clickNode(clickedNode){

        lastClickedNode = clickedNode;

        if (clickedNode.clicked==1){
            highlightTransition();
            return;
        } else {
            clickedNode.clicked=1;
            addNodesBezierEdges(clickedNode);
            redrawMap();
        }

        //redraw() if "pop" toggle & just extend()
    }

    function addNodesBezierEdges(clickedNode){

        for (contactType in clickedNode.relationships){
            var contacts = clickedNode.relationships[contactType]

            for ( targetHandle in contacts){

                //  ERROR: if node not plotted, ignore & continue
                if  (visibleNodesHandleMap[targetHandle] == undefined){
                    console.log("Missing node: "+targetHandle+" for "+clickedNode.handle);
                    continue;
                }

                var s_index = visibleNodesHandleMap[clickedNode.handle],
                    t_index = visibleNodesHandleMap[targetHandle];

                var s = visibleNodes[s_index],
                    t = visibleNodes[t_index],
                    i = {};

                var invisibleEdgeA = {source : s, target : i},
                    invisibleEdgeB = {source : i, target : t};

                var visibleEdge = {
                        source:s, inter:i, target:t,
                        contactType:contactType, value:contacts[targetHandle]
                }

                if (s.handle != t.handle){
                    invisibleNodes.push(i);
                    invisibleEdges.push(invisibleEdgeA, invisibleEdgeB);

                    visibleEdges.push(visibleEdge);

                    allNodes.push(i)
                    allEdges.push(invisibleEdgeA, invisibleEdgeB, visibleEdge)

                    // node.push(i)
                    // link.push(visibleEdge)
                }

            }

        }
    }

    function moveItems(){

        function border(z, axis ){
            var r = 5;
            if (axis == 'x'){return Math.max(2*r, Math.min(width-2*r, z))}
            else if (axis == 'y'){return Math.max(2*r, Math.min(height-2*r, z))};
        }

        function linkBezierPath(d){
            return  "M" + border(d.source.x,'x') + "," + border(d.source.y,'y')
                  + "S" + border(d.inter.x, 'x') + "," + border(d.inter.y, 'y')
                  + " " + border(d.target.x,'x') + "," + border(d.target.y,'y');
        }

        function nodeTranslate(d){
            return "translate("+ border(d.x,'x') + ","
                               + border(d.y,'y') + ")";
        }

        link.attr("d", linkBezierPath);
        node.attr("transform", nodeTranslate);
    }

   function radiusTransition(){
        if (toggle.radius==true){
            node.selectAll('circle')
                  .transition()
                  .duration(2000)
                  .attr("r", function(d){return d.clicked==1?5+ Math.sqrt(d.tweets/100):5;});
            node.selectAll('text')
                  .text(function(d){return d.clicked==1?d.name:''});

        } else if (toggle.radius ==false){
            node.selectAll('circle')
                  .transition()
                  .duration(2000)
                  .attr("r", function(d){return 5;});
            node.selectAll('text')
                  .text(function(d){return;})
        }
    }

    function highlightTransition(){
        if (lastClickedNode == undefined){
            // nothing to highlight
            return;
        }

        if (toggle.highlight==true){
            var highlightedNodes = [lastClickedNode.handle];

            visibleEdges.forEach(function(d){
                if (d.source.handle==lastClickedNode.handle){
                    highlightedNodes.push(d.target.handle)
                }
            });

            node.selectAll("circle")
                  .transition()
                  .duration(1000)
                  .attr('opacity', function(d){
                      return (highlightedNodes.some(function(e){return d.handle==e;}))?1:0.1;
                  });
            node.selectAll("text")
                  .transition()
                  .duration(3000)
                  .text(function(d){
                      return (highlightedNodes.some(function(e){return d.handle==e;})&& toggle.radius==true)?d.name:undefined;
                  });

            link.style("stroke-opacity", function(d){return (d.source.handle==lastClickedNode.handle)?1:0.05});

            svg.selectAll("marker").selectAll("path")
                  .style("stroke-opacity",0.05)

        } else {
            node.selectAll("circle")
                  .transition("circle")
                  .transition()
                  .duration(1000)
                  .attr('opacity', 1);

            node.selectAll("text")
                  .text(function(d){return (toggle.radius == true && d.clicked==1)?d.name:undefined;});

            link.style('stroke-opacity', 0.5);

            svg.selectAll("marker").selectAll("path")
                  .style("stroke-opacity",0.5);
        }

    }

    function clickAllTransition(){
        if (toggle.click_all==true){

            var nodes = visibleNodes.slice();

            function clicker(initial, nodes){
                return function(){
                    if (initial<nodes.length){
                        clickNode(nodes[initial]);
                        initial+=1;
                    }
                }
            }

            clickNodeTimer = clicker(0, nodes);

            toggle.clicker = window.setInterval(clickNodeTimer,1500);

        } else {
            window.clearInterval(toggle.clicker);
        }
    }

    d3.json(map_url, generateMap);

})();<commit_msg>filtering method added<commit_after>;(function() {

    var map_url = $SCRIPT_ROOT+'/data'+$INITIAL_LOAD;
    console.log(map_url);

    var width = 2000,
        height = 1200;

    var parties_map = {
        "Conservative":"#0575c9",
        "Labour":"#ed1e0e",
        "Liberal Democrat":"#fe8300",
        "UKIP":"#712f87",
        "Green":"#78c31e",
        "Scottish National Party":"#EBC31C",
        "Social Democratic and Labour Party":"#65a966",
        "DUP":"#c0153d",
        "Sinn Fein":"#00623f",
        "Alliance":"#e1c21e",
        "Respect":"#31b56a",
        "Plaid Cymru":"#4e9f2f",
        "Independent":"#4e9f2f",
        "UUP":"#4e9f2f"
    }

    var force,
        node,
        link,
        mark;

    var svg;

    var visibleNodes = [],
        visibleEdges = [],
        invisibleNodes = [],
        invisibleEdges= [],
        allNodes = [],
        allEdges = [];

    // used to generate links based on handles
    // not place in node array.
    var visibleNodesHandleMap = {};

    var clickedNodes=[];
    var lastClickedNode;

    var toggle = {
        radius:false,
        highlight:false,
        click_all:false
    };

    function keyController(){
        var key = d3.event.keyCode;

        if  (key == 119 || key == 87 )
        {
            toggle.radius = (!toggle.radius);
            radiusTransition();
        }
        else if (key == 115 || key == 83)
        {
            toggle.highlight = (!toggle.highlight);
            highlightTransition();
        }
        else if (key == 120 || key == 88)
        {
            toggle.click_all = (!toggle.click_all);
            clickAllTransition();
        }
    }

    function requestMapToD3Map(backend_map){
        function filterTweetType(nodeList, passes){
            return nodeList.forEach( function(node, index, array){
                for (relationshipType in node.relationships){
                    if (relationshipType != passes){
                        delete node.relationships[relationshipType]
                    }
                }
                array[index]=node
            })
        }
        filterTweetType(backend_map.nodes, 'no_by_proxy');
        console.log(backend_map.nodes)

        return {
            visibleNodes:backend_map.nodes,
            visibleEdges:[]
        }
    }

    function generateMap(error, server_map){
        var map = requestMapToD3Map(server_map);

        visibleNodes = map.visibleNodes.slice();
        visibleEdges = map.visibleEdges.slice();

        allNodes = map.visibleNodes.slice();
        allEdges = map.visibleEdges.slice();

        visibleNodes.forEach(function(d){
            visibleNodesHandleMap[d.handle] = visibleNodes.indexOf(d)
        });

        force = d3.layout.force()
            .charge(-200)
            .gravity(.5)
            .linkDistance(Math.sqrt(150000/visibleNodes.length))
            .linkStrength(2)
            .chargeDistance(10000)
            .size([width, height])
            .start();

        redrawMap();

        d3.select("body").on("keypress", keyController)

        force.on("tick", function(){
            moveItems();
        });

    }

    function redrawMap(){
        d3.selectAll('svg').remove();
        drawMap();
    }

    function drawMap(){
        svg = d3.select("body").append("svg")
                .attr("width", width)
                .attr("height", height);

        force
            .nodes(allNodes)
            .links(allEdges)
            .start()

        // draw nodes
        node = svg.selectAll(".node")
             .data(visibleNodes)
           .enter().append("g")
             .attr("class", "node")
             .attr("id", function(d){return d.handle})
             .call(force.drag)
        node.append("circle")
             .attr("clicked", function(d){return d.clicked})
             .attr("fill", function(d){ return parties_map[d.party]})
             .attr("party", function(d){return d.party})
             .style("stroke", "white")
             .on("click", clickNode)
        node.append("text")
             .text(function(d){return })
             .attr("dx", -25)
             .attr("dy", 5)
             .style("stroke", "black")
        node.append("title")
             .text(function(d){return d.name})

        // draw links
        link = svg.selectAll(".link")
              .data(visibleEdges)
             .enter().append("path")
              .attr("class", "link")
              .style("stroke-width", 1)
              .style("stroke-opacity", 0.5)
              .style("fill", "none")
              .style("stroke", function(d){return (d.contact=='mentions')?'grey':'black'})
              .style("marker-end", "url(#end)")

        // draw arrowheads
        mark = svg.append("defs").selectAll("marker")
              .data( visibleEdges.map(function(d){return d.source.handle}))
             .enter().append("marker")
              .attr("id", "end")
              .attr("viewBox", "0 -5 10 10")
              .attr("refX", 20)
              .attr("refY", -5)
              .attr("markerWidth", 6)
              .attr("markerHeight", 6)
              .attr("orient", "auto")
             .append("path")
              .attr("d", "M0,-5L10,0L0,5 L10,0 L0, -5")
              .attr("class", function(d){return d}) // the link's source's handle
              .style("stroke", function(d){ return (d.contact=='mentions')?'grey':'black'})
              .style("stroke-opacity", 0.5)

        // draw customiseable features
        node.selectAll("circle")
             .attr("r", function(d){return (d.clicked==1 && toggle.radius==true)?5+Math.sqrt(d.tweets/100):5;})
             .style("stroke-opacity", function(d){return (d.clicked==0)?0.5:1;})
             .style("stroke-width", function(d){return (d.clicked==0)?3:1.5;})
        node.selectAll("text")
             .text(function(d){return (d.clicked==1 && toggle.radius==true)?d.name:undefined;})

        highlightTransition();
    }

    function clickNode(clickedNode){

        lastClickedNode = clickedNode;

        if (clickedNode.clicked==1){
            highlightTransition();
            return;
        } else {
            clickedNode.clicked=1;
            addNodesBezierEdges(clickedNode);
            redrawMap();
        }

        //redraw() if "pop" toggle & just extend()
    }

    function addNodesBezierEdges(clickedNode){

        for (contactType in clickedNode.relationships){
            var contacts = clickedNode.relationships[contactType]

            for ( targetHandle in contacts){

                //  ERROR: if node not plotted, ignore & continue
                if  (visibleNodesHandleMap[targetHandle] == undefined){
                    console.log("Missing node: "+targetHandle+" for "+clickedNode.handle);
                    continue;
                }

                var s_index = visibleNodesHandleMap[clickedNode.handle],
                    t_index = visibleNodesHandleMap[targetHandle];

                var s = visibleNodes[s_index],
                    t = visibleNodes[t_index],
                    i = {};

                var invisibleEdgeA = {source : s, target : i},
                    invisibleEdgeB = {source : i, target : t};

                var visibleEdge = {
                        source:s, inter:i, target:t,
                        contactType:contactType, value:contacts[targetHandle]
                }

                if (s.handle != t.handle){
                    invisibleNodes.push(i);
                    invisibleEdges.push(invisibleEdgeA, invisibleEdgeB);

                    visibleEdges.push(visibleEdge);

                    allNodes.push(i)
                    allEdges.push(invisibleEdgeA, invisibleEdgeB, visibleEdge)

                    // node.push(i)
                    // link.push(visibleEdge)
                }

            }

        }
    }

    function moveItems(){

        function border(z, axis ){
            var r = 5;
            if (axis == 'x'){return Math.max(2*r, Math.min(width-2*r, z))}
            else if (axis == 'y'){return Math.max(2*r, Math.min(height-2*r, z))};
        }

        function linkBezierPath(d){
            return  "M" + border(d.source.x,'x') + "," + border(d.source.y,'y')
                  + "S" + border(d.inter.x, 'x') + "," + border(d.inter.y, 'y')
                  + " " + border(d.target.x,'x') + "," + border(d.target.y,'y');
        }

        function nodeTranslate(d){
            return "translate("+ border(d.x,'x') + ","
                               + border(d.y,'y') + ")";
        }

        link.attr("d", linkBezierPath);
        node.attr("transform", nodeTranslate);
    }

   function radiusTransition(){
        if (toggle.radius==true){
            node.selectAll('circle')
                  .transition()
                  .duration(2000)
                  .attr("r", function(d){return d.clicked==1?5+ Math.sqrt(d.tweets/100):5;});
            node.selectAll('text')
                  .text(function(d){return d.clicked==1?d.name:''});

        } else if (toggle.radius ==false){
            node.selectAll('circle')
                  .transition()
                  .duration(2000)
                  .attr("r", function(d){return 5;});
            node.selectAll('text')
                  .text(function(d){return;})
        }
    }

    function highlightTransition(){
        if (lastClickedNode == undefined){
            // nothing to highlight
            return;
        }

        if (toggle.highlight==true){
            var highlightedNodes = [lastClickedNode.handle];

            visibleEdges.forEach(function(d){
                if (d.source.handle==lastClickedNode.handle){
                    highlightedNodes.push(d.target.handle)
                }
            });

            node.selectAll("circle")
                  .transition()
                  .duration(1000)
                  .attr('opacity', function(d){
                      return (highlightedNodes.some(function(e){return d.handle==e;}))?1:0.1;
                  });
            node.selectAll("text")
                  .transition()
                  .duration(3000)
                  .text(function(d){
                      return (highlightedNodes.some(function(e){return d.handle==e;})&& toggle.radius==true)?d.name:undefined;
                  });

            link.style("stroke-opacity", function(d){return (d.source.handle==lastClickedNode.handle)?1:0.05});

            svg.selectAll("marker").selectAll("path")
                  .style("stroke-opacity",0.05)

        } else {
            node.selectAll("circle")
                  .transition("circle")
                  .transition()
                  .duration(1000)
                  .attr('opacity', 1);

            node.selectAll("text")
                  .text(function(d){return (toggle.radius == true && d.clicked==1)?d.name:undefined;});

            link.style('stroke-opacity', 0.5);

            svg.selectAll("marker").selectAll("path")
                  .style("stroke-opacity",0.5);
        }

    }

    function clickAllTransition(){
        if (toggle.click_all==true){

            var nodes = visibleNodes.slice();

            function clicker(initial, nodes){
                return function(){
                    if (initial<nodes.length){
                        clickNode(nodes[initial]);
                        initial+=1;
                    }
                }
            }

            clickNodeTimer = clicker(0, nodes);

            toggle.clicker = window.setInterval(clickNodeTimer,1500);

        } else {
            window.clearInterval(toggle.clicker);
        }
    }

    d3.json(map_url, generateMap);

    function cycleFocus(){

    }

})();<|endoftext|>


<commit_before>var nconf = require('nconf'),
    path = require('path'),
    localUtils = require('./utils'),
    packageInfo = require('../../../package.json'),
    env = process.env.NODE_ENV || 'development';

nconf.set('NODE_ENV', env);

/**
 * command line arguments
 */
nconf.argv();

/**
 * env arguments
 */
nconf.env();

/**
 * load config files
 */
nconf.file('1', __dirname + '/overrides.json');
nconf.file('2', path.join(process.cwd(), 'config.' + env + '.json'));
nconf.file('3', __dirname + '/env/config.' + env + '.json');
nconf.file('4', __dirname + '/defaults.json');

/**
 * transform all relative paths to absolute paths
 */
localUtils.makePathsAbsolute.bind(nconf)();

/**
 * values we have to set manual
 * @TODO: ghost-cli?
 */
nconf.set('ghostVersion', packageInfo.version);

module.exports = nconf;
module.exports.isPrivacyDisabled = localUtils.isPrivacyDisabled.bind(nconf);
module.exports.getContentPath = localUtils.getContentPath.bind(nconf);
<commit_msg>🐛  argv config overrides (#7493)<commit_after>var nconf = require('nconf'),
    path = require('path'),
    localUtils = require('./utils'),
    packageInfo = require('../../../package.json'),
    env = process.env.NODE_ENV || 'development';

nconf.set('NODE_ENV', env);

/**
 * command line arguments
 */
nconf.argv();

/**
 * env arguments
 */
nconf.env();

/**
 * load config files
 * @TODO:
 * - why does this work? i have no idea!
 * - find out why argv override works, when defining these weird keys
 * - i could not find any nconf usages so that all config requirements work
 */
nconf.file('ghost1', __dirname + '/overrides.json');
nconf.file('ghost2', path.join(process.cwd(), 'config.' + env + '.json'));
nconf.file('ghost3', __dirname + '/env/config.' + env + '.json');
nconf.file('ghost4', __dirname + '/defaults.json');

/**
 * transform all relative paths to absolute paths
 */
localUtils.makePathsAbsolute.bind(nconf)();

/**
 * values we have to set manual
 * @TODO: ghost-cli?
 */
nconf.set('ghostVersion', packageInfo.version);

module.exports = nconf;
module.exports.isPrivacyDisabled = localUtils.isPrivacyDisabled.bind(nconf);
module.exports.getContentPath = localUtils.getContentPath.bind(nconf);
<|endoftext|>


<commit_before>/** MIT License (c) copyright B Cavalier & J Hann */

(function(define) {
define(function(require) {

	var methodsToForward, ObjectMediator;

	methodsToForward = ['add', 'remove'];

	ObjectMediator = require('./SimpleMediator');

	/**
	 * Sets up mediation between two collection adapters
	 * @param adapter1 {Object} collection adapter
	 * @param adapter2 {Object} collection adapter
	 * @param options {Object} options
	 * @param options.sync {Boolean} whether to immediately synchronize data from
	 *   adapter1 to adapter2.  Default is true
	 */
	return function (adapter1, adapter2, options) {

		var unwatch1, unwatch2, unwatchObjects;

		if(!options) options = {};

		if(!('sync' in options) || options.sync) {
			adapter1.syncTo(adapter2);
		}

		unwatchObjects = createUnwatcher();
		unwatch1 = initForwarding(adapter1, adapter2, unwatchObjects);
		unwatch2 = initForwarding(adapter2, adapter1, unwatchObjects);

		return function () {
			unwatch1();
			unwatch2();
			unwatchObjects();
		};
	};

	function createForwarder(method) {
		function doForward(target, item, index) {
			this.forwardTo = noop;
			try {
				target[method](item, index);
			} finally {
				this.forwardTo = doForward;
			}
		}

		return {
			forwardTo: doForward
		};
	}

	function createCallback(forwarder, to) {
		return function (item, index) {
			forwarder.forwardTo(to, item, index);
		}
	}

	function initForwarding(from, to, itemUnwatcher) {
		var forwarder, callbacks, i, len;

		callbacks = [];
		for (i = 0, len = methodsToForward.length; i < len; i++) {
			forwarder = createForwarder(methodsToForward[i]);
			forwarder.unwatcher = itemUnwatcher;
			callbacks.push(createCallback(forwarder, to));
		}

		return from.watch.apply(from, callbacks);
	}

	function createUnwatcher() {
		var unwatchers;
		unwatchers = [];
		function unwatchAll() {
			var unwatch;
			while ((unwatch = unwatchers.pop())) unwatch();
		}
		unwatchAll.push = function addUnwatcher(unwatcher) {
			unwatchers.push(unwatcher);
		};
		return unwatchAll;
	}

	function noop() {}

});
}(
	typeof define == 'function'
		? define
		: function(factory) { module.exports = factory(require); }
));
<commit_msg>redesign CollectionMediator to adapt and mediate items as well as collections<commit_after>/** MIT License (c) copyright B Cavalier & J Hann */

(function (define) {
define(function (require) {
"use strict";

	var methodsToForward, ObjectMediator;

	methodsToForward = ['add', 'remove'];

	ObjectMediator = require('./SimpleMediator');

	/**
	 * Sets up mediation between two collection adapters
	 * @param primary {Object} collection adapter
	 * @param secondary {Object} collection adapter
	 * @param resolver {Function} function (object, type) { returns Adapter; }
	 * @param options {Object} options
	 * @param options.bind if truthy, immediately synchronize data primary
	 *   primary secondary secondary.
	 */
	return function (primary, secondary, resolver, options) {

		var itemMap1, itemMap2, mediationHandler1, mediationHandler2,
			watchHandler1, unwatch1, unwatch2;

		if (!options) options = {};

		// these maps keep track of items that are being watched
		itemMap1 = createMap(primary.comparator, primary.namer);
		itemMap2 = createMap(secondary.comparator, secondary.namer);

		// these functions handle any item-to-item mediation
		mediationHandler1 = createItemMediatorHandler(primary, itemMap1, resolver);
		mediationHandler2 = createItemMediatorHandler(secondary, itemMap2, resolver);

		// this function handles property changes that could affect collection order
		watchHandler1 = createItemWatcherHandler(primary, secondary, resolver);

		if (!('sync' in options) || options.sync) {
			primary.forEach(function (item) {
				var copy;
				// watch for item changes
				watchHandler1(item, primary, itemMap1);
				// push item into secondary
				copy = secondary.add(item);
				// if secondary returns a copy
				if (copy) {
					mediationHandler1(copy, item, secondary);
				}
			});
		}

		unwatch1 = initForwarding(primary, secondary, mediationHandler1);
		unwatch2 = initForwarding(secondary, primary, mediationHandler2);

		return function () {
			itemMap1.forEach(unwatchItemData);
			itemMap2.forEach(unwatchItemData);
			unwatch1();
			unwatch2();
		};
	};

	function unwatchItemData (data) {
		if (data.unwatch) data.unwatch();
		if (data.unmediate) data.unmediate();
	}

	function createAdapter (object, resolver, type, options) {
		var Adapter = resolver(object, type);
		return new Adapter(object, options);
	}

	function createItemWatcherHandler (primary, secondary, resolver) {
		if (typeof primary.checkPosition == 'function' || typeof secondary.checkPosition == 'function') {
			return function watchItem (item, target, itemMap) {
				var itemData, newAdapter;
				itemData = itemMap.get(item);
				if (itemData) {
					// the item was already being watched
					// TODO: do we care?
					if (itemData.unwatch) itemData.unwatch();
				}
				else {
					itemData = itemMap.set(item, {
						adapter: createAdapter(item, resolver, 'object', target.getBindings())
					});
				}
				itemData.unwatch = itemData.adapter.watchAll(function (prop, value) {
					// if primary requires ordering, tell it that the item may have moved
					// TODO: if adapter returned another copy, lose previous copy, adapt this one, and start watching it
					if (typeof primary.checkPosition == 'function') primary.checkPosition(item);
					// if secondary requires ordering, tell it that the item may have moved
					// TODO: if adapter returned another copy, lose previous copy, adapt this one, and start watching it
					if (typeof secondary.checkPosition == 'function') secondary.checkPosition(item);
				});
				return itemData;
			}
		}
		else {
			return noop;
		}
	}

	function createItemMediatorHandler (sender, itemMap, resolver) {
		return function discoverItem (newItem, refItem, target) {
			var itemData, newAdapter;
			itemData = itemMap.get(refItem);
			if (itemData) {
				// the item was already being mediated
				if (itemData.unmediate) itemData.unmediate();
			}
			else {
				itemData = itemMap.set(refItem, {
					adapter: createAdapter(refItem, resolver, 'object', sender.getBindings())
				});
			}
			newAdapter = createAdapter(newItem, resolver, 'object', target.getBindings());
			itemData.unmediate = ObjectMediator(itemData.adapter, newAdapter);
		}
	}

	function createForwarder (method, discoveryCallback) {
		function doForward(target, item, index) {
			var copy, copyAdapter;
			this.forwardTo = noop;
			try {
				copy = target[method](item, index);
			} finally {
				this.forwardTo = doForward;
			}
			// if adapter2 returns a copy
			if (copy) {
				discoveryCallback(copy, item, target);
			}
		}

		return {
			forwardTo: doForward
		};
	}

	function createCallback (forwarder, to) {
		return function (item, index) {
			forwarder.forwardTo(to, item, index);
		}
	}

	function initForwarding (from, to, discoveryCallback) {
		var forwarder, callbacks, i, len;

		callbacks = [];
		for (i = 0, len = methodsToForward.length; i < len; i++) {
			forwarder = createForwarder(methodsToForward[i], discoveryCallback);
			callbacks.push(createCallback(forwarder, to));
		}

		// if adapter2 wants a keyFunc and doesn't have one, copy it from adapter1
		if ('namer' in to && !to.namer && from.namer) {
			to.namer = from.namer;
		}
		// if adapter2 wants a comparator and doesn't have one, copy it from adapter1
		if ('comparator' in to && !to.comparator && from.comparator) {
			to.comparator = from.comparator;
		}

		return from.watch.apply(from, callbacks);
	}

	// it's pretty clear we need to share this kind of thing all over the place :)
	function createMap (comparator, namer) {
		var index, list, finder;

		index = {};
		list = [];

		function scan (key) {
			var i, entry;
			i = list.length;
			while ((entry = list[--i])) if (entry.object == key) return i;
		}

		function search (key) {
			var min, max, mid, compare;
			min = 0;
			max = list.length;
			do {
				mid = Math.floor((min + max) / 2);
				compare = comparator(key, list[mid].key);
				// don't use mid +/- 1 or we may miss in-between
				if (compare > 0) min = mid;
				else if (compare < 0) max = mid;
				else return mid;
			}
			while (max - min > 1 && !isNaN(compare));
		}

		if (!namer) {
			if (comparator) finder = search;
			else finder = scan;
		}

		if (namer) return {
			get: function (key) {
				return list[index[namer(key)]];
			},
			set: function (key, object) {
				index[namer(key)] = list.push(object);
				return object;
			},
			remove: function (key) {
				var name = namer(key);
				list.splice(index[name], 1);
				delete index[name];
			},
			forEach: function (lambda) {
				for (var p in index) lambda(list[index[p]], p);
			}
		};
		else return {
			get: function (key) {
				var entry = list[finder(key)];
				return entry && entry.object;
			},
			set: function (key, object) {
				list.splice(finder(key) || list.length, 0, { key: key, object: object});
				return object;
			},
			remove: function (key) {
				list.splice(finder(key) || list.length, 1);
			},
			forEach: function (lambda) {
				var i, entry;
				i= list.length;
				while ((entry = list[--i])) lambda(entry.object, entry.key);
			}
		}
	}

	function noop () {}

});
}(
	typeof define == 'function'
		? define
		: function (factory) { module.exports = factory(require); }
));
<|endoftext|>


<commit_before>(function(DOM, PATTERN, I18N_MISMATCH) {
    "use strict";

    var hasCheckedRadio = function(el) {
            return el.get("name") === this.get("name") && el.get("checked");
        },
        attachValidityTooltip = function(el) {
            var validityTooltip = DOM.create("div.better-validity-tooltip").hide();

            validityTooltip.on("click", function() {
                validityTooltip.hide();
                // focus to the invalid input
                el.fire("focus");
            });

            el.data(VALIDITY_TOOLTIP_KEY, validityTooltip).after(validityTooltip);

            return validityTooltip;
        },
        VALIDITY_KEY = "validity",
        VALIDITY_TOOLTIP_KEY = "validity-tooltip",
        VALIDITY_TOOLTIP_DELAY = 100,
        lastTooltipTimestamp = new Date(),
        delay = 0;

    DOM.extend("input,select,textarea", {
        constructor: function() {
            var type = this.get("type"),
                events = ~type.indexOf("checkbox radio") ? ["click", "click"] : ["input", "change"];

            if (this.matches("textarea")) this.on("input", this.onTextareaInput);

            this
                .on(events[0], this.onOptimisticValidityCheck)
                .on(events[1], this.onPesimisticValidityCheck);

            attachValidityTooltip(this);
        },
        validity: function(errors) {
            if (arguments.length) return this.data(VALIDITY_KEY, errors);

            var type = this.get("type"),
                value = this.get("value"),
                required = this.matches("[required]"),
                regexp;

            errors = this.data(VALIDITY_KEY);

            if (typeof errors === "function") errors = errors();

            errors = errors || [];

            if (!errors.length) {
                switch(type) {
                case "image":
                case "submit":
                case "button":
                case "select-one":
                case "select-multiple":
                    // only check custom error case
                    break;

                case "radio":
                    if (!required || this.parent("form").findAll("[name]").some(hasCheckedRadio, this)) break;
                    /* falls through */
                case "checkbox":
                    if (required && !this.get("checked")) errors.push("can't be empty");
                    break;

                default:
                    if (value) {
                        regexp = PATTERN[type];

                        if (regexp && !regexp.test(value)) errors.push(I18N_MISMATCH[type]);

                        if (type !== "textarea" && (type = this.get("pattern"))) {
                            type = "^(?:" + type + ")$";

                            if (!(regexp = PATTERN[type])) {
                                regexp = PATTERN[type] = new RegExp(type);
                            }

                            if (!regexp.test(value)) {
                                errors.push(this.get("title") || "illegal value format");
                            }
                        }
                    } else if (required) {
                        errors.push("can't be empty");
                    }
                }
            }

            return errors;
        },
        onOptimisticValidityCheck: function() {
            if (!this.validity().length) this.fire("validity:ok");
        },
        onPesimisticValidityCheck: function() {
            var errors = this.validity();

            if (errors.length) {
                this.fire("validity:fail", errors);
            } else {
                this.fire("validity:ok");
            }
        },
        onTextareaInput: function() {
            // maxlength fix for textarea
            var maxlength = parseFloat(this.get("maxlength")),
                value = this.get();

            if (maxlength && value.length > maxlength) {
                this.set(value.substr(0, maxlength));
            }
        }
    });

    DOM.extend("form", {
        constructor: function() {
            // disable native validation
            this
                .set("novalidate", "novalidate")
                .on("submit", this.onFormSubmit)
                .on("reset", this.onFormReset);
        },
        validity: function(errors) {
            if (arguments.length) return this.data(VALIDITY_KEY, errors);

            errors = this.data(VALIDITY_KEY);

            if (typeof errors === "function") errors = errors();

            errors = errors || {};
            errors.length = 0;

            return this.findAll("[name]").reduce(function(memo, el) {
                var name = el.get("name"),
                    errors = name in memo ? memo[name] : el.validity();

                if (errors.length) {
                    memo[name] = errors;

                    memo.length += errors.length;
                }

                return memo;
            }, errors);
        },
        onFormSubmit: function() {
            var errors = this.validity(), name;

            for (name in errors) {
                this.find("[name=" + name + "]").fire("validity:fail", errors[name]);
            }

            if (errors.length) {
                // fire event on form level
                this.fire("validity:fail", errors);

                return false;
            }
        },
        onFormReset: function() {
            this.findAll("[name]").each(function(el) { el.data(VALIDITY_TOOLTIP_KEY).hide() });
        }
    });

    DOM.on("validity:ok", function(target, cancel) {
        target.removeClass("invalid").addClass("valid");

        if (!cancel) {
            var validityTooltip = target.data(VALIDITY_TOOLTIP_KEY);

            if (validityTooltip) validityTooltip.hide();
        }
    });

    DOM.on("validity:fail", function(errors, target, cancel) {
        target.removeClass("valid").addClass("invalid");

        // errors could be string, array, object
        if (!cancel && (typeof errors === "string" || Array.isArray(errors)) && errors.length) {
            if (Array.isArray(errors)) errors = errors.join("<br>");

            var validityTooltip = target.data(VALIDITY_TOOLTIP_KEY) || attachValidityTooltip(target);

            // use a small delay if several tooltips are going to be displayed
            if (new Date() - lastTooltipTimestamp < VALIDITY_TOOLTIP_DELAY) {
                delay += VALIDITY_TOOLTIP_DELAY;
            } else {
                delay = VALIDITY_TOOLTIP_DELAY;
            }

            validityTooltip.hide().i18n(errors).show(delay);

            lastTooltipTimestamp = new Date();
        }
    });
}(window.DOM, {
    email: new RegExp("^([a-z0-9_\\.\\-\\+]+)@([\\da-z\\.\\-]+)\\.([a-z\\.]{2,6})$", "i"),
    url: new RegExp("^(https?:\\/\\/)?[\\da-z\\.\\-]+\\.[a-z\\.]{2,6}[#&+_\\?\\/\\w \\.\\-=]*$", "i"),
    tel: new RegExp("^((\\+\\d{1,3}(-| )?\\(?\\d\\)?(-| )?\\d{1,5})|(\\(?\\d{2,6}\\)?))(-| )?(\\d{3,4})(-| )?(\\d{4})(( x| ext)\\d{1,5}){0,1}$"),
    number: new RegExp("^-?[0-9]*(\\.[0-9]+)?$")
}, {
    email: "should be a valid email",
    url: "should be a valid URL",
    tel: "should be a valid phone number"
}));
<commit_msg>don't fail on hidden elements, display the first error from array<commit_after>(function(DOM, PATTERN, I18N_MISMATCH) {
    "use strict";

    var hasCheckedRadio = function(el) {
            return el.get("name") === this.get("name") && el.get("checked");
        },
        attachValidityTooltip = function(el) {
            var validityTooltip = DOM.create("div.better-validity-tooltip").hide();

            validityTooltip.on("click", function() {
                validityTooltip.hide();
                // focus to the invalid input
                el.fire("focus");
            });

            el.data(VALIDITY_TOOLTIP_KEY, validityTooltip).after(validityTooltip);

            return validityTooltip;
        },
        VALIDITY_KEY = "validity",
        VALIDITY_TOOLTIP_KEY = "validity-tooltip",
        VALIDITY_TOOLTIP_DELAY = 100,
        lastTooltipTimestamp = new Date(),
        delay = 0;

    DOM.extend("input,select,textarea", {
        constructor: function() {
            var type = this.get("type"),
                events = ~type.indexOf("checkbox radio") ? ["click", "click"] : ["input", "change"];

            if (this.matches("textarea")) this.on("input", this.onTextareaInput);

            this
                .on(events[0], this.onOptimisticValidityCheck)
                .on(events[1], this.onPesimisticValidityCheck);

            attachValidityTooltip(this);
        },
        validity: function(errors) {
            if (arguments.length) return this.data(VALIDITY_KEY, errors);

            var type = this.get("type"),
                value = this.get("value"),
                required = this.matches("[required]"),
                regexp;

            errors = this.data(VALIDITY_KEY);

            if (typeof errors === "function") errors = errors();

            errors = errors || [];

            if (!errors.length) {
                switch(type) {
                case "image":
                case "submit":
                case "button":
                case "select-one":
                case "select-multiple":
                    // only check custom error case
                    break;

                case "radio":
                    if (!required || this.parent("form").findAll("[name]").some(hasCheckedRadio, this)) break;
                    /* falls through */
                case "checkbox":
                    if (required && !this.get("checked")) errors.push("can't be empty");
                    break;

                default:
                    if (value) {
                        regexp = PATTERN[type];

                        if (regexp && !regexp.test(value)) errors.push(I18N_MISMATCH[type]);

                        if (type !== "textarea" && (type = this.get("pattern"))) {
                            type = "^(?:" + type + ")$";

                            if (!(regexp = PATTERN[type])) {
                                regexp = PATTERN[type] = new RegExp(type);
                            }

                            if (!regexp.test(value)) {
                                errors.push(this.get("title") || "illegal value format");
                            }
                        }
                    } else if (required) {
                        errors.push("can't be empty");
                    }
                }
            }

            return errors;
        },
        onOptimisticValidityCheck: function() {
            if (!this.validity().length) this.fire("validity:ok");
        },
        onPesimisticValidityCheck: function() {
            var errors = this.validity();

            if (errors.length) {
                this.fire("validity:fail", errors);
            } else {
                this.fire("validity:ok");
            }
        },
        onTextareaInput: function() {
            // maxlength fix for textarea
            var maxlength = parseFloat(this.get("maxlength")),
                value = this.get();

            if (maxlength && value.length > maxlength) {
                this.set(value.substr(0, maxlength));
            }
        }
    });

    DOM.extend("form", {
        constructor: function() {
            // disable native validation
            this
                .set("novalidate", "novalidate")
                .on("submit", this.onFormSubmit)
                .on("reset", this.onFormReset);
        },
        validity: function(errors) {
            if (arguments.length) return this.data(VALIDITY_KEY, errors);

            errors = this.data(VALIDITY_KEY);

            if (typeof errors === "function") errors = errors();

            errors = errors || {};
            errors.length = 0;

            return this.findAll("[name]").reduce(function(memo, el) {
                var name = el.get("name"),
                    errors = name in memo ? memo[name] : (el.validity ? el.validity() : []);

                if (errors.length) {
                    memo[name] = errors;

                    memo.length += errors.length;
                }

                return memo;
            }, errors);
        },
        onFormSubmit: function() {
            var errors = this.validity(), name;

            for (name in errors) {
                this.find("[name=" + name + "]").fire("validity:fail", errors[name]);
            }

            if (errors.length) {
                // fire event on form level
                this.fire("validity:fail", errors);

                return false;
            }
        },
        onFormReset: function() {
            this.findAll("[name]").each(function(el) { el.data(VALIDITY_TOOLTIP_KEY).hide() });
        }
    });

    DOM.on("validity:ok", function(target, cancel) {
        target.removeClass("invalid").addClass("valid");

        if (!cancel) {
            var validityTooltip = target.data(VALIDITY_TOOLTIP_KEY);

            if (validityTooltip) validityTooltip.hide();
        }
    });

    DOM.on("validity:fail", function(errors, target, cancel) {
        target.removeClass("valid").addClass("invalid");

        // errors could be string, array, object
        if (!cancel && (typeof errors === "string" || Array.isArray(errors)) && errors.length) {
            if (Array.isArray(errors)) errors = errors[0]; // display only the first error

            var validityTooltip = target.data(VALIDITY_TOOLTIP_KEY) || attachValidityTooltip(target);

            // use a small delay if several tooltips are going to be displayed
            if (new Date() - lastTooltipTimestamp < VALIDITY_TOOLTIP_DELAY) {
                delay += VALIDITY_TOOLTIP_DELAY;
            } else {
                delay = VALIDITY_TOOLTIP_DELAY;
            }

            validityTooltip.hide().i18n(errors).show(delay);

            lastTooltipTimestamp = new Date();
        }
    });
}(window.DOM, {
    email: new RegExp("^([a-z0-9_\\.\\-\\+]+)@([\\da-z\\.\\-]+)\\.([a-z\\.]{2,6})$", "i"),
    url: new RegExp("^(https?:\\/\\/)?[\\da-z\\.\\-]+\\.[a-z\\.]{2,6}[#&+_\\?\\/\\w \\.\\-=]*$", "i"),
    tel: new RegExp("^((\\+\\d{1,3}(-| )?\\(?\\d\\)?(-| )?\\d{1,5})|(\\(?\\d{2,6}\\)?))(-| )?(\\d{3,4})(-| )?(\\d{4})(( x| ext)\\d{1,5}){0,1}$"),
    number: new RegExp("^-?[0-9]*(\\.[0-9]+)?$")
}, {
    email: "should be a valid email",
    url: "should be a valid URL",
    tel: "should be a valid phone number"
}));
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Button, Icon, Modal,Alert, Input, message, Menu, Row, Col } from 'antd'
import { autobind } from 'core-decorators';
import axios from 'axios';
import { withRouter } from 'react-router';
const { TextArea } = Input;
const Search = Input.Search;
const TYPE_EDIT = 'edit';
const confirm = Modal.confirm;
import {
  fetchGroupList,
  setCurrGroup,
  setGroupList
} from '../../../reducer/modules/group.js'

import './GroupList.scss'

@connect(
  state => ({
    groupList: state.group.groupList,
    currGroup: state.group.currGroup,
    curUserRole: state.user.role
  }),
  {
    fetchGroupList,
    setCurrGroup,
    setGroupList
  }
)
@withRouter
export default class GroupList extends Component {

  static propTypes = {
    groupList: PropTypes.array,
    currGroup: PropTypes.object,
    fetchGroupList: PropTypes.func,
    setCurrGroup: PropTypes.func,
    setGroupList: PropTypes.func,
    match: PropTypes.object,
    history: PropTypes.object,
    curUserRole: PropTypes.string
  }

  state = {
    addGroupModalVisible: false,
    editGroupModalVisible: false,
    newGroupName: '',
    newGroupDesc: '',
    currGroupName: '',
    currGroupDesc: '',
    groupList: []
  }

  constructor(props) {
    super(props)
  }

  async componentWillMount() {
    const groupName = this.props.match.params.groupName;
    await this.props.fetchGroupList();
    let currGroup = this.props.groupList[0] || { group_name: '', group_desc: '' };
    if(this.props.groupList.length && groupName){
      for(let i = 0;i<this.props.groupList.length;i++){
        if(this.props.groupList[i].group_name === groupName){
          currGroup = this.props.groupList[i];
        }else{
          this.props.history.replace(`${currGroup._id}`);
        }
      }
    }else if(!groupName && this.props.groupList.length){
      this.props.history.push(`/group/${this.props.groupList[0]._id}`);
    }
    this.setState({groupList: this.props.groupList});
    this.props.setCurrGroup(currGroup)
  }

  @autobind
  showModal(type) {
    if (type === 'edit') {
      const { currGroup } = this.props;
      this.setState({
        currGroupName: currGroup.group_name,
        currGroupDesc: currGroup.group_desc,
        editGroupModalVisible: true
      });
    } else {
      this.setState({
        addGroupModalVisible: true
      });
    }
  }
  @autobind
  hideModal(type) {
    if (type === TYPE_EDIT) {
      this.setState({
        editGroupModalVisible: false
      });
    } else {
      this.setState({
        addGroupModalVisible: false
      });
    }
  }
  @autobind
  async addGroup() {
    const { newGroupName: group_name, newGroupDesc: group_desc } = this.state;
    const res = await axios.post('/api/group/add', { group_name, group_desc })
    if (!res.data.errcode) {
      this.setState({
        addGroupModalVisible: false
      });
      await this.props.fetchGroupList();
      this.setState({groupList: this.props.groupList});
      this.props.setCurrGroup(res.data.data)
    }
  }
  @autobind
  async editGroup() {
    const { currGroupName: group_name, currGroupDesc: group_desc } = this.state;
    const id = this.props.currGroup._id;
    const res = axios.post('/api/group/up', { group_name, group_desc, id });
    if (res.data.errcode) {
      message.error(res.data.errmsg);
    } else {
      this.setState({
        editGroupModalVisible: false
      });
      this.props.setCurrGroup({ group_name, group_desc, _id: id });
    }
  }
  @autobind
  inputNewGroupName(e, type) {
    if (type === TYPE_EDIT) {
      this.setState({ currGroupName: e.target.value})
    } else {
      this.setState({newGroupName: e.target.value});
    }
  }
  @autobind
  inputNewGroupDesc(e, type) {
    if (type === TYPE_EDIT) {
      this.setState({ currGroupDesc: e.target.value})
    } else {
      this.setState({newGroupDesc: e.target.value});
    }
  }

  @autobind
  selectGroup(e) {
    const groupId = e.key;
    const currGroup = this.props.groupList.find((group) => { return +group._id === +groupId });
    this.props.setCurrGroup(currGroup);
    this.props.history.replace(`${currGroup._id}`);
  }

  showConfirm =()=> {
    let that = this;
    confirm({
      title: that.props.currGroup.group_name,
      content:

        <Alert message="该操作会删除该分组下所有项目!" type="warning" />

请输入分组名称：<input id="group_name" />

,
      onOk() {
        let groupName = document.getElementById('group_name').value;
        if(that.props.currGroup.group_name !== groupName){
          message.error('分组名称有误')
          return new Promise((resolve, reject)=>{
            reject('error')
          })
        }else{
          that.deleteGroup()
        }

      },
      onCancel() { }
    });
  }

  @autobind
  async deleteGroup() {
    const self = this;
    const { currGroup } = self.props;
    const res = await axios.post('/api/group/del', {id: currGroup._id})
    if (res.data.errcode) {
      message.error(res.data.errmsg);
    } else {
      message.success('删除成功')
      await self.props.fetchGroupList()
      const currGroup = self.props.groupList[0] || { group_name: '', group_desc: '' };
      self.setState({groupList: self.props.groupList});
      self.props.setCurrGroup(currGroup)
    }
  }

  @autobind
  searchGroup(e, value) {
    const v = value || e.target.value;
    const { groupList } = this.props;
    if (v === '') {
      this.setState({groupList})
    } else {
      this.setState({groupList: groupList.filter(group => new RegExp(v, 'i').test(group.group_name))})
    }
  }

  render () {
    const { currGroup } = this.props;
    const delmark = <Icon className="edit-group" type="edit" title="编辑分组" onClick={() => this.showModal(TYPE_EDIT)}/>
    const editmark = <Icon className="delete-group" onClick={()=> {this.showConfirm()}}  type="delete" title="删除分组"/>

    return (

{currGroup.group_name}

              {
                this.props.curUserRole === "admin"?(editmark):''
              }
              {
                this.props.curUserRole === "admin"?(delmark):''
              }

简介：{currGroup.group_desc}

              <Search onChange={this.searchGroup} onSearch={(v) => this.searchGroup(null, v)}/>

            {
              this.props.curUserRole === "admin"?(<Button type="primary" onClick={this.showModal}>添加分组</Button>):''
            }

          <Menu
            className="group-list"
            mode="inline"
            onClick={this.selectGroup}
            selectedKeys={[`${currGroup._id}`]}
          >
            {
              this.state.groupList.map((group) => (
                <Menu.Item key={`${group._id}`} className="group-item">
                  <Icon type="folder-open" />{group.group_name}
                </Menu.Item>
              ))
            }
          </Menu>

        <Modal
          title="添加分组"
          visible={this.state.addGroupModalVisible}
          onOk={this.addGroup}
          onCancel={this.hideModal}
          className="add-group-modal"
        >
          <Row gutter={6} className="modal-input">
            <Col span="5">
分组名：
</Col>
            <Col span="15">
              <Input placeholder="请输入分组名称" onChange={this.inputNewGroupName}></Input>
            </Col>
          </Row>
          <Row gutter={6} className="modal-input">
            <Col span="5">
简介：
</Col>
            <Col span="15">
              <TextArea rows = {3} placeholder="请输入分组描述" onChange={this.inputNewGroupDesc}></TextArea>
            </Col>
          </Row>
        </Modal>
        <Modal
          title="编辑分组"
          visible={this.state.editGroupModalVisible}
          onOk={this.editGroup}
          onCancel={() => this.hideModal(TYPE_EDIT)}
          className="add-group-modal"
        >
          <Row gutter={6} className="modal-input">
            <Col span="5">
分组名：
</Col>
            <Col span="15">
              <Input placeholder="请输入分组名称" value={this.state.currGroupName} onChange={(e) => this.inputNewGroupName(e, TYPE_EDIT)}></Input>
            </Col>
          </Row>
          <Row gutter={6} className="modal-input">
            <Col span="5">
简介：
</Col>
            <Col span="15">
              <TextArea rows={3} placeholder="请输入分组描述" value={this.state.currGroupDesc} onChange={(e) => this.inputNewGroupDesc(e, TYPE_EDIT)}></TextArea>
            </Col>
          </Row>
        </Modal>

    )
  }
}
<commit_msg>feat: 修复删除项目bug和完善删除分组功能<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Button, Icon, Modal,Alert, Input, message, Menu, Row, Col } from 'antd'
import { autobind } from 'core-decorators';
import axios from 'axios';
import { withRouter } from 'react-router';
const { TextArea } = Input;
const Search = Input.Search;
const TYPE_EDIT = 'edit';
const confirm = Modal.confirm;
import {
  fetchGroupList,
  setCurrGroup,
  setGroupList
} from '../../../reducer/modules/group.js'

import './GroupList.scss'

@connect(
  state => ({
    groupList: state.group.groupList,
    currGroup: state.group.currGroup,
    curUserRole: state.user.role
  }),
  {
    fetchGroupList,
    setCurrGroup,
    setGroupList
  }
)
@withRouter
export default class GroupList extends Component {

  static propTypes = {
    groupList: PropTypes.array,
    currGroup: PropTypes.object,
    fetchGroupList: PropTypes.func,
    setCurrGroup: PropTypes.func,
    setGroupList: PropTypes.func,
    match: PropTypes.object,
    history: PropTypes.object,
    curUserRole: PropTypes.string
  }

  state = {
    addGroupModalVisible: false,
    editGroupModalVisible: false,
    newGroupName: '',
    newGroupDesc: '',
    currGroupName: '',
    currGroupDesc: '',
    groupList: []
  }

  constructor(props) {
    super(props)
  }

  async componentWillMount() {
    const groupName = this.props.match.params.groupName;
    await this.props.fetchGroupList();
    let currGroup = this.props.groupList[0] || { group_name: '', group_desc: '' };
    if(this.props.groupList.length && groupName){
      for(let i = 0;i<this.props.groupList.length;i++){
        if(this.props.groupList[i].group_name === groupName){
          currGroup = this.props.groupList[i];
        }else{
          this.props.history.replace(`${currGroup._id}`);
        }
      }
    }else if(!groupName && this.props.groupList.length){
      this.props.history.push(`/group/${this.props.groupList[0]._id}`);
    }
    this.setState({groupList: this.props.groupList});
    this.props.setCurrGroup(currGroup)
  }

  @autobind
  showModal(type) {
    if (type === 'edit') {
      const { currGroup } = this.props;
      this.setState({
        currGroupName: currGroup.group_name,
        currGroupDesc: currGroup.group_desc,
        editGroupModalVisible: true
      });
    } else {
      this.setState({
        addGroupModalVisible: true
      });
    }
  }
  @autobind
  hideModal(type) {
    if (type === TYPE_EDIT) {
      this.setState({
        editGroupModalVisible: false
      });
    } else {
      this.setState({
        addGroupModalVisible: false
      });
    }
  }
  @autobind
  async addGroup() {
    const { newGroupName: group_name, newGroupDesc: group_desc } = this.state;
    const res = await axios.post('/api/group/add', { group_name, group_desc })
    if (!res.data.errcode) {
      this.setState({
        addGroupModalVisible: false
      });
      await this.props.fetchGroupList();
      this.setState({groupList: this.props.groupList});
      this.props.setCurrGroup(res.data.data)
    }
  }
  @autobind
  async editGroup() {
    const { currGroupName: group_name, currGroupDesc: group_desc } = this.state;
    const id = this.props.currGroup._id;
    const res = axios.post('/api/group/up', { group_name, group_desc, id });
    if (res.data.errcode) {
      message.error(res.data.errmsg);
    } else {
      this.setState({
        editGroupModalVisible: false
      });
      this.props.setCurrGroup({ group_name, group_desc, _id: id });
    }
  }
  @autobind
  inputNewGroupName(e, type) {
    if (type === TYPE_EDIT) {
      this.setState({ currGroupName: e.target.value})
    } else {
      this.setState({newGroupName: e.target.value});
    }
  }
  @autobind
  inputNewGroupDesc(e, type) {
    if (type === TYPE_EDIT) {
      this.setState({ currGroupDesc: e.target.value})
    } else {
      this.setState({newGroupDesc: e.target.value});
    }
  }

  @autobind
  selectGroup(e) {
    const groupId = e.key;
    const currGroup = this.props.groupList.find((group) => { return +group._id === +groupId });
    this.props.setCurrGroup(currGroup);
    this.props.history.replace(`${currGroup._id}`);
  }

  showConfirm =()=> {
    let that = this;
    confirm({
      title: "确认删除"+that.props.currGroup.group_name+"分组吗？",
      content:

        <Alert message="警告：此操作非常危险,会删除该分组下面所有项目和接口，并且无法恢复!" type="warning" />

请输入分组名称确认此操作:<input id="group_name" />

,
      onOk() {
        let groupName = document.getElementById('group_name').value;
        if(that.props.currGroup.group_name !== groupName){
          message.error('分组名称有误')
          return new Promise((resolve, reject)=>{
            reject('error')
          })
        }else{
          that.deleteGroup()
        }

      },
      iconType: 'delete',
      onCancel() { }
    });
  }

  @autobind
  async deleteGroup() {
    const self = this;
    const { currGroup } = self.props;
    const res = await axios.post('/api/group/del', {id: currGroup._id})
    if (res.data.errcode) {
      message.error(res.data.errmsg);
    } else {
      message.success('删除成功')
      await self.props.fetchGroupList()
      const currGroup = self.props.groupList[0] || { group_name: '', group_desc: '' };
      self.setState({groupList: self.props.groupList});
      self.props.setCurrGroup(currGroup)
    }
  }

  @autobind
  searchGroup(e, value) {
    const v = value || e.target.value;
    const { groupList } = this.props;
    if (v === '') {
      this.setState({groupList})
    } else {
      this.setState({groupList: groupList.filter(group => new RegExp(v, 'i').test(group.group_name))})
    }
  }

  render () {
    const { currGroup } = this.props;
    const delmark = <Icon className="edit-group" type="edit" title="编辑分组" onClick={() => this.showModal(TYPE_EDIT)}/>
    const editmark = <Icon className="delete-group" onClick={()=> {this.showConfirm()}}  type="delete" title="删除分组"/>

    return (

{currGroup.group_name}

              {
                this.props.curUserRole === "admin"?(editmark):''
              }
              {
                this.props.curUserRole === "admin"?(delmark):''
              }

简介：{currGroup.group_desc}

              <Search onChange={this.searchGroup} onSearch={(v) => this.searchGroup(null, v)}/>

            {
              this.props.curUserRole === "admin"?(<Button type="primary" onClick={this.showModal}>添加分组</Button>):''
            }

          <Menu
            className="group-list"
            mode="inline"
            onClick={this.selectGroup}
            selectedKeys={[`${currGroup._id}`]}
          >
            {
              this.state.groupList.map((group) => (
                <Menu.Item key={`${group._id}`} className="group-item">
                  <Icon type="folder-open" />{group.group_name}
                </Menu.Item>
              ))
            }
          </Menu>

        <Modal
          title="添加分组"
          visible={this.state.addGroupModalVisible}
          onOk={this.addGroup}
          onCancel={this.hideModal}
          className="add-group-modal"
        >
          <Row gutter={6} className="modal-input">
            <Col span="5">
分组名：
</Col>
            <Col span="15">
              <Input placeholder="请输入分组名称" onChange={this.inputNewGroupName}></Input>
            </Col>
          </Row>
          <Row gutter={6} className="modal-input">
            <Col span="5">
简介：
</Col>
            <Col span="15">
              <TextArea rows = {3} placeholder="请输入分组描述" onChange={this.inputNewGroupDesc}></TextArea>
            </Col>
          </Row>
        </Modal>
        <Modal
          title="编辑分组"
          visible={this.state.editGroupModalVisible}
          onOk={this.editGroup}
          onCancel={() => this.hideModal(TYPE_EDIT)}
          className="add-group-modal"
        >
          <Row gutter={6} className="modal-input">
            <Col span="5">
分组名：
</Col>
            <Col span="15">
              <Input placeholder="请输入分组名称" value={this.state.currGroupName} onChange={(e) => this.inputNewGroupName(e, TYPE_EDIT)}></Input>
            </Col>
          </Row>
          <Row gutter={6} className="modal-input">
            <Col span="5">
简介：
</Col>
            <Col span="15">
              <TextArea rows={3} placeholder="请输入分组描述" value={this.state.currGroupDesc} onChange={(e) => this.inputNewGroupDesc(e, TYPE_EDIT)}></TextArea>
            </Col>
          </Row>
        </Modal>

    )
  }
}
<|endoftext|>


<commit_before>var dojoConfig = {
  baseUrl: './',
  packages: [
    {
      name: 'dojo',
      location: 'deps/dojo'
    },
    {
      name: 'dcl',
      location: 'deps/dcl',
      main: 'dcl'
    },
    {
      name: 'frozen',
      location: 'deps/frozen/src',
      main: 'GameCore'
    },
    {
      name: 'lodash',
      location: 'deps/lodash/dist',
      main: 'lodash'
    },
    {
      name: 'game',
      location: 'src',
      main: 'game'
    },
    {
      name: 'sounds',
      location: 'resources/sfx'
    }
  ],
  deps: ['game'],
  async: true
};<commit_msg>cleanup dojoConfig<commit_after>var dojoConfig = {
  baseUrl: './',
  packages: [
    { name: 'dojo', location: 'deps/dojo' },
    { name: 'dcl', location: 'deps/dcl', main: 'dcl' },
    { name: 'lodash', location: 'deps/lodash/dist', main: 'lodash' },
    { name: 'hammer', location: 'deps/hammer', main: 'hammer' },
    { name: 'frozen', location: 'deps/frozen/src', main: 'GameCore' },
    { name: 'game', location: 'src', main: 'game' },
    { name: 'sounds', location: 'resources/sfx' }
  ],
  deps: ['game'],
  async: true
};<|endoftext|>


<commit_before>var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
  res.sendfile('index.html');
});

io.on('connection', function(socket){
  console.log('a user connected');
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

io.on('connection', function(socket){
  console.log('a user connected');
  socket.on('disconnect', function(){
    console.log('user disconnected');
  });
});

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    console.log('message: ' + msg);
  });
});
io.emit('some event', { for: 'everyone' });
io.on('connection', function(socket){
  socket.broadcast.emit('hi');
});
io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  });
});<commit_msg>edit node.js<commit_after>var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(server);
var port = process.env.PORT || 3000;
app.get('/', function(req, res){
  res.sendfile('index.html');
});

io.on('connection', function(socket){
  console.log('a user connected');
});

io.on('connection', function(socket){
  console.log('a user connected');
  socket.on('disconnect', function(){
    console.log('user disconnected');
  });
});

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    console.log('message: ' + msg);
  });
});
io.emit('some event', { for: 'everyone' });
io.on('connection', function(socket){
  socket.broadcast.emit('hi');
});
io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  });
});<|endoftext|>