<commit_before>import { routerReducer as routing } from 'react-router-redux';
import { reducer as form } from 'redux-form';
import { combineReducers } from 'redux';
import { APP_TOOLS, APP_TOPIC_MAPPER, APP_SOURCE_MANAGER, APP_EXPLORER } from '../config';
import app from './app';
import user from './user';
import topics from './topics/topics';
import explorer from './explorer/explorer';
import sources from './sources/sources';
import system from './system/system';
import notebook from './notebook/notebook';
import story from './story/story';

function getRootReducer(appName) {
  // only load the reducer for the appropriate app
  let appReducer;
  switch (appName) {
    case APP_TOPIC_MAPPER:
      appReducer = topics;
      break;
    case APP_SOURCE_MANAGER:
      appReducer = sources;
      break;
    case APP_EXPLORER:
      appReducer = explorer;
      break;
    case APP_TOOLS:
    default:
      appReducer = null;
      break;
  }

  const defaultReducers = {
    app,
    user,
    system,
    form,
    routing,
    notebook,
    story,
  };

  let reducers = {};
  reducers[appName] = appReducer;
  reducers = Object.assign(reducers, defaultReducers);

  const rootReducer = combineReducers(reducers);

  return rootReducer;
}

export default getRootReducer;
<commit_msg>include all reducers again (#786, #756)<commit_after>import { routerReducer as routing } from 'react-router-redux';
import { reducer as form } from 'redux-form';
import { combineReducers } from 'redux';
import { APP_TOOLS, APP_TOPIC_MAPPER, APP_SOURCE_MANAGER, APP_EXPLORER } from '../config';
import app from './app';
import user from './user';
import topics from './topics/topics';
import explorer from './explorer/explorer';
import sources from './sources/sources';
import system from './system/system';
import notebook from './notebook/notebook';
import story from './story/story';

const LIMIT_REDUCERS = false; // change to true once Topics decoupled from Sources

function getRootReducer(appName) {
  let appReducers;
  if (LIMIT_REDUCERS) {
    // only load the reducer for the appropriate app
    switch (appName) {
      case APP_TOPIC_MAPPER:
        appReducers = { topics };
        break;
      case APP_SOURCE_MANAGER:
        appReducers = { sources };
        break;
      case APP_EXPLORER:
        appReducers = { explorer };
        break;
      case APP_TOOLS:
      default:
        appReducers = {};
        break;
    }
  } else {
    appReducers = { topics, sources, explorer };
  }

  const defaultReducers = {
    app,
    user,
    system,
    form,
    routing,
    notebook,
    story,
  };

  let reducers = { ...appReducers };
  reducers = Object.assign(reducers, defaultReducers);

  const rootReducer = combineReducers(reducers);

  return rootReducer;
}

export default getRootReducer;
<|endoftext|>


<commit_before>"use strict";

(function(exports) {
  var fs = require("fs");
  var os = require("os");
  var path = require("path");
  var child_process = require("child_process");
  var njds = require("nodejs-disks");

  var selectedItems = [];
  var activeItem = null;
  var activeItemIndex = 0;
  var activeListEl = null;

  var pathHistory = [];

  var currentSearchString = "";

  var openItem = function(item) {
    if(!item) return;
    if(item.type == "directory") {
        if(item.name == "..") {

          if(item.root == item.directory) {
            loadDrives();
            return;
          }
        }
        process.chdir(process.cwd() + "/" + item.textContent);
        clearSearch();
        loadDirectory();
    } else
    if(item.type == "file") {
      switch(os.platform().toLowerCase()) {

        case "linux":
          // Linux handles opening files differently. It's defined by
          // the window managers / desktop environments / whatever they
          // choose to run their software from...
          //break;
        case "win32":
        default:
          var child = child_process.exec(item.textContent);
          child.unref();
          break;
      }
    } else
    if(item.type == "drive") {
      switch(os.platform().toLowerCase()) {
        case 'win32':
          process.chdir(item.textContent + "\\");
          loadDirectory();
          break;
        default:
          process.chdir(item.getAttribute("mountpoint"));
          loadDirectory();
          break;
      }
    }
  };

  var deselectAll = function() {
    for(var i=0; selectedItems.length > i; i++) {
      selectedItems[i].classList.remove("selected");
    }

    selectedItems = [];
  };

  var deselectAllExcept = function(inExceptions) {
    let exceptions = Array.isArray(inExceptions) ? inExceptions : arguments;

    deselectAll();
    for(let i=0; exceptions.length > i; i++) {
      const item = exceptions[i];
      selectItem(item);
    }
  };

  var selectItem = function(item) {
    if(!item) return;
    selectedItems.push(item);
    item.classList.add("selected");
  };

  var getItemIndex = function(item) {
    if(item.parentElement != activeListEl) return -1;
    var c = activeListEl.firstElementChild;
    var i=0;
    while(c) {
      if(c == item) break;
      i++;
      c = c.nextElementSibling;
    }

    return i;
  };

  var setActiveItem = function(el) {
    if(el === null) {
      if(activeItem) {
        activeItem.classList.remove("active");
        activeItem = null;
      }
    } else
    if(el.parentElement == activeListEl) {
      if(activeItem) activeItem.classList.remove("active");
      activeItem = el;
      activeItem.classList.add("active");
      activeItem.focus();
      activeItem.scrollIntoViewIfNeeded();

      var i=0;
      var c = activeListEl.firstElementChild;
      while(c) {
        if(c == el) break;
        i++;
        c = c.nextElementSibling;
      }

      activeItemIndex = i;
    }
  };

  var updateSearch = function() {
    var items = document.querySelectorAll("atomfm-item");

    document.querySelector("#atomfm-search-bar").textContent = currentSearchString;

    let selit = null;
    let numItemsFound = 0;

    deselectAll();

    for(let i=0; items.length > i; i++) {
      let item = items[i];
      const itemText = item.textContent.trim();
      if(itemText.startsWith(currentSearchString)) {
        numItemsFound += 1;
        if(selit === null) {
          selit = item;
          selectItem(selit);
          setActiveItem(selit);
        }
        item.innerHTML = "";
        let hlEl = document.createElement("span");
        hlEl.classList.add("searched-text");
        hlEl.textContent = currentSearchString;
        item.appendChild(hlEl);
        item.appendChild(document.createTextNode(itemText.substr(currentSearchString.length)));
      } else {
        item.innerHTML = item.textContent;
      }
    }

    if(numItemsFound == 0) {

    }

  };

  var clearSearch = function() {
    currentSearchString = "";
    updateSearch();
  };

  var AtomFileManagerItemElementPrototype = Object.create(HTMLElement.prototype);

  Object.defineProperty(AtomFileManagerItemElementPrototype, "type", {
    get: function() {
      return this.getAttribute("type");
    },
    set: function(val) {
      if(val == "") {
        this.removeAttribute("type");
      } else {
        this.setAttribute("type", val);
      }
    }
  });

  Object.defineProperty(AtomFileManagerItemElementPrototype, "path", {
    get: function() {
      return path.format({
        root: this.getAttribute("root"),
        dir: this.getAttribute("directory"),
        ext: this.getAttribute("ext"),
        name: this.getAttribute("name")
      });
    },

    set: function(val) {
      var parsedPath = path.parse(val);
      this.setAttribute("root", parsedPath.root);
      this.setAttribute("directory", parsedPath.dir);
      this.setAttribute("ext", parsedPath.ext);
      this.setAttribute("name", parsedPath.name);
    }
  });

  Object.defineProperties(AtomFileManagerItemElementPrototype, {
    "root": {
      get: function() {
        return this.getAttribute("root");
      },
      set: function(val) {
        if(val == "") this.removeAttribute("root");
        else this.setAttribute("root", val);
      }
    },
    "directory": {
      get: function() {
        return this.getAttribute("directory");
      },
      set: function(val) {
        if(val == "") this.removeAttribute("directory");
        else this.setAttribute("directory", val);
      }
    },
    "ext": {
      get: function() {
        return this.getAttribute("ext");
      },
      set: function(val) {
        if(val == "") this.removeAttribute("ext");
        else this.setAttribute("ext", val);
      }
    },
    "name": {
      get: function() {
        return this.getAttribute("name");
      },
      set: function(val) {
        if(val == "") this.removeAttribute("name");
        else this.setAttribute("name", val);
      }
    },
  });

  AtomFileManagerItemElementPrototype.createdCallback = function(el) {
    var self = this;
    this.addEventListener("click", function(e) {
      if(e.ctrlKey) {

      } else
      if(e.shiftKey) {
        var currentIndex = getItemIndex(self);
        var activeIndex = activeItemIndex;
        deselectAll();
        if(currentIndex < activeIndex) {
          for(var i=currentIndex; i < activeIndex; i++) {
            selectItem(activeListEl.children[i]);
          }
          selectItem(activeItem);
        } else
        if(activeIndex < currentIndex) {
          for(var i=activeIndex; i < currentIndex; i ++) {
            selectItem(activeListEl.children[i])
          }
          selectItem(activeItem);
        }

      } else {
        deselectAll();
        selectedItems = [];
      }

      setActiveItem(self);
      self.classList.add("selected");
      self.classList.add("active");
      selectedItems.push(this);
    });

    this.addEventListener("dblclick", function(e) {
      openItem(this);
    });

    this.addEventListener("focus", function(e) {
      this.scrollIntoView();
      console.log(e);
    });
  };

  window.addEventListener("popstate", function(e) {
    if(e.state) {
      process.chdir(e.state);
      loadDirectory(e.state, false);
    }
  });

  window.addEventListener("click", function(e) {

    if(e.srcElement instanceof AtomFileManagerItemElement) return;

    for(var i=0; selectedItems.length > i; i++) {
      var selectedItem = selectedItems[i];
      if(selectedItems) selectedItem.classList.remove("selected");
    }

    selectedItems = [];
  });

  window.addEventListener("keydown", function(e) {

    // Escape key
    if(e.which == 27) {
      setActiveItem(null);
      clearSearch();

      if(activeItem) {
        activeItem.classList.remove("active");
        activeItem = null;
        deselectAll();
      }
    } else
    // Return key
    if(e.which == 13) {
      if(selectedItems.length > 1) {
        openItem(activeItem || activeListEl.firstElementChild);
      } else {
        openItem(selectedItems[0] || activeListEl.firstElementChild);
      }
    } else
    // F5 key
    if(e.which == 116) {
      loadDirectory();
    } else
    // Up Arrow
    if(e.which == 38) {
      if(!e.shiftKey && !e.ctrlKey) deselectAll();
      if(activeItemIndex > 0) {
        setActiveItem(activeListEl.children[activeItemIndex -1]);
      } else
      if(activeItemIndex == 0) {
        setActiveItem(activeListEl.children[activeItemIndex]);
      }
      if(!e.ctrlKey) {
        selectItem(activeItem);
      }

      e.preventDefault();

    } else
    // Down Arrow
    if(e.which == 40) {
      if(!e.shiftKey && !e.ctrlKey) deselectAll();
      if(activeItemIndex + 1 < activeListEl.children.length) {
        setActiveItem(activeListEl.children[activeItemIndex + 1]);
      }
      if(!e.ctrlKey) {
        selectItem(activeItem);
      }

      e.preventDefault();
    } else
    // Right Arrow
    if(e.which == 39) {
      if(e.altKey) {
        history.forward();
      }
    } else
    // Left Arrow
    if(e.which == 37) {
      if(e.altKey) {
        history.back();
      }
    } else
    // Backspace
    if(e.which == 8) {
      if(currentSearchString.length > 0) {
        currentSearchString = currentSearchString.substr(0, currentSearchString.length-1);
        updateSearch();
      }
    }

  });

  window.addEventListener("keypress", function(e) {
    if(e.ctrlKey || e.altKey) return;

    currentSearchString += String.fromCharCode(e.charCode);
    updateSearch();
  });

  var AtomFileManagerListElement = document.registerElement("atomfm-list");
  var AtomFileManagerItemElement = document.registerElement("atomfm-item",{
    prototype: AtomFileManagerItemElementPrototype
  });

  exports.AtomFileManagerListElement = AtomFileManagerListElement;

  function loadDirectory(dir, affectHistory) {
    if(!activeListEl) return;
    var dir = dir || process.cwd();

    activeItemIndex = 0;
    activeItem = null;
    activeListEl.innerHTML = "";
    if(affectHistory !== false) {
      history.pushState(dir, "", dir);
    }
    fs.readdir(dir, function(err, paths) {
      paths.unshift("..");
      for(var i=0; paths.length > i; i++) {

        (function() {
          var filePath = paths[i];
          var pathEl = document.createElement("atomfm-item");
          pathEl.textContent = filePath;
          pathEl.path = dir + "/" + filePath;
          activeListEl.appendChild(pathEl);
          fs.stat(filePath, function(err, stats) {
            if(err) {
              return;
            }
            var pathType = "unknown";
            if(stats.isFile()) pathType = "file";
            else if(stats.isDirectory()) pathType = "directory";
            else if(stats.isBlockDevice()) pathType = "block-device";
            else if(stats.isCharacterDevice()) pathType = "character-device";
            else if(stats.isSymbolicLink()) pathType = "symbolic-link";
            else if(stats.isFIFO()) pathType = "fifo";
            else if(stats.isSocket()) pathType = "socket";
            pathEl.setAttribute("type", pathType);
          });
        }());

        clearSearch();
      }
    });
  }

  function loadDrives() {
    if(!activeListEl) return;

    njds.drives(function(err, drives) {
      for(var i=0; drives.length > i; i++) {
        njds.drivesDetail(drives, function(err, details) {
          activeListEl.innerHTML = "";
          for(var i=0; details.length > i; i++) {
            var driveDetail = details[i];

            var driveEl = document.createElement("atomfm-item");
            driveEl.type = "drive";
            driveEl.textContent = driveDetail.drive;
            driveEl.setAttribute("mountpoint", driveDetail.mountpoint);

            activeListEl.appendChild(driveEl);
          }
        });

      }
    })
  }

  document.addEventListener("readystatechange", function() {
    if(document.readyState == "complete") {
      activeListEl = document.getElementsByTagName("atomfm-list")[0];
      loadDirectory(process.cwd());
    }
  });

}(window));
<commit_msg>Performance improvement when changing directory.<commit_after>"use strict";

(function(exports) {
  var fs = require("fs");
  var os = require("os");
  var path = require("path");
  var child_process = require("child_process");
  var njds = require("nodejs-disks");

  var selectedItems = [];
  var activeItem = null;
  var activeItemIndex = 0;
  var activeListEl = null;

  var pathHistory = [];

  var currentSearchString = "";

  var openItem = function(item) {
    if(!item) return;
    if(item.type == "directory") {
        if(item.name == "..") {

          if(item.root == item.directory) {
            loadDrives();
            return;
          }
        }
        process.chdir(process.cwd() + "/" + item.textContent);
        clearSearch();
        loadDirectory();
    } else
    if(item.type == "file") {
      switch(os.platform().toLowerCase()) {

        case "linux":
          // Linux handles opening files differently. It's defined by
          // the window managers / desktop environments / whatever they
          // choose to run their software from...
          //break;
        case "win32":
        default:
          var child = child_process.exec(item.textContent);
          child.unref();
          break;
      }
    } else
    if(item.type == "drive") {
      switch(os.platform().toLowerCase()) {
        case 'win32':
          process.chdir(item.textContent + "\\");
          loadDirectory();
          break;
        default:
          process.chdir(item.getAttribute("mountpoint"));
          loadDirectory();
          break;
      }
    }
  };

  var deselectAll = function() {
    for(var i=0; selectedItems.length > i; i++) {
      selectedItems[i].classList.remove("selected");
    }

    selectedItems = [];
  };

  var deselectAllExcept = function(inExceptions) {
    let exceptions = Array.isArray(inExceptions) ? inExceptions : arguments;

    deselectAll();
    for(let i=0; exceptions.length > i; i++) {
      const item = exceptions[i];
      selectItem(item);
    }
  };

  var selectItem = function(item) {
    if(!item) return;
    selectedItems.push(item);
    item.classList.add("selected");
  };

  var getItemIndex = function(item) {
    if(item.parentElement != activeListEl) return -1;
    var c = activeListEl.firstElementChild;
    var i=0;
    while(c) {
      if(c == item) break;
      i++;
      c = c.nextElementSibling;
    }

    return i;
  };

  var setActiveItem = function(el) {
    if(el === null) {
      if(activeItem) {
        activeItem.classList.remove("active");
        activeItem = null;
      }
    } else
    if(el.parentElement == activeListEl) {
      if(activeItem) activeItem.classList.remove("active");
      activeItem = el;
      activeItem.classList.add("active");
      activeItem.focus();
      activeItem.scrollIntoViewIfNeeded();

      var i=0;
      var c = activeListEl.firstElementChild;
      while(c) {
        if(c == el) break;
        i++;
        c = c.nextElementSibling;
      }

      activeItemIndex = i;
    }
  };

  var updateSearch = function() {
    var items = document.querySelectorAll("atomfm-item");

    document.querySelector("#atomfm-search-bar").textContent = currentSearchString;

    let selit = null;
    let numItemsFound = 0;

    deselectAll();

    for(let i=0; items.length > i; i++) {
      let item = items[i];
      const itemText = item.textContent.trim();
      if(itemText.startsWith(currentSearchString)) {
        numItemsFound += 1;
        if(selit === null) {
          selit = item;
          selectItem(selit);
          setActiveItem(selit);
        }
        item.innerHTML = "";
        let hlEl = document.createElement("span");
        hlEl.classList.add("searched-text");
        hlEl.textContent = currentSearchString;
        item.appendChild(hlEl);
        item.appendChild(document.createTextNode(itemText.substr(currentSearchString.length)));
      } else {
        item.innerHTML = item.textContent;
      }
    }

    if(numItemsFound == 0) {

    }

  };

  var clearSearch = function() {
    currentSearchString = "";
    var searchedTextEls = document.querySelectorAll("atomfm-item > .searched-text");
    for(let i=0; searchedTextEls.length > i; i++) {
      const searchedTextEl = searchedTextEls[i];
      const parent = searchedTextEl.parentElement;
      const searchedText = searchedTextEl.textContent;
      searchedTextEl.remove();

      if(searchedText.length > 0) {
        parent.insertBefore(document.createTextNode(searchedText), parent.firstChild);
      }
    }
  };

  var AtomFileManagerItemElementPrototype = Object.create(HTMLElement.prototype);

  Object.defineProperty(AtomFileManagerItemElementPrototype, "type", {
    get: function() {
      return this.getAttribute("type");
    },
    set: function(val) {
      if(val == "") {
        this.removeAttribute("type");
      } else {
        this.setAttribute("type", val);
      }
    }
  });

  Object.defineProperty(AtomFileManagerItemElementPrototype, "path", {
    get: function() {
      return path.format({
        root: this.getAttribute("root"),
        dir: this.getAttribute("directory"),
        ext: this.getAttribute("ext"),
        name: this.getAttribute("name")
      });
    },

    set: function(val) {
      var parsedPath = path.parse(val);
      this.setAttribute("root", parsedPath.root);
      this.setAttribute("directory", parsedPath.dir);
      this.setAttribute("ext", parsedPath.ext);
      this.setAttribute("name", parsedPath.name);
    }
  });

  Object.defineProperties(AtomFileManagerItemElementPrototype, {
    "root": {
      get: function() {
        return this.getAttribute("root");
      },
      set: function(val) {
        if(val == "") this.removeAttribute("root");
        else this.setAttribute("root", val);
      }
    },
    "directory": {
      get: function() {
        return this.getAttribute("directory");
      },
      set: function(val) {
        if(val == "") this.removeAttribute("directory");
        else this.setAttribute("directory", val);
      }
    },
    "ext": {
      get: function() {
        return this.getAttribute("ext");
      },
      set: function(val) {
        if(val == "") this.removeAttribute("ext");
        else this.setAttribute("ext", val);
      }
    },
    "name": {
      get: function() {
        return this.getAttribute("name");
      },
      set: function(val) {
        if(val == "") this.removeAttribute("name");
        else this.setAttribute("name", val);
      }
    },
  });

  AtomFileManagerItemElementPrototype.createdCallback = function(el) {
    var self = this;
    this.addEventListener("click", function(e) {
      if(e.ctrlKey) {

      } else
      if(e.shiftKey) {
        var currentIndex = getItemIndex(self);
        var activeIndex = activeItemIndex;
        deselectAll();
        if(currentIndex < activeIndex) {
          for(var i=currentIndex; i < activeIndex; i++) {
            selectItem(activeListEl.children[i]);
          }
          selectItem(activeItem);
        } else
        if(activeIndex < currentIndex) {
          for(var i=activeIndex; i < currentIndex; i ++) {
            selectItem(activeListEl.children[i])
          }
          selectItem(activeItem);
        }

      } else {
        deselectAll();
        selectedItems = [];
      }

      setActiveItem(self);
      self.classList.add("selected");
      self.classList.add("active");
      selectedItems.push(this);
    });

    this.addEventListener("dblclick", function(e) {
      openItem(this);
    });

    this.addEventListener("focus", function(e) {
      this.scrollIntoView();
      console.log(e);
    });
  };

  window.addEventListener("popstate", function(e) {
    if(e.state) {
      process.chdir(e.state);
      loadDirectory(e.state, false);
    }
  });

  window.addEventListener("click", function(e) {

    if(e.srcElement instanceof AtomFileManagerItemElement) return;

    for(var i=0; selectedItems.length > i; i++) {
      var selectedItem = selectedItems[i];
      if(selectedItems) selectedItem.classList.remove("selected");
    }

    selectedItems = [];
  });

  window.addEventListener("keydown", function(e) {

    // Escape key
    if(e.which == 27) {
      setActiveItem(null);
      clearSearch();

      if(activeItem) {
        activeItem.classList.remove("active");
        activeItem = null;
        deselectAll();
      }
    } else
    // Return key
    if(e.which == 13) {
      if(selectedItems.length > 1) {
        openItem(activeItem || activeListEl.firstElementChild);
      } else {
        openItem(selectedItems[0] || activeListEl.firstElementChild);
      }
    } else
    // F5 key
    if(e.which == 116) {
      loadDirectory();
    } else
    // Up Arrow
    if(e.which == 38) {
      if(!e.shiftKey && !e.ctrlKey) deselectAll();
      if(activeItemIndex > 0) {
        setActiveItem(activeListEl.children[activeItemIndex -1]);
      } else
      if(activeItemIndex == 0) {
        setActiveItem(activeListEl.children[activeItemIndex]);
      }
      if(!e.ctrlKey) {
        selectItem(activeItem);
      }

      e.preventDefault();

    } else
    // Down Arrow
    if(e.which == 40) {
      if(!e.shiftKey && !e.ctrlKey) deselectAll();
      if(activeItemIndex + 1 < activeListEl.children.length) {
        setActiveItem(activeListEl.children[activeItemIndex + 1]);
      }
      if(!e.ctrlKey) {
        selectItem(activeItem);
      }

      e.preventDefault();
    } else
    // Right Arrow
    if(e.which == 39) {
      if(e.altKey) {
        history.forward();
      }
    } else
    // Left Arrow
    if(e.which == 37) {
      if(e.altKey) {
        history.back();
      }
    } else
    // Backspace
    if(e.which == 8) {
      if(currentSearchString.length > 0) {
        currentSearchString = currentSearchString.substr(0, currentSearchString.length-1);
        updateSearch();
      }
    }

  });

  window.addEventListener("keypress", function(e) {
    if(e.ctrlKey || e.altKey) return;

    currentSearchString += String.fromCharCode(e.charCode);
    updateSearch();
  });

  var AtomFileManagerListElement = document.registerElement("atomfm-list");
  var AtomFileManagerItemElement = document.registerElement("atomfm-item",{
    prototype: AtomFileManagerItemElementPrototype
  });

  exports.AtomFileManagerListElement = AtomFileManagerListElement;

  function loadDirectory(dir, affectHistory) {
    if(!activeListEl) return;
    var dir = dir || process.cwd();

    activeItemIndex = 0;
    activeItem = null;
    activeListEl.innerHTML = "";
    if(affectHistory !== false) {
      history.pushState(dir, "", dir);
    }
    fs.readdir(dir, function(err, paths) {
      paths.unshift("..");
      for(var i=0; paths.length > i; i++) {

        (function() {
          var filePath = paths[i];
          var pathEl = document.createElement("atomfm-item");
          pathEl.textContent = filePath;
          pathEl.path = dir + "/" + filePath;
          activeListEl.appendChild(pathEl);
          fs.stat(filePath, function(err, stats) {
            if(err) {
              return;
            }
            var pathType = "unknown";
            if(stats.isFile()) pathType = "file";
            else if(stats.isDirectory()) pathType = "directory";
            else if(stats.isBlockDevice()) pathType = "block-device";
            else if(stats.isCharacterDevice()) pathType = "character-device";
            else if(stats.isSymbolicLink()) pathType = "symbolic-link";
            else if(stats.isFIFO()) pathType = "fifo";
            else if(stats.isSocket()) pathType = "socket";
            pathEl.setAttribute("type", pathType);
          });
        }());

        clearSearch();
      }
    });
  }

  function loadDrives() {
    if(!activeListEl) return;

    njds.drives(function(err, drives) {
      for(var i=0; drives.length > i; i++) {
        njds.drivesDetail(drives, function(err, details) {
          activeListEl.innerHTML = "";
          for(var i=0; details.length > i; i++) {
            var driveDetail = details[i];

            var driveEl = document.createElement("atomfm-item");
            driveEl.type = "drive";
            driveEl.textContent = driveDetail.drive;
            driveEl.setAttribute("mountpoint", driveDetail.mountpoint);

            activeListEl.appendChild(driveEl);
          }
        });

      }
    })
  }

  document.addEventListener("readystatechange", function() {
    if(document.readyState == "complete") {
      activeListEl = document.getElementsByTagName("atomfm-list")[0];
      loadDirectory(process.cwd());
    }
  });

}(window));
<|endoftext|>


<commit_before>import h from '../hyperscript.js';
import colors from '../colors.js';
import SiimpleComponent from '../index.js';

import sizes from './sizes.js';

//Footer layout component
export default class SiimpleFooter extends SiimpleComponent
{
  //Render the footer component
  render()
  {
    //Initialize the footer class list
    var class_list = [ 'siimple-footer' ];

    //Check the color
    if(typeof this.props.color === 'string' && colors.list.indexOf(this.props.color.toLowerCase()) !== -1)
    {
      //Add the color class
      class_list.push('siimple-footer--' + this.props.color.toLowerCase());
    }

    //Check the content size
    if(typeof this.props.size === 'string' && sizes.indexOf(this.props.size.toLowerCase()) !== -1)
    {
      //Add the size class
      class_list.push('siimple-footer--' + this.props.size.toLowerCase());
    }

    //Render the footer div
    return h.div({ className: class_list }, this.props.children);
  }
}

//Default props
SiimpleFooter.defaultProps = { color: null, size: null };
<commit_msg>Big rewrite<commit_after>import React from "react";
import {hyperscript as h} from "neutrine-utils";

import "siimple/scss/layout/_footer.scss";

//Footer layout component
export default class Footer extends React.Component {
    render() {
        //Initialize the footer class list
        let classList = ["siimple-footer"];
        //Check the color
        if (typeof this.props.color === "string") {
            classList.push("siimple-footer--" + this.props.color.toLowerCase());
        }
        //Check the content size
        if (typeof this.props.size === "string") {
            classList.push("siimple-footer--" + this.props.size.toLowerCase());
        }
        //Render the footer div
        return h("div", {className: classList, style: this.props.style}, this.props.children);
    }
}

//Default props
Footer.defaultProps = {
    color: null,
    size: null,
    style: null
};

<|endoftext|>


<commit_before><commit_msg>fix name conflict for variable<commit_after><|endoftext|>


<commit_before>dagre.layout = function() {
  // External configuration
  var
      // Nodes to lay out. At minimum must have `width` and `height` attributes.
      nodes = [],
      // Edges to lay out. At mimimum must have `source` and `target` attributes.
      edges = [],
      // Min separation between adjacent nodes in the same rank.
      nodeSep = 50,
      // Min separation between adjacent edges in the same rank.
      edgeSep = 10,
      // Min separation between ranks.
      rankSep = 30,
      // Number of passes to take during the ordering phase.
      orderIters = 24,
      // Debug positioning with a particular direction (up-left, up-right, down-left, down-right).
      posDir = null,
      // How much debug information to include?
      debugLevel = 0;

  // Internal state
  var
      // Graph used to determine relationships quickly
      g;

  // Phase functions
  var
      rank = dagre.layout.rank(),
      order = dagre.layout.order(),
      position = dagre.layout.position();

  // This layout object
  var self = {};

  self.nodes = function(x) {
    if (!arguments.length) return nodes;
    nodes = x;
    return self;
  };

  self.edges = function(x) {
    if (!arguments.length) return edges;
    edges = x;
    return self;
  };

  self.orderIters = function(x) {
    if (!arguments.length) return order.iterations();
    order.iterations(x);
    return self;
  }

  self.nodeSep = function(x) {
    if (!arguments.length) return position.nodeSep();
    position.nodeSep(x);
    return self;
  };

  self.edgeSep = function(x) {
    if (!arguments.length) return position.edgeSep();
    position.edgeSep(x);
    return self;
  }

  self.rankSep = function(x) {
    if (!arguments.length) return position.rankSep();
    position.rankSep(x);
    return self;
  }

  self.posDir = function(x) {
    if (!arguments.length) return position.direction();
    position.direction(x);
    return self;
  }

  self.debugLevel = function(x) {
    if (!arguments.length) return debugLevel;
    debugLevel = x;
    rank.debugLevel(x);
    order.debugLevel(x);
    position.debugLevel(x);
    return self;
  }

  self.run = function() {
    var timer = createTimer();

    // Build internal graph
    init();

    if (g.nodes().length === 0) {
      // Nothing to do!
      return;
    }

    var reversed = acyclic(g);

    rank.run(g);
    addDummyNodes();
    order.run(g);
    position.run(g);
    collapseDummyNodes();

    undoAcyclic(reversed);

    g = null;

    if (debugLevel >= 1) {
      console.log("Total layout time: " + timer.elapsedString());
    }
  };

  return self;

  // Build graph and save mapping of generated ids to original nodes and edges
  function init() {
    g = dagre.graph();

    var nextId = 0;

    // Tag each node so that we can properly represent relationships when
    // we add edges. Also copy relevant dimension information.
    nodes.forEach(function(u) {
      var id = "id" in u ? u.id : "_N" + nextId++;
      u.dagre = { id: id, width: u.width, height: u.height };
      g.addNode(id, u.dagre);
    });

    edges.forEach(function(e) {
      var source = e.source.dagre.id;
      if (!g.hasNode(source)) {
        throw new Error("Source node for '" + e + "' not in node list");
      }

      var target = e.target.dagre.id;
      if (!g.hasNode(target)) {
        throw new Error("Target node for '" + e + "' not in node list");
      }

      e.dagre = {
        points: []
      };

      // Track edges that aren't self loops - layout does nothing for self
      // loops, so they can be skipped.
      if (source !== target) {
        var id = "id" in e ? e.id : "_E" + nextId++;
        // TODO should we use prototypal inheritance for this?
        if (e.minLen) {
          e.dagre.minLen = e.minLen;
        }
        g.addEdge(id, source, target, e.dagre);
      }
    });
  }

  function acyclic(g) {
    var onStack = {};
    var visited = {};
    var reversed = [];

    function dfs(u) {
      if (u in visited)
        return;

      visited[u] = true;
      onStack[u] = true;
      g.outEdges(u).forEach(function(e) {
        var v = g.target(e);
        if (v in onStack) {
          var edge = g.edge(e);
          g.delEdge(e);
          reversed.push(e);
          g.addEdge(e, v, u, edge);
        } else {
          dfs(v);
        }
      });

      delete onStack[u];
    }

    g.nodes().forEach(function(u) {
      dfs(u);
    });

    return reversed;
  }

  function undoAcyclic(reversed) {
    reversed.forEach(function(e) {
      g.edge(e).points.reverse();
    });
  }

  // Assumes input graph has no self-loops and is otherwise acyclic.
  function addDummyNodes() {
    g.edges().forEach(function(e) {
      var source = g.source(e);
      var target = g.target(e);
      var sourceRank = g.node(source).rank;
      var targetRank = g.node(target).rank;
      if (sourceRank + 1 < targetRank) {
        var prefix = "_D-" + e + "-";
        for (var u = source, rank = sourceRank + 1, i = 0; rank < targetRank; ++rank, ++i) {
          var v = prefix + rank;
          var node = { width: 0,
                       height: 0,
                       edgeId: e,
                       edge: g.edge(e),
                       source: g.source(e),
                       target: g.target(e),
                       index: i,
                       rank: rank,
                       dummy: true };
          g.addNode(v, node);
          g.addEdge(u + " -> " + v, u, v, {});
          u = v;
        }
        g.addEdge(u + " -> " + target, u, target, {});
        g.delEdge(e);
      }
    });
  }

  function collapseDummyNodes() {
    var visited = {};

    g.nodes().forEach(function(u) {
      var node = g.node(u);
      if (node.dummy) {
        if (!g.hasEdge(node.edgeId)) {
          g.addEdge(node.edgeId, node.source, node.target, node.edge);
        }
        var points = g.edge(node.edgeId).points;
        points[node.index] = { x: node.x, y: node.y };
        g.delNode(u);
      }
    });
  }
}
<commit_msg>Make graph local to each invocation of layout.run<commit_after>dagre.layout = function() {
  // External configuration
  var
      // Nodes to lay out. At minimum must have `width` and `height` attributes.
      nodes = [],
      // Edges to lay out. At mimimum must have `source` and `target` attributes.
      edges = [],
      // Min separation between adjacent nodes in the same rank.
      nodeSep = 50,
      // Min separation between adjacent edges in the same rank.
      edgeSep = 10,
      // Min separation between ranks.
      rankSep = 30,
      // Number of passes to take during the ordering phase.
      orderIters = 24,
      // Debug positioning with a particular direction (up-left, up-right, down-left, down-right).
      posDir = null,
      // How much debug information to include?
      debugLevel = 0;

  // Phase functions
  var
      rank = dagre.layout.rank(),
      order = dagre.layout.order(),
      position = dagre.layout.position();

  // This layout object
  var self = {};

  self.nodes = function(x) {
    if (!arguments.length) return nodes;
    nodes = x;
    return self;
  };

  self.edges = function(x) {
    if (!arguments.length) return edges;
    edges = x;
    return self;
  };

  self.orderIters = function(x) {
    if (!arguments.length) return order.iterations();
    order.iterations(x);
    return self;
  }

  self.nodeSep = function(x) {
    if (!arguments.length) return position.nodeSep();
    position.nodeSep(x);
    return self;
  };

  self.edgeSep = function(x) {
    if (!arguments.length) return position.edgeSep();
    position.edgeSep(x);
    return self;
  }

  self.rankSep = function(x) {
    if (!arguments.length) return position.rankSep();
    position.rankSep(x);
    return self;
  }

  self.posDir = function(x) {
    if (!arguments.length) return position.direction();
    position.direction(x);
    return self;
  }

  self.debugLevel = function(x) {
    if (!arguments.length) return debugLevel;
    debugLevel = x;
    rank.debugLevel(x);
    order.debugLevel(x);
    position.debugLevel(x);
    return self;
  }

  self.run = function() {
    var timer = createTimer();

    // Build internal graph
    var g = init();

    if (g.nodes().length === 0) {
      // Nothing to do!
      return;
    }

    var reversed = acyclic(g);

    rank.run(g);
    addDummyNodes(g);
    order.run(g);
    position.run(g);
    collapseDummyNodes(g);

    undoAcyclic(g, reversed);

    if (debugLevel >= 1) {
      console.log("Total layout time: " + timer.elapsedString());
    }
  };

  return self;

  // Build graph and save mapping of generated ids to original nodes and edges
  function init() {
    var g = dagre.graph();

    var nextId = 0;

    // Tag each node so that we can properly represent relationships when
    // we add edges. Also copy relevant dimension information.
    nodes.forEach(function(u) {
      var id = "id" in u ? u.id : "_N" + nextId++;
      u.dagre = { id: id, width: u.width, height: u.height };
      g.addNode(id, u.dagre);
    });

    edges.forEach(function(e) {
      var source = e.source.dagre.id;
      if (!g.hasNode(source)) {
        throw new Error("Source node for '" + e + "' not in node list");
      }

      var target = e.target.dagre.id;
      if (!g.hasNode(target)) {
        throw new Error("Target node for '" + e + "' not in node list");
      }

      e.dagre = {
        points: []
      };

      // Track edges that aren't self loops - layout does nothing for self
      // loops, so they can be skipped.
      if (source !== target) {
        var id = "id" in e ? e.id : "_E" + nextId++;
        // TODO should we use prototypal inheritance for this?
        if (e.minLen) {
          e.dagre.minLen = e.minLen;
        }
        g.addEdge(id, source, target, e.dagre);
      }
    });

    return g;
  }

  function acyclic(g) {
    var onStack = {};
    var visited = {};
    var reversed = [];

    function dfs(u) {
      if (u in visited)
        return;

      visited[u] = true;
      onStack[u] = true;
      g.outEdges(u).forEach(function(e) {
        var v = g.target(e);
        if (v in onStack) {
          var edge = g.edge(e);
          g.delEdge(e);
          reversed.push(e);
          g.addEdge(e, v, u, edge);
        } else {
          dfs(v);
        }
      });

      delete onStack[u];
    }

    g.nodes().forEach(function(u) {
      dfs(u);
    });

    return reversed;
  }

  function undoAcyclic(g, reversed) {
    reversed.forEach(function(e) {
      g.edge(e).points.reverse();
    });
  }

  // Assumes input graph has no self-loops and is otherwise acyclic.
  function addDummyNodes(g) {
    g.edges().forEach(function(e) {
      var source = g.source(e);
      var target = g.target(e);
      var sourceRank = g.node(source).rank;
      var targetRank = g.node(target).rank;
      if (sourceRank + 1 < targetRank) {
        var prefix = "_D-" + e + "-";
        for (var u = source, rank = sourceRank + 1, i = 0; rank < targetRank; ++rank, ++i) {
          var v = prefix + rank;
          var node = { width: 0,
                       height: 0,
                       edgeId: e,
                       edge: g.edge(e),
                       source: g.source(e),
                       target: g.target(e),
                       index: i,
                       rank: rank,
                       dummy: true };
          g.addNode(v, node);
          g.addEdge(u + " -> " + v, u, v, {});
          u = v;
        }
        g.addEdge(u + " -> " + target, u, target, {});
        g.delEdge(e);
      }
    });
  }

  function collapseDummyNodes(g) {
    var visited = {};

    g.nodes().forEach(function(u) {
      var node = g.node(u);
      if (node.dummy) {
        if (!g.hasEdge(node.edgeId)) {
          g.addEdge(node.edgeId, node.source, node.target, node.edge);
        }
        var points = g.edge(node.edgeId).points;
        points[node.index] = { x: node.x, y: node.y };
        g.delNode(u);
      }
    });
  }
}
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

const identifierUtils = require("../util/identifier");

function toIndexOf(list) {
	return function(item) {
		return list.indexOf(item);
	};
}

function toChunkModuleIndices(modules) {
	return function(idx) {
		return modules[idx];
	};
}

function moveModuleBetween(oldChunk, newChunk) {
	return function(module) {
		oldChunk.moveModule(module, newChunk);
	};
}

function isNotAEntryModule(entryModule) {
	return function(module) {
		return entryModule !== module;
	};
}

function copyWithReason(obj) {
	const newObj = {};
	Object.keys(obj).forEach((key) => {
		newObj[key] = obj[key];
	});
	if(!newObj.reasons || newObj.reasons.indexOf("aggressive-splitted") < 0)
		newObj.reasons = (newObj.reasons || []).concat("aggressive-splitted");
	return newObj;
}

class AggressiveSplittingPlugin {
	constructor(options) {
		this.options = options || {};
		if(typeof this.options.minSize !== "number") this.options.minSize = 30 * 1024;
		if(typeof this.options.maxSize !== "number") this.options.maxSize = 50 * 1024;
		if(typeof this.options.chunkOverhead !== "number") this.options.chunkOverhead = 0;
		if(typeof this.options.entryChunkMultiplicator !== "number") this.options.entryChunkMultiplicator = 1;
	}
	apply(compiler) {
		compiler.plugin("compilation", (compilation) => {
			compilation.plugin("optimize-chunks-advanced", (chunks) => {
				const savedSplits = compilation.records && compilation.records.aggressiveSplits || [];
				const usedSplits = compilation._aggressiveSplittingSplits ?
					savedSplits.concat(compilation._aggressiveSplittingSplits) : savedSplits;

				const minSize = this.options.minSize;
				const maxSize = this.options.maxSize;
				// 1. try to restore to recorded splitting
				for(let j = 0; j < usedSplits.length; j++) {
					const splitData = usedSplits[j];
					for(let i = 0; i < chunks.length; i++) {
						const chunk = chunks[i];
						const chunkModuleNames = chunk.modules.map(m => identifierUtils.makePathsRelative(compiler.context, m.identifier()));

						if(chunkModuleNames.length < splitData.modules.length)
							continue;
						const moduleIndicies = splitData.modules.map(toIndexOf(chunkModuleNames));
						const hasAllModules = moduleIndicies.every((idx) => {
							return idx >= 0;
						});
						if(hasAllModules) {
							if(chunkModuleNames.length > splitData.modules.length) {
								const selectedModules = moduleIndicies.map(toChunkModuleIndices(chunk.modules));
								const newChunk = compilation.addChunk();
								selectedModules.forEach(moveModuleBetween(chunk, newChunk));
								chunk.split(newChunk);
								chunk.name = null;
								newChunk._fromAggressiveSplitting = true;
								if(j < savedSplits.length)
									newChunk._fromAggressiveSplittingIndex = j;
								if(splitData.id !== null && splitData.id !== undefined) {
									newChunk.id = splitData.id;
								}
								newChunk.origins = chunk.origins.map(copyWithReason);
								chunk.origins = chunk.origins.map(copyWithReason);
								return true;
							} else {
								if(j < savedSplits.length)
									chunk._fromAggressiveSplittingIndex = j;
								chunk.name = null;
								if(splitData.id !== null && splitData.id !== undefined) {
									chunk.id = splitData.id;
								}
							}
						}
					}
				}
				// 2. for any other chunk which isn't splitted yet, split it
				for(let i = 0; i < chunks.length; i++) {
					const chunk = chunks[i];
					const size = chunk.size(this.options);
					if(size > maxSize && chunk.modules.length > 1) {
						const newChunk = compilation.addChunk();
						const modules = chunk.modules
							.filter(isNotAEntryModule(chunk.entryModule))
							.sort((a, b) => {
								a = a.identifier();
								b = b.identifier();
								if(a > b) return 1;
								if(a < b) return -1;
								return 0;
							});
						for(let k = 0; k < modules.length; k++) {
							chunk.moveModule(modules[k], newChunk);
							const newSize = newChunk.size(this.options);
							const chunkSize = chunk.size(this.options);
							// break early if it's fine
							if(chunkSize < maxSize && newSize < maxSize && newSize >= minSize && chunkSize >= minSize)
								break;
							if(newSize > maxSize && k === 0) {
								// break if there is a single module which is bigger than maxSize
								break;
							}
							if(newSize > maxSize || chunkSize < minSize) {
								// move it back
								newChunk.moveModule(modules[k], chunk);
								// check if it's fine now
								if(newSize < maxSize && newSize >= minSize && chunkSize >= minSize)
									break;
							}
						}
						if(newChunk.modules.length > 0) {
							chunk.split(newChunk);
							chunk.name = null;
							newChunk.origins = chunk.origins.map(copyWithReason);
							chunk.origins = chunk.origins.map(copyWithReason);
							compilation._aggressiveSplittingSplits = (compilation._aggressiveSplittingSplits || []).concat({
								modules: newChunk.modules.map(m => identifierUtils.makePathsRelative(compiler.context, m.identifier()))
							});
							return true;
						} else {
							chunks.splice(chunks.indexOf(newChunk), 1);
						}
					}
				}
			});
			compilation.plugin("record-hash", (records) => {
				// 3. save to made splittings to records
				const minSize = this.options.minSize;
				if(!records.aggressiveSplits) records.aggressiveSplits = [];
				compilation.chunks.forEach((chunk) => {
					if(chunk.hasEntryModule()) return;
					const size = chunk.size(this.options);
					const incorrectSize = size < minSize;
					const modules = chunk.modules.map(m => identifierUtils.makePathsRelative(compiler.context, m.identifier()));
					if(typeof chunk._fromAggressiveSplittingIndex === "undefined") {
						if(incorrectSize) return;
						chunk.recorded = true;
						records.aggressiveSplits.push({
							modules: modules,
							hash: chunk.hash,
							id: chunk.id
						});
					} else {
						const splitData = records.aggressiveSplits[chunk._fromAggressiveSplittingIndex];
						if(splitData.hash !== chunk.hash || incorrectSize) {
							if(chunk._fromAggressiveSplitting) {
								chunk._aggressiveSplittingInvalid = true;
								splitData.invalid = true;
							} else {
								splitData.hash = chunk.hash;
							}
						}
					}
				});
				records.aggressiveSplits = records.aggressiveSplits.filter((splitData) => {
					return !splitData.invalid;
				});
			});
			compilation.plugin("need-additional-seal", (callback) => {
				const invalid = compilation.chunks.some((chunk) => {
					return chunk._aggressiveSplittingInvalid;
				});
				if(invalid)
					return true;
			});
		});
	}
}
module.exports = AggressiveSplittingPlugin;
<commit_msg>Upstream changes from https://github.com/jantimon/html-webpack-plugin/pull/670#issuecomment-301484908<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

const identifierUtils = require("../util/identifier");

function toIndexOf(list) {
	return function(item) {
		return list.indexOf(item);
	};
}

function toChunkModuleIndices(modules) {
	return function(idx) {
		return modules[idx];
	};
}

function moveModuleBetween(oldChunk, newChunk) {
	return function(module) {
		oldChunk.moveModule(module, newChunk);
	};
}

function isNotAEntryModule(entryModule) {
	return function(module) {
		return entryModule !== module;
	};
}

function copyWithReason(obj) {
	const newObj = {};
	Object.keys(obj).forEach((key) => {
		newObj[key] = obj[key];
	});
	if(!newObj.reasons || newObj.reasons.indexOf("aggressive-splitted") < 0)
		newObj.reasons = (newObj.reasons || []).concat("aggressive-splitted");
	return newObj;
}

class AggressiveSplittingPlugin {
	constructor(options) {
		this.options = options || {};
		if(typeof this.options.minSize !== "number") this.options.minSize = 30 * 1024;
		if(typeof this.options.maxSize !== "number") this.options.maxSize = 50 * 1024;
		if(typeof this.options.chunkOverhead !== "number") this.options.chunkOverhead = 0;
		if(typeof this.options.entryChunkMultiplicator !== "number") this.options.entryChunkMultiplicator = 1;
	}
	apply(compiler) {
		compiler.plugin("this-compilation", (compilation) => {
			compilation.plugin("optimize-chunks-advanced", (chunks) => {
				const savedSplits = compilation.records && compilation.records.aggressiveSplits || [];
				const usedSplits = compilation._aggressiveSplittingSplits ?
					savedSplits.concat(compilation._aggressiveSplittingSplits) : savedSplits;

				const minSize = this.options.minSize;
				const maxSize = this.options.maxSize;
				// 1. try to restore to recorded splitting
				for(let j = 0; j < usedSplits.length; j++) {
					const splitData = usedSplits[j];
					for(let i = 0; i < chunks.length; i++) {
						const chunk = chunks[i];
						const chunkModuleNames = chunk.modules.map(m => identifierUtils.makePathsRelative(compiler.context, m.identifier()));

						if(chunkModuleNames.length < splitData.modules.length)
							continue;
						const moduleIndicies = splitData.modules.map(toIndexOf(chunkModuleNames));
						const hasAllModules = moduleIndicies.every((idx) => {
							return idx >= 0;
						});
						if(hasAllModules) {
							if(chunkModuleNames.length > splitData.modules.length) {
								const selectedModules = moduleIndicies.map(toChunkModuleIndices(chunk.modules));
								const newChunk = compilation.addChunk();
								selectedModules.forEach(moveModuleBetween(chunk, newChunk));
								chunk.split(newChunk);
								chunk.name = null;
								newChunk._fromAggressiveSplitting = true;
								if(j < savedSplits.length)
									newChunk._fromAggressiveSplittingIndex = j;
								if(splitData.id !== null && splitData.id !== undefined) {
									newChunk.id = splitData.id;
								}
								newChunk.origins = chunk.origins.map(copyWithReason);
								chunk.origins = chunk.origins.map(copyWithReason);
								return true;
							} else {
								if(j < savedSplits.length)
									chunk._fromAggressiveSplittingIndex = j;
								chunk.name = null;
								if(splitData.id !== null && splitData.id !== undefined) {
									chunk.id = splitData.id;
								}
							}
						}
					}
				}
				// 2. for any other chunk which isn't splitted yet, split it
				for(let i = 0; i < chunks.length; i++) {
					const chunk = chunks[i];
					const size = chunk.size(this.options);
					if(size > maxSize && chunk.modules.length > 1) {
						const newChunk = compilation.addChunk();
						const modules = chunk.modules
							.filter(isNotAEntryModule(chunk.entryModule))
							.sort((a, b) => {
								a = a.identifier();
								b = b.identifier();
								if(a > b) return 1;
								if(a < b) return -1;
								return 0;
							});
						for(let k = 0; k < modules.length; k++) {
							chunk.moveModule(modules[k], newChunk);
							const newSize = newChunk.size(this.options);
							const chunkSize = chunk.size(this.options);
							// break early if it's fine
							if(chunkSize < maxSize && newSize < maxSize && newSize >= minSize && chunkSize >= minSize)
								break;
							if(newSize > maxSize && k === 0) {
								// break if there is a single module which is bigger than maxSize
								break;
							}
							if(newSize > maxSize || chunkSize < minSize) {
								// move it back
								newChunk.moveModule(modules[k], chunk);
								// check if it's fine now
								if(newSize < maxSize && newSize >= minSize && chunkSize >= minSize)
									break;
							}
						}
						if(newChunk.modules.length > 0) {
							chunk.split(newChunk);
							chunk.name = null;
							newChunk.origins = chunk.origins.map(copyWithReason);
							chunk.origins = chunk.origins.map(copyWithReason);
							compilation._aggressiveSplittingSplits = (compilation._aggressiveSplittingSplits || []).concat({
								modules: newChunk.modules.map(m => identifierUtils.makePathsRelative(compiler.context, m.identifier()))
							});
							return true;
						} else {
							chunks.splice(chunks.indexOf(newChunk), 1);
						}
					}
				}
			});
			compilation.plugin("record-hash", (records) => {
				// 3. save to made splittings to records
				const minSize = this.options.minSize;
				if(!records.aggressiveSplits) records.aggressiveSplits = [];
				compilation.chunks.forEach((chunk) => {
					if(chunk.hasEntryModule()) return;
					const size = chunk.size(this.options);
					const incorrectSize = size < minSize;
					const modules = chunk.modules.map(m => identifierUtils.makePathsRelative(compiler.context, m.identifier()));
					if(typeof chunk._fromAggressiveSplittingIndex === "undefined") {
						if(incorrectSize) return;
						chunk.recorded = true;
						records.aggressiveSplits.push({
							modules: modules,
							hash: chunk.hash,
							id: chunk.id
						});
					} else {
						const splitData = records.aggressiveSplits[chunk._fromAggressiveSplittingIndex];
						if(splitData.hash !== chunk.hash || incorrectSize) {
							if(chunk._fromAggressiveSplitting) {
								chunk._aggressiveSplittingInvalid = true;
								splitData.invalid = true;
							} else {
								splitData.hash = chunk.hash;
							}
						}
					}
				});
				records.aggressiveSplits = records.aggressiveSplits.filter((splitData) => {
					return !splitData.invalid;
				});
			});
			compilation.plugin("need-additional-seal", (callback) => {
				const invalid = compilation.chunks.some((chunk) => {
					return chunk._aggressiveSplittingInvalid;
				});
				if(invalid)
					return true;
			});
		});
	}
}
module.exports = AggressiveSplittingPlugin;
<|endoftext|>


<commit_before>const request = require('request-promise')
const { listen, mockDb } = require('./utils')

jest.mock('../src/db', () => mockDb)
const service = require('../src')
let url

beforeEach(async () => {
  mockDb._reset()
  url = await listen(service)
})

describe('single', () => {
  it('should set the views of a non-existant path to one', async () => {
    const body = JSON.parse(await request(`${url}/nonexistant`))
    expect(body.views).toEqual(1)
  })

  it('should increment the views of an existant path', async () => {
    await request(`${url}/existant`)
    const body = JSON.parse(await request(`${url}/existant`))
    expect(body.views).toEqual(2)
  })

  it('should return 0 views on a non-existant path if inc is set to false', async () => {
    const body = JSON.parse(await request(`${url}/path?inc=false`))
    expect(body.views).toEqual(0)
  })

  it('should not increment the views of an existant path if inc is set to false', async () => {
    await request(`${url}/existant`)
    const body = JSON.parse(await request(`${url}/existant?inc=false`))
    expect(body.views).toEqual(1)
  })

  it('should not return anything for a POST request', async () => {
    const body = await request.post(`${url}/existant`)
    expect(body).toEqual('')
  })
})

describe('all', () => {
  it('should return an empty array if no previous views exist', async () => {
    const body = JSON.parse(await request(`${url}/?all=true`))
    expect(body.data).toEqual({})
    expect(body.time).toBeDefined()
  })

  it('should return previous views of one route', async () => {
    await request(`${url}/route`)
    await request(`${url}/route`)
    const body = JSON.parse(await request(`${url}/?all=true`))
    expect(Object.keys(body.data).length).toBe(1)
    expect(body.data['/route'].views).toBeDefined()
    expect(body.data['/route'].views.length).toBe(2)
  })

  it('should return previous views of all routes', async () => {
    await request(`${url}/route`)
    await request(`${url}/route`)
    await request(`${url}/route2`)
    await request(`${url}/route2`)
    await request(`${url}/route2`)
    const body = JSON.parse(await request(`${url}/?all=true`))
    expect(Object.keys(body.data).length).toBe(2)
    expect(body.data['/route'].views).toBeDefined()
    expect(body.data['/route'].views.length).toBe(2)
    expect(body.data['/route2'].views).toBeDefined()
    expect(body.data['/route2'].views.length).toBe(3)
  })

  describe('filtering', () => {
    it('should filter based on pathname', async () => {
      await request(`${url}/rover`)
      await request(`${url}/route`)
      const body = JSON.parse(await request(`${url}/rover?all=true`))
      expect(Object.keys(body.data).length).toBe(1)
      expect(body.data['/rover'].views).toBeDefined()
      expect(body.data['/rover'].views.length).toBe(1)
    })

    it('should filter based on starting with pathname', async () => {
      await request(`${url}/rover`)
      await request(`${url}/rover2`)
      await request(`${url}/route`)
      const body = JSON.parse(await request(`${url}/rover?all=true`))
      expect(Object.keys(body.data).length).toBe(2)
      expect(body.data['/rover'].views).toBeDefined()
      expect(body.data['/rover'].views.length).toBe(1)
      expect(body.data['/rover2'].views).toBeDefined()
      expect(body.data['/rover2'].views.length).toBe(1)
    })
  })
})
<commit_msg>Test filter disabling when getting all views<commit_after>const request = require('request-promise')
const { listen, mockDb } = require('./utils')

jest.mock('../src/db', () => mockDb)
const service = require('../src')
let url

beforeEach(async () => {
  mockDb._reset()
  url = await listen(service)
})

describe('single', () => {
  it('should set the views of a non-existant path to one', async () => {
    const body = JSON.parse(await request(`${url}/nonexistant`))
    expect(body.views).toEqual(1)
  })

  it('should increment the views of an existant path', async () => {
    await request(`${url}/existant`)
    const body = JSON.parse(await request(`${url}/existant`))
    expect(body.views).toEqual(2)
  })

  it('should return 0 views on a non-existant path if inc is set to false', async () => {
    const body = JSON.parse(await request(`${url}/path?inc=false`))
    expect(body.views).toEqual(0)
  })

  it('should not increment the views of an existant path if inc is set to false', async () => {
    await request(`${url}/existant`)
    const body = JSON.parse(await request(`${url}/existant?inc=false`))
    expect(body.views).toEqual(1)
  })

  it('should not return anything for a POST request', async () => {
    const body = await request.post(`${url}/existant`)
    expect(body).toEqual('')
  })
})

describe('all', () => {
  it('should return an empty array if no previous views exist', async () => {
    const body = JSON.parse(await request(`${url}/?all=true`))
    expect(body.data).toEqual({})
    expect(body.time).toBeDefined()
  })

  it('should return previous views of one route', async () => {
    await request(`${url}/route`)
    await request(`${url}/route`)
    const body = JSON.parse(await request(`${url}/?all=true`))
    expect(Object.keys(body.data).length).toBe(1)
    expect(body.data['/route'].views).toBeDefined()
    expect(body.data['/route'].views.length).toBe(2)
  })

  it('should return previous views of all routes', async () => {
    await request(`${url}/route`)
    await request(`${url}/route`)
    await request(`${url}/route2`)
    await request(`${url}/route2`)
    await request(`${url}/route2`)
    const body = JSON.parse(await request(`${url}/?all=true`))
    expect(Object.keys(body.data).length).toBe(2)
    expect(body.data['/route'].views).toBeDefined()
    expect(body.data['/route'].views.length).toBe(2)
    expect(body.data['/route2'].views).toBeDefined()
    expect(body.data['/route2'].views.length).toBe(3)
  })

  describe('filtering', () => {
    it('should filter based on pathname', async () => {
      await request(`${url}/rover`)
      await request(`${url}/route`)
      const body = JSON.parse(await request(`${url}/rover?all=true`))
      expect(Object.keys(body.data).length).toBe(1)
      expect(body.data['/rover'].views).toBeDefined()
      expect(body.data['/rover'].views.length).toBe(1)
    })

    it('should filter based on starting with pathname', async () => {
      await request(`${url}/rover`)
      await request(`${url}/rover2`)
      await request(`${url}/route`)
      const body = JSON.parse(await request(`${url}/rover?all=true`))
      expect(Object.keys(body.data).length).toBe(2)
      expect(body.data['/rover'].views).toBeDefined()
      expect(body.data['/rover'].views.length).toBe(1)
      expect(body.data['/rover2'].views).toBeDefined()
      expect(body.data['/rover2'].views.length).toBe(1)
    })

    it('should not filter if filter is set to false', async () => {
      await request(`${url}/rover`)
      await request(`${url}/rover2`)
      await request(`${url}/route`)
      const body = JSON.parse(await request(`${url}/rover?all=true&filter=false`))
      expect(Object.keys(body.data).length).toBe(3)
    })
  })
})
<|endoftext|>


<commit_before>// generated on 2016-04-18 using generator-webapp 2.0.0
import gulp from 'gulp';
import gulpLoadPlugins from 'gulp-load-plugins';
import browserSync from 'browser-sync';
import del from 'del';
import {stream as wiredep} from 'wiredep';

const $ = gulpLoadPlugins();
const reload = browserSync.reload;

gulp.task('styles', () => {
  return gulp.src('app/styles/*.scss')
    .pipe($.plumber())
    .pipe($.sourcemaps.init())
    .pipe($.sass.sync({
      outputStyle: 'expanded',
      precision: 10,
      includePaths: ['.']
    }).on('error', $.sass.logError))
    .pipe($.autoprefixer({browsers: ['> 1%', 'last 2 versions', 'Firefox ESR']}))
    .pipe($.sourcemaps.write())
    .pipe(gulp.dest('.tmp/styles'))
    .pipe(reload({stream: true}));
});

gulp.task('scripts', () => {
  return gulp.src('app/scripts/**/*.js')
    .pipe($.plumber())
    .pipe($.sourcemaps.init())
    .pipe($.babel())
    .pipe($.sourcemaps.write('.'))
    .pipe(gulp.dest('.tmp/scripts'))
    .pipe(reload({stream: true}));
});

function lint(files, options) {
  return () => {
    return gulp.src(files)
      .pipe(reload({stream: true, once: true}))
      .pipe($.eslint(options))
      .pipe($.eslint.format())
      .pipe($.if(!browserSync.active, $.eslint.failAfterError()));
  };
}
const testLintOptions = {
  env: {
    mocha: true
  }
};

gulp.task('lint', lint('app/scripts/**/*.js'));
gulp.task('lint:test', lint('test/spec/**/*.js', testLintOptions));

gulp.task('html', ['styles', 'scripts'], () => {
  return gulp.src('app/*.html')
    .pipe($.useref({searchPath: ['.tmp', 'app', '.']}))
    .pipe($.if('*.js', $.uglify()))
    .pipe($.if('*.css', $.cssnano()))
    .pipe($.if('*.html', $.htmlmin({collapseWhitespace: true})))
    .pipe(gulp.dest('dist'));
});

gulp.task('images', () => {
  return gulp.src('app/images/**/*')
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true,
      // don't remove IDs from SVGs, they are often used
      // as hooks for embedding and styling
      svgoPlugins: [{cleanupIDs: false}]
    })))
    .pipe(gulp.dest('dist/images'));
});

gulp.task('fonts', () => {
  return gulp.src(require('main-bower-files')('**/*.{eot,svg,ttf,woff,woff2}', function (err) {})
    .concat('app/fonts/**/*'))
    .pipe(gulp.dest('.tmp/fonts'))
    .pipe(gulp.dest('dist/fonts'));
});

gulp.task('extras', () => {
  return gulp.src([
    'app/*.*',
    '!app/*.html'
  ], {
    dot: true
  }).pipe(gulp.dest('dist'));
});

gulp.task('clean', del.bind(null, ['.tmp', 'dist']));

gulp.task('serve', ['styles', 'scripts', 'fonts'], () => {
  browserSync({
    notify: false,
    port: 9000,
    server: {
      baseDir: ['.tmp', 'app'],
      routes: {
        '/bower_components': 'bower_components'
      }
    }
  });

  gulp.watch([
    'app/*.html',
    'app/images/**/*',
    '.tmp/fonts/**/*'
  ]).on('change', reload);

  gulp.watch('app/styles/**/*.scss', ['styles']);
  gulp.watch('app/scripts/**/*.js', ['scripts']);
  gulp.watch('app/fonts/**/*', ['fonts']);
  gulp.watch('bower.json', ['wiredep', 'fonts']);
});

gulp.task('serve:dist', () => {
  browserSync({
    notify: false,
    port: 9000,
    server: {
      baseDir: ['dist']
    }
  });
});

gulp.task('serve:test', ['scripts'], () => {
  browserSync({
    notify: false,
    port: 9000,
    ui: false,
    server: {
      baseDir: 'test',
      routes: {
        '/scripts': '.tmp/scripts',
        '/bower_components': 'bower_components'
      }
    }
  });

  gulp.watch('app/scripts/**/*.js', ['scripts']);
  gulp.watch('test/spec/**/*.js').on('change', reload);
  gulp.watch('test/spec/**/*.js', ['lint:test']);
});

// inject bower components
gulp.task('wiredep', () => {
  gulp.src('app/styles/*.scss')
    .pipe(wiredep({
      ignorePath: /^(\.\.\/)+/
    }))
    .pipe(gulp.dest('app/styles'));

  gulp.src('app/*.html')
    .pipe(wiredep({
      exclude: ['bootstrap-sass'],
      ignorePath: /^(\.\.\/)*\.\./
    }))
    .pipe(gulp.dest('app'));
});

gulp.task('build', ['lint', 'html', 'images', 'fonts', 'extras'], () => {
  return gulp.src('dist/**/*').pipe($.size({title: 'build', gzip: true}));
});

gulp.task('default', ['clean'], () => {
  gulp.start('build');
});
<commit_msg>Updated gulpfile<commit_after>// generated on 2016-04-18 using generator-webapp 2.0.0
import gulp from 'gulp';
import gulpLoadPlugins from 'gulp-load-plugins';
import browserSync from 'browser-sync';
import del from 'del';
import {stream as wiredep} from 'wiredep';

const $ = gulpLoadPlugins();
const reload = browserSync.reload;

gulp.task('styles', () => {
  return gulp.src('app/styles/*.scss')
    .pipe($.plumber())
    .pipe($.sourcemaps.init())
    .pipe($.sass.sync({
      outputStyle: 'expanded',
      precision: 10,
      includePaths: ['.']
    }).on('error', $.sass.logError))
    .pipe($.autoprefixer({browsers: ['> 1%', 'last 2 versions', 'Firefox ESR']}))
    .pipe($.sourcemaps.write())
    .pipe(gulp.dest('.tmp/styles'))
    .pipe(reload({stream: true}));
});

gulp.task('scripts', () => {
  return gulp.src('app/scripts/**/*.js')
    .pipe($.plumber())
    .pipe($.sourcemaps.init())
    .pipe($.babel())
    .pipe($.sourcemaps.write('.'))
    .pipe(gulp.dest('.tmp/scripts'))
    .pipe(reload({stream: true}));
});

function lint(files, options) {
  return () => {
    return gulp.src(files)
      .pipe(reload({stream: true, once: true}))
      .pipe($.eslint(options))
      .pipe($.eslint.format())
      .pipe($.if(!browserSync.active, $.eslint.failAfterError()));
  };
}
const testLintOptions = {
  env: {
    mocha: true
  }
};

gulp.task('lint', lint('app/scripts/**/*.js'));
gulp.task('lint:test', lint('test/spec/**/*.js', testLintOptions));

gulp.task('html', ['styles', 'scripts'], () => {
  return gulp.src('app/*.html')
    .pipe($.useref({searchPath: ['.tmp', 'app', '.']}))
    .pipe($.if('*.js', $.uglify()))
    .pipe($.if('*.css', $.cssnano()))
    .pipe($.if('*.html', $.htmlmin({collapseWhitespace: true})))
    .pipe(gulp.dest('dist'));
});

gulp.task('images', () => {
  return gulp.src('app/images/**/*')
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true,
      // don't remove IDs from SVGs, they are often used
      // as hooks for embedding and styling
      svgoPlugins: [{cleanupIDs: false}]
    })))
    .pipe(gulp.dest('dist/images'));
});

gulp.task('fonts', () => {
  return gulp.src(require('main-bower-files')('**/*.{eot,svg,ttf,woff,woff2}', function (err) {})
    .concat('app/fonts/**/*'))
    .pipe(gulp.dest('.tmp/fonts'))
    .pipe(gulp.dest('dist/fonts'));
});

gulp.task('extras', () => {
  return gulp.src([
    'app/*.*',
    '!app/*.html'
  ], {
    dot: true
  }).pipe(gulp.dest('dist'));
});

gulp.task('clean', del.bind(null, ['.tmp', 'dist']));

gulp.task('serve', ['styles', 'scripts', 'fonts'], () => {
  browserSync({
    notify: false,
    port: 9000,
    server: {
      baseDir: ['.tmp', 'app'],
      routes: {
        '/bower_components': 'bower_components'
      }
    }
  });

  gulp.watch([
    'app/*.html',
    'app/images/**/*',
    '.tmp/fonts/**/*'
  ]).on('change', reload);

  gulp.watch('app/styles/**/*.scss', ['styles']);
  gulp.watch('app/scripts/**/*.js', ['scripts']);
  gulp.watch('app/fonts/**/*', ['fonts']);
  gulp.watch('bower.json', ['wiredep', 'fonts']);
});

gulp.task('serve:dist', () => {
  browserSync({
    notify: false,
    port: 9000,
    server: {
      baseDir: ['dist']
    }
  });
});

gulp.task('serve:test', ['scripts'], () => {
  browserSync({
    notify: false,
    port: 9000,
    ui: false,
    server: {
      baseDir: 'test',
      routes: {
        '/scripts': '.tmp/scripts',
        '/bower_components': 'bower_components'
      }
    }
  });

  gulp.watch('app/scripts/**/*.js', ['scripts']);
  gulp.watch('test/spec/**/*.js').on('change', reload);
  gulp.watch('test/spec/**/*.js', ['lint:test']);
});

// inject bower components
gulp.task('wiredep', () => {
  gulp.src('app/styles/*.scss')
    .pipe(wiredep({
      ignorePath: /^(\.\.\/)+/
    }))
    .pipe(gulp.dest('app/styles'));

  gulp.src('app/*.html')
    .pipe(wiredep({
      exclude: ['bootstrap-sass'],
      ignorePath: /^(\.\.\/)*\.\./
    }))
    .pipe(gulp.dest('app'));
});

gulp.task('build', ['html', 'images', 'fonts', 'extras'], () => {
  return gulp.src('dist/**/*').pipe($.size({title: 'build', gzip: true}));
});

gulp.task('default', ['clean'], () => {
  gulp.start('build');
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2016 Google Inc. 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.
 */

/** A method that is configured to call a service over HTTP. No code or
  function body is required here, as the actual body is generated to call
  the remote service. This will always return a promise that supplies the
  return value of the service call.

Overriding by an XHRMethod is not supported. You can override an
  XHRMethod with a normal one.

FUTURE: Generalize. */
foam.CLASS({
  package: 'foam.api',
  name: 'XHRMethod',
  extends: 'foam.core.Method',
  requires: [
    'foam.api.XHRArgument',
    'foam.core.Imports',
  ],
  imports: [
    'assert',
  ],

  properties: [
    {
      /** The path prefix. Parameters may add to the path */
      name: 'path'
    },
    {
      name: 'httpMethod',
      value: 'GET',
    },
    {
      /** The parameters to call with, in order */
      class: 'FObjectArray',
      name: 'parameters',
      of: 'foam.api.XHRArgument',
      factory: function() { return []; }
    },
    {
      /** XHRMethods will always return a Promise, but the Promise will pass
        along a parameter of the type specified here. */
      name: 'returns',
    },
    {
      /** the name of the XHR service to import at run time */
      name: 'xhrServiceName',
      value: 'XHRService'
    }
  ],

  methods: [
    function installInClass(c) {
      // add an import for the XHRService on our host class

      // May have many XHRMethods in a host class, but only do service import once.
      var existing = c.getAxiomByName(this.xhrServiceName);
      this.assert( ( ! existing ) || this.Imports.isInstance(existing),
        "XHRMethod installInClass found conflicting axiom", existing && existing.name, ".",
        "Use a different XMRMethod.xhrServiceName.");

      if ( ! existing ) {
        c.installAxiom(this.Imports.create({
          name: this.xhrServiceName,
          key: this.xhrServiceName
        }));
      }
    },

    function installInProto(p) {
      // generate body to call through to xhr
      var axiom = this;

      // TODO: just import the thing that does the xhr, preconfigured

      // set up function with correct args, pass them into the
      // actual implementation, callRemote_()
      // ALTERNATE: just pass args in and read from arguments
      var code = "(function "+axiom.name+"_"+"(";
      var names = this.parameters.map(axiom.XHRArgument.NAME.f);
      code += names.join(', ');
      code += ') {\n';
      code += 'var opt_args = {\n';
      names.forEach(function(name) {
        code += '  '+name+': '+name+',\n';
      });
      code += '};\n';
      code += 'return axiom.callRemote_(opt_args, this);\n';
      code += '})';

      code = eval(code);

      p[axiom.name] = code;
    },

    function callRemote_(/* object */ opt_args, host /* Promise */) {
      this.assert( host[this.xhrServiceName], "XHRMethod call can't find XHR service import ", this.xhrServiceName, "on", host);

      // 'this' is the axiom instance
      var self = this;
      var path = this.path;
      var query = "";

      // add on parameters passed as part of the path or query
      self.parameters.forEach(function(param) {
        var val = opt_args[param.name].toString();
        // put the dot back if we removed one from the name
        var pname = param.name.replace('__dot__','.');
        if ( param.location === 'path' ) {
          // find the placeholder and replace it
          path = path.replace("{"+pname+"}", val);
        } else if ( param.location === 'query' ) {
          query += "&" + pname + "=" + val;
        }
      });
      path = path + ( query ? "?" + query.substring(1) : "" );
      var request = host[this.xhrServiceName].create();
      request.path += "/" + path;
      request.method = self.httpMethod;

      return request.send().then(function(response) {
        self.assert(response.responseType === 'json', "XHRMethod given a request not configured to return JSON", request);
        return response.payload.then(function(json) {
          if ( ! self.returns ) {
            // no return
            return;
          }
          if ( self.returns.type ) {
            // a modelled return type
            return self.returns.type.create(json, host);
          }
          // else return raw json
          return json;
        });
      });
    },

  ]

});

foam.CLASS({
  package: 'foam.api',
  name: 'XHRArgument',
  extends: 'foam.core.Argument',
  properties: [
    {
      /** The location to put this value in the request: 'query' or 'path' */
      name: 'location',
      value: 'query',
    },
    // optional
  ],
 });
<commit_msg>XHRMethod fixed undefined arguments:<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. 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.
 */

/** A method that is configured to call a service over HTTP. No code or
  function body is required here, as the actual body is generated to call
  the remote service. This will always return a promise that supplies the
  return value of the service call.

Overriding by an XHRMethod is not supported. You can override an
  XHRMethod with a normal one.

FUTURE: Generalize. */
foam.CLASS({
  package: 'foam.api',
  name: 'XHRMethod',
  extends: 'foam.core.Method',
  requires: [
    'foam.api.XHRArgument',
    'foam.core.Imports',
  ],
  imports: [
    'assert',
  ],

  properties: [
    {
      /** The path prefix. Parameters may add to the path */
      name: 'path'
    },
    {
      name: 'httpMethod',
      value: 'GET',
    },
    {
      /** The parameters to call with, in order */
      class: 'FObjectArray',
      name: 'parameters',
      of: 'foam.api.XHRArgument',
      factory: function() { return []; }
    },
    {
      /** XHRMethods will always return a Promise, but the Promise will pass
        along a parameter of the type specified here. */
      name: 'returns',
    },
    {
      /** the name of the XHR service to import at run time */
      name: 'xhrServiceName',
      value: 'XHRService'
    }
  ],

  methods: [
    function installInClass(c) {
      // add an import for the XHRService on our host class

      // May have many XHRMethods in a host class, but only do service import once.
      var existing = c.getAxiomByName(this.xhrServiceName);
      this.assert( ( ! existing ) || this.Imports.isInstance(existing),
        "XHRMethod installInClass found conflicting axiom", existing && existing.name, ".",
        "Use a different XMRMethod.xhrServiceName.");

      if ( ! existing ) {
        c.installAxiom(this.Imports.create({
          name: this.xhrServiceName,
          key: this.xhrServiceName
        }));
      }
    },

    function installInProto(p) {
      // generate body to call through to xhr
      var axiom = this;

      // TODO: just import the thing that does the xhr, preconfigured

      // set up function with correct args, pass them into the
      // actual implementation, callRemote_()
      // ALTERNATE: just pass args in and read from arguments
      var code = "(function "+axiom.name+"_"+"(";
      var names = this.parameters.map(axiom.XHRArgument.NAME.f);
      code += names.join(', ');
      code += ') {\n';
      code += 'var opt_args = {\n';
      names.forEach(function(name) {
        code += '  '+name+': '+name+',\n';
      });
      code += '};\n';
      code += 'return axiom.callRemote_(opt_args, this);\n';
      code += '})';

      code = eval(code);

      p[axiom.name] = code;
    },

    function callRemote_(/* object */ opt_args, host /* Promise */) {
      this.assert( host[this.xhrServiceName], "XHRMethod call can't find XHR service import ", this.xhrServiceName, "on", host);

      // 'this' is the axiom instance
      var self = this;
      var path = this.path;
      var query = "";

      // add on parameters passed as part of the path or query
      self.parameters.forEach(function(param) {
        if ( ! opt_args[param.name] ) return;
        var val = opt_args[param.name].toString();
        // put the dot back if we removed one from the name
        var pname = param.name.replace('__dot__','.');
        if ( param.location === 'path' ) {
          // find the placeholder and replace it
          path = path.replace("{"+pname+"}", val);
        } else if ( param.location === 'query' ) {
          query += "&" + pname + "=" + val;
        }
      });
      path = path + ( query ? "?" + query.substring(1) : "" );
      var request = host[this.xhrServiceName].create();
      request.path += "/" + path;
      request.method = self.httpMethod;

      return request.send().then(function(response) {
        self.assert(response.responseType === 'json', "XHRMethod given a request not configured to return JSON", request);
        return response.payload.then(function(json) {
          if ( ! self.returns ) {
            // no return
            return;
          }
          if ( self.returns.type ) {
            // a modelled return type
            return self.returns.type.create(json, host);
          }
          // else return raw json
          return json;
        });
      });
    },

  ]

});

foam.CLASS({
  package: 'foam.api',
  name: 'XHRArgument',
  extends: 'foam.core.Argument',
  properties: [
    {
      /** The location to put this value in the request: 'query' or 'path' */
      name: 'location',
      value: 'query',
    },
    // optional
  ],
 });
<|endoftext|>