<commit_before>import _isElement from 'lodash/isElement';
import _isString from 'lodash/isString';
import _throttle from 'lodash/throttle';

// import './sticky-header.scss';

export default class StickyHeader {
  defaultOptions = {
    header: '.sticky-header',
    wrapper: null,
    autoHide: true
  };

  scrollPosition = 0;
  headerOffsetTop = 0;
  scrollParent = window;
  header = null;
  autoHide = true;

  classNames = {
    stick: 'sticky-stick',
    show: 'sticky-show'
  };

  constructor(params) {
    const options = { ...this.defaultOptions, ...params };
    // Set scroll wrapper if present in params
    if (options.wrapper) {
      if (_isElement(options.wrapper)) {
        this.scrollParent = options.wrapper;
      } else if (_isString(options.wrapper)) {
        this.scrollParent = document.querySelector(options.wrapper);
      }
    }

    // Set header element if present in params
    if (options.header) {
      if (_isElement(options.header)) {
        this.header = options.header;
      } else if (_isString(options.header)) {
        this.header = document.querySelector(options.header);
      }
    }

    if (!this.header) {
      throw new Error('StickyHeader is missing header parameter!');
    }
    if (!this.scrollParent) {
      throw new Error('StickyHeader is missing scroll parent!');
    }

    if (!this.header.classList.contains('sticky-header')) {
      this.header.classList.add('sticky-header');
    }

    this.autoHide = options.autoHide;

    this.handleScroll = _throttle(this.handleScroll.bind(this), 10);
    this.scrollParent.addEventListener('scroll', this.handleScroll);
    this.scrollPosition = this.calculateScrollPosition();
    this.headerOffsetTop = this.header.offsetTop || 0;
    this.headerHeight = this.header.offsetHeight;

    if (!this.autoHide) {
      this.header.classList.add(this.classNames.show);
    }
  }

  handleScroll() {
    const newPosition = this.calculateScrollPosition();

    if (newPosition < this.scrollPosition) {
      // scrolling up
      if (newPosition <= this.headerOffsetTop + this.headerHeight) {
        this.unfixHeader();
      } else if (this.autoHide) {
        this.showFixedHeader();
      }
    } else {
      // scrolling down
      if (this.autoHide) {
        this.hideFixedHeader();
      }

      if (newPosition >= this.headerOffsetTop + this.headerHeight) {
        this.fixHeader();
      }
    }

    this.scrollPosition = newPosition;
  }

  calculateScrollPosition() {
    if (_isElement(this.scrollParent)) {
      return this.scrollParent.scrollTop;
    }

    const doc = document.documentElement;
    return (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
  }

  fixHeader() {
    if (!this.header.classList.contains(this.classNames.stick)) {
      this.header.style.transitionDuration = '0ms'; // 0ms transition to avoid hiding animation after first fixing

      if (_isElement(this.scrollParent)) {
        this.scrollParent.style.paddingTop = `${this.headerHeight}px`;
      } else {
        document.body.style.paddingTop = `${this.headerHeight}px`;
      }

      this.header.classList.add(this.classNames.stick);
      setTimeout(() => {
        this.header.style.transitionDuration = '';
      }, 500);
    }
  }

  unfixHeader() {
    if (this.header.classList.contains(this.classNames.stick)) {
      if (_isElement(this.scrollParent)) {
        this.scrollParent.style.paddingTop = '';
      } else {
        document.body.style.paddingTop = '';
      }

      this.header.style.transitionDuration = '0ms'; // 0ms transition to avoid hiding animation after first fixing
      this.header.classList.remove(this.classNames.stick);
      if (this.autoHide) {
        this.header.classList.remove(this.classNames.show);
      }
    }
  }

  showFixedHeader() {
    if (this.header.classList.contains(this.classNames.stick)) {
      this.header.classList.add(this.classNames.show);
    }
  }

  hideFixedHeader() {
    if (this.header.classList.contains(this.classNames.stick)) {
      this.header.classList.remove(this.classNames.show);
    }
  }

  destroy() {
    this.scrollParent.removeEventListener('scroll', this.handleScroll);
  }
}
<commit_msg>Fix header when it's top is reached, not when header is scrolled past<commit_after>import _isElement from 'lodash/isElement';
import _isString from 'lodash/isString';
import _throttle from 'lodash/throttle';

// import './sticky-header.scss';

export default class StickyHeader {
  defaultOptions = {
    header: '.sticky-header',
    wrapper: null,
    autoHide: true
  };

  scrollPosition = 0;
  headerOffsetTop = 0;
  scrollParent = window;
  header = null;
  autoHide = true;

  classNames = {
    stick: 'sticky-stick',
    show: 'sticky-show'
  };

  constructor(params) {
    const options = { ...this.defaultOptions, ...params };
    // Set scroll wrapper if present in params
    if (options.wrapper) {
      if (_isElement(options.wrapper)) {
        this.scrollParent = options.wrapper;
      } else if (_isString(options.wrapper)) {
        this.scrollParent = document.querySelector(options.wrapper);
      }
    }

    // Set header element if present in params
    if (options.header) {
      if (_isElement(options.header)) {
        this.header = options.header;
      } else if (_isString(options.header)) {
        this.header = document.querySelector(options.header);
      }
    }

    if (!this.header) {
      throw new Error('StickyHeader is missing header parameter!');
    }
    if (!this.scrollParent) {
      throw new Error('StickyHeader is missing scroll parent!');
    }

    if (!this.header.classList.contains('sticky-header')) {
      this.header.classList.add('sticky-header');
    }

    this.autoHide = options.autoHide;

    this.handleScroll = _throttle(this.handleScroll.bind(this), 10);
    this.scrollParent.addEventListener('scroll', this.handleScroll);
    this.scrollPosition = this.calculateScrollPosition();
    this.headerOffsetTop = this.header.offsetTop || 0;
    this.headerHeight = this.header.offsetHeight;

    if (!this.autoHide) {
      this.header.classList.add(this.classNames.show);
    }
  }

  handleScroll() {
    const newPosition = this.calculateScrollPosition();

    if (newPosition < this.scrollPosition) {
      // scrolling up
      if (newPosition <= this.headerOffsetTop) {
        this.unfixHeader();
      } else if (this.autoHide) {
        this.showFixedHeader();
      }
    } else {
      // scrolling down
      if (this.autoHide) {
        this.hideFixedHeader();
      }

      if (newPosition >= this.headerOffsetTop) {
        this.fixHeader();
      }
    }

    this.scrollPosition = newPosition;
  }

  calculateScrollPosition() {
    if (_isElement(this.scrollParent)) {
      return this.scrollParent.scrollTop;
    }

    const doc = document.documentElement;
    return (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
  }

  fixHeader() {
    if (!this.header.classList.contains(this.classNames.stick)) {
      this.header.style.transitionDuration = '0ms'; // 0ms transition to avoid hiding animation after first fixing

      if (_isElement(this.scrollParent)) {
        this.scrollParent.style.paddingTop = `${this.headerHeight}px`;
      } else {
        document.body.style.paddingTop = `${this.headerHeight}px`;
      }

      this.header.classList.add(this.classNames.stick);
      setTimeout(() => {
        this.header.style.transitionDuration = '';
      }, 500);
    }
  }

  unfixHeader() {
    if (this.header.classList.contains(this.classNames.stick)) {
      if (_isElement(this.scrollParent)) {
        this.scrollParent.style.paddingTop = '';
      } else {
        document.body.style.paddingTop = '';
      }

      this.header.style.transitionDuration = '0ms'; // 0ms transition to avoid hiding animation after first fixing
      this.header.classList.remove(this.classNames.stick);
      if (this.autoHide) {
        this.header.classList.remove(this.classNames.show);
      }
    }
  }

  showFixedHeader() {
    if (this.header.classList.contains(this.classNames.stick)) {
      this.header.classList.add(this.classNames.show);
    }
  }

  hideFixedHeader() {
    if (this.header.classList.contains(this.classNames.stick)) {
      this.header.classList.remove(this.classNames.show);
    }
  }

  destroy() {
    this.scrollParent.removeEventListener('scroll', this.handleScroll);
  }
}
<|endoftext|>


<commit_before>let todoList = {
  todos: [],
  addTodo: function(todoText) {
    this.todos.push({
      todoText: todoText,
      completed: false
    });
  },
  changeTodo: function(position, todoText) {
    this.todos[position].todoText = todoText;
  },
  deleteTodo: function(position) {
    this.todos.splice(position, 1);
  },
  toggleCompleted: function(position) {
    let todo = this.todos[position];
    todo.completed = !todo.completed;
  },
  toggleAll: function() {
    let totalTodos = this.todos.length;
    let completedTodos = 0;
    this.todos.forEach(function(todo){
      if(todo.completed === true) {
        completedTodos++;
      }
    });
    this.todos.forEach(function(todo){
      if(completedTodos === totalTodos) {
        todo.completed = false;
      } else {
        todo.completed = true;
      }
    })
  }
};

let handlers = {
  addTodo: function() {
    let addTodoTextInput = document.getElementById('addTodoTextInput');
    if(isEmpty(addTodoTextInput) === true) {
      return;
    } else {
      todoList.addTodo(addTodoTextInput.value);
      addTodoTextInput.value = "";
    }
    view.displayTodos();
  },
  changeTodo: function() {
    let editTodoPosition = document.getElementById('editTodoPosition');
    let editTodoTextInput = document.getElementById('editTodoTextInput');
    if(isEmpty(editTodoPosition) === true){
      return;
    } else if(isEmpty(editTodoTextInput) === true) {
      return;
    } else {
      todoList.changeTodo(editTodoPosition.valueAsNumber, editTodoTextInput.value);
      editTodoPosition.value = "";
      editTodoTextInput.value = "";
    }
    view.displayTodos();
  },
  deleteTodo: function(position) {
    todoList.deleteTodo(position);
    view.displayTodos();
  },
  toggleCompleted: function() {
    let toggleCompletedPosition = document.getElementById('toggleCompletedPosition');
    if(isEmpty(toggleCompletedPosition) === true) {
      return;
    } else {
      todoList.toggleCompleted(toggleCompletedPosition.valueAsNumber);
      toggleCompletedPosition.value = "";
    }
    view.displayTodos();
  },
  toggleAll: function() {
    todoList.toggleAll();
    view.displayTodos();
  },
}

let view = {
  displayTodos: function() {
    let todosUl = document.querySelector('ul');
    todosUl.innerHTML = "";
    for(let i = 0; i < todoList.todos.length; i++) {
      let todo = todoList.todos[i];
      let todoLi = document.createElement('li');
      let todoTextWithCompletion = "";
      if(todo.completed === true) {
        todoTextWithCompletion = "(x) " + todo.todoText;
         } else {
           todoTextWithCompletion = "() " + todo.todoText;
         }
      todoLi.id = i;
      todoLi.textContent = todoTextWithCompletion;
      todoLi.appendChild(this.createDeleteButton());
      todosUl.appendChild(todoLi);
    }
  },
  createDeleteButton: function() {
    let deleteButton = document.createElement('button');
    deleteButton.textContent = "Delete";
    deleteButton.className = "deleteButton";
    return deleteButton;
  },
  setUpEventListeners: function() {
    let todosUl = document.querySelector('ul');
    todosUl.addEventListener('click', function(event) {
      let elementClicked = event.target;
      if(elementClicked.className === "deleteButton") {
        handlers.deleteTodo(parseInt(event.target.parentNode.id));
      }
    });
  }
}

function isEmpty(el) {
  let elValue = el.value;
  if(el.value === "" || null) {
    alert("This input can't be empty.");
    return true;
  }
  return false;
}

view.setUpEventListeners();<commit_msg>version 11 done<commit_after>let todoList = {
  todos: [],
  addTodo: function(todoText) {
    this.todos.push({
      todoText: todoText,
      completed: false
    });
  },
  changeTodo: function(position, todoText) {
    this.todos[position].todoText = todoText;
  },
  deleteTodo: function(position) {
    this.todos.splice(position, 1);
  },
  toggleCompleted: function(position) {
    let todo = this.todos[position];
    todo.completed = !todo.completed;
  },
  toggleAll: function() {
    let totalTodos = this.todos.length;
    let completedTodos = 0;
    this.todos.forEach(function(todo){
      if(todo.completed === true) {
        completedTodos++;
      }
    });
    this.todos.forEach(function(todo){
      if(completedTodos === totalTodos) {
        todo.completed = false;
      } else {
        todo.completed = true;
      }
    })
  }
};

let handlers = {
  addTodo: function() {
    let addTodoTextInput = document.getElementById('addTodoTextInput');
    if(isEmpty(addTodoTextInput) === true) {
      return;
    } else {
      todoList.addTodo(addTodoTextInput.value);
      addTodoTextInput.value = "";
    }
    view.displayTodos();
  },
  changeTodo: function() {
    let editTodoPosition = document.getElementById('editTodoPosition');
    let editTodoTextInput = document.getElementById('editTodoTextInput');
    if(isEmpty(editTodoPosition) === true){
      return;
    } else if(isEmpty(editTodoTextInput) === true) {
      return;
    } else {
      todoList.changeTodo(editTodoPosition.valueAsNumber, editTodoTextInput.value);
      editTodoPosition.value = "";
      editTodoTextInput.value = "";
    }
    view.displayTodos();
  },
  deleteTodo: function(position) {
    todoList.deleteTodo(position);
    view.displayTodos();
  },
  toggleCompleted: function() {
    let toggleCompletedPosition = document.getElementById('toggleCompletedPosition');
    if(isEmpty(toggleCompletedPosition) === true) {
      return;
    } else {
      todoList.toggleCompleted(toggleCompletedPosition.valueAsNumber);
      toggleCompletedPosition.value = "";
    }
    view.displayTodos();
  },
  toggleAll: function() {
    todoList.toggleAll();
    view.displayTodos();
  },
}

let view = {
  displayTodos: function() {
    let todosUl = document.querySelector('ul');
    todosUl.innerHTML = "";
    todoList.todos.forEach(function(todo, position){
      let todoLi = document.createElement('li');
      let todoTextWithCompletion = "";
      if(todo.completed === true) {
        todoTextWithCompletion = "(x) " + todo.todoText;
         } else {
           todoTextWithCompletion = "() " + todo.todoText;
         }
      todoLi.id = position;
      todoLi.textContent = todoTextWithCompletion;
      todoLi.appendChild(this.createDeleteButton());
      todosUl.appendChild(todoLi);
    }, this);
  },
  createDeleteButton: function() {
    let deleteButton = document.createElement('button');
    deleteButton.textContent = "Delete";
    deleteButton.className = "deleteButton";
    return deleteButton;
  },
  setUpEventListeners: function() {
    let todosUl = document.querySelector('ul');
    todosUl.addEventListener('click', function(event) {
      let elementClicked = event.target;
      if(elementClicked.className === "deleteButton") {
        handlers.deleteTodo(parseInt(event.target.parentNode.id));
      }
    });
  }
}

function isEmpty(el) {
  let elValue = el.value;
  if(el.value === "" || null) {
    alert("This input can't be empty.");
    return true;
  }
  return false;
}

view.setUpEventListeners();<|endoftext|>


<commit_before>var querystring = require('querystring');
var urlParser = require('url').parse;
var request = require('superagent');
var parseString = require('xml2js').parseString;

function validateTicket(ticket) {
  return /^ST-.{29,256}$/.test(ticket);
}

function getTicketIfPresentInUrl(url) {
  if (!url) {
    return false;
  }

  var parsedUrl = urlParser(url, true);

  if (parsedUrl.query && parsedUrl.query.ticket) {
    return parsedUrl.query.ticket;
  }

  return false;
}

function setTicket(req, res, next, ticket, options, originUrl) {
  var validationUrl = options.serviceValidate + querystring.stringify({
    service: originUrl,
    ticket: ticket});

  request.get(validationUrl, function (response) {
    if (!response.ok) {
      return res.redirect(301, originUrl);
    }
    parseString(response.text, function (err, result) {
      if (err) {
        return res.redirect(301, originUrl);
      }

      var serviceResponse = result['cas:serviceResponse'];

      if (serviceResponse) {
        var authenticationSuccess = serviceResponse['cas:authenticationSuccess'];

        if (authenticationSuccess) {
          var username = '';
          if (authenticationSuccess.length === 1) {
            var firstUserElement = authenticationSuccess[0];
            var firstUser = firstUserElement['cas:user'];
            username = firstUser[0];
          }

          req.session.cas = {
            st: ticket,
            user: username
          };

          if (options.loggedInEventHandler && typeof options.loggedInEventHandler === 'function') {
            options.loggedInEventHandler(req.session);
          }
        }

        return res.redirect(301, originUrl);
      }
    });
  });
}

function shouldIgnoreRequest(req, options) {
  return options.ignoredRoutes.some(function(element) {
    return element.matches(req.url);
  });
}

function stripTicketFromUrl(url) {
  return url.replace(/(ticket=[\w-\d]*[&])|([&]ticket=[\w\d-]*)|([?]ticket=[\w\d-]*)/, "");
}

function getCasLoginUrl(options, serviceUrl) {
  return options.host + '/cas/login?' + querystring.stringify({service: serviceUrl});
}

module.exports = function (options) {
  return function (req, res, next) {
    var fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;

    if(shouldIgnoreRequest(req, options)) {
      return next();
    }

    var ticket = getTicketIfPresentInUrl(req.url);
    var originUrl = stripTicketFromUrl(fullUrl);

    if (ticket) {
      if (!validateTicket(ticket)) {
        return res.redirect(307, originUrl);
      }

      if (!req.session || !req.session.cas || !req.session.cas.st) {
        return setTicket(req, res, next, ticket, options, originUrl);

      } else {
        return next();
      }
    } else if (req.session && req.session.cas && req.session.cas.st) {
      next();
    } else {
      return res.redirect(301, getCasLoginUrl(options, originUrl));
    }
  };
};
<commit_msg>use options.service url if provided<commit_after>var querystring = require('querystring');
var urlParser = require('url').parse;
var request = require('superagent');
var parseString = require('xml2js').parseString;

function validateTicket(ticket) {
  return /^ST-.{29,256}$/.test(ticket);
}

function getTicketIfPresentInUrl(url) {
  if (!url) {
    return false;
  }

  var parsedUrl = urlParser(url, true);

  if (parsedUrl.query && parsedUrl.query.ticket) {
    return parsedUrl.query.ticket;
  }

  return false;
}

function setTicket(req, res, next, ticket, options, originUrl) {
  var validationUrl = options.serviceValidate + querystring.stringify({
    service: originUrl,
    ticket: ticket});

  request.get(validationUrl, function (response) {
    if (!response.ok) {
      return res.redirect(301, originUrl);
    }
    parseString(response.text, function (err, result) {
      if (err) {
        return res.redirect(301, originUrl);
      }

      var serviceResponse = result['cas:serviceResponse'];

      if (serviceResponse) {
        var authenticationSuccess = serviceResponse['cas:authenticationSuccess'];

        if (authenticationSuccess) {
          var username = '';
          if (authenticationSuccess.length === 1) {
            var firstUserElement = authenticationSuccess[0];
            var firstUser = firstUserElement['cas:user'];
            username = firstUser[0];
          }

          req.session.cas = {
            st: ticket,
            user: username
          };

          if (options.loggedInEventHandler && typeof options.loggedInEventHandler === 'function') {
            options.loggedInEventHandler(req.session);
          }
        }

        return res.redirect(301, originUrl);
      }
    });
  });
}

function shouldIgnoreRequest(req, options) {
  return options.ignoredRoutes.some(function(element) {
    return element.matches(req.url);
  });
}

function stripTicketFromUrl(url) {
  return url.replace(/(ticket=[\w-\d]*[&])|([&]ticket=[\w\d-]*)|([?]ticket=[\w\d-]*)/, "");
}

function getCasLoginUrl(options, serviceUrl) {
  return options.host + '/cas/login?' + querystring.stringify({service: serviceUrl});
}

module.exports = function (options) {
  return function (req, res, next) {
    var fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;

    if(shouldIgnoreRequest(req, options)) {
      return next();
    }

    var ticket = getTicketIfPresentInUrl(req.url);
    var originUrl = options.service || stripTicketFromUrl(fullUrl);

    if (ticket) {
      if (!validateTicket(ticket)) {
        return res.redirect(307, originUrl);
      }

      if (!req.session || !req.session.cas || !req.session.cas.st) {
        return setTicket(req, res, next, ticket, options, originUrl);

      } else {
        return next();
      }
    } else if (req.session && req.session.cas && req.session.cas.st) {
      next();
    } else {
      return res.redirect(301, getCasLoginUrl(options, originUrl));
    }
  };
};
<|endoftext|>


<commit_before>/* global bootbox */

$(() => {
    $("input[type=checkbox].select-all").change(selectAllChecked);
    $("input[type=checkbox]").change(checkboxChecked);
    // check "Select All" checkbox if all checkboxes of this group are checked
    $("input[type=checkbox]").each((index, target) => {
        const item = $(target).data("select-all-checkbox");
        const group = $(target).data("checkbox-group");
        if (item !== null && group !== null) {
            checkSelectAllIfAllChecked(item, group);
        }
    });
    // scroll to the given anchor
    const params = new URLSearchParams(location.search);
    const jumpTo = params.get('jumpto');
    if (jumpTo && jumpTo.length > 0) {
        location.href = "#" + jumpTo;
    }
});
// scrolls to an anchor with animation
scrollToAnchor = (aid) => {
    const aTag = $("a[name='" + aid + "']");
    $('html,body').animate({
        scrollTop: aTag.offset().top
    }, 'slow');
};
refreshCodeMirrors = () => {
    $('.CodeMirror').each((i, el) =>
        el.CodeMirror.refresh()
    );
};
// shakes a div (animation)
// This is used when login fails
shake = (div) => {
    const interval = 100;
    const distance = 10;
    const times = 4;
    $(div).css('position', 'relative');
    for (let iter = 0; iter < (times + 1); iter++) {
        $(div).animate({
            left: ((iter % 2 === 0 ? distance : distance * -1))
        }, interval);
    }// for

    $(div).animate({
        left: 0
    }, interval);
};
// this bind an event to a checkbox to toggle a password field between clear
// text and stars
bindTogglePassword = (inputField, checkboxField) => {
    const input = $(inputField);
    const checkbox = $(checkboxField);
    $(checkbox).click(() => {
        if ($(checkbox).is(':checked')) {
            $(input).attr('type', 'text');
        } else {
            $(input).attr('type', 'password');
        }
    });
};
checkboxChecked = (event) => {
    const item = $(event.target).data("select-all-checkbox");
    const group = $(event.target).data("checkbox-group");
    checkSelectAllIfAllChecked(item, group);
};
checkSelectAllIfAllChecked = (item, group) => {
    if (!item) {
        return;
    }
// if the count of the checked checkboxes in the group is equal to the count
// of all checkboxes in this group
    const allSelected = $("input[type=checkbox][data-checkbox-group='" + group
            + "']:checked").length === $("input[type=checkbox][data-checkbox-group='"
            + group + "']").length;
    // check the "Select All" Checkbox, else uncheck it
    $(item).prop("checked", allSelected).change();
};
selectAllChecked = (event) => {
    const selectAllCheckbox = $(event.target);
    const target = $(selectAllCheckbox).data("target");
    $(target).prop("checked",
            $(selectAllCheckbox).
            is(":checked")).change();
};
setWaitCursor = () => {
    $('body').css('cursor', 'progress');
};
setDefaultCursor = () => {
    $('body').css('cursor', 'auto');
};
initRemoteAlerts = (rootElement) => {
    $(rootElement).find(".remote-alert").click((event) => {
        event.preventDefault();
        setWaitCursor();
        const url = $(event.currentTarget).data("url");
        $.get(url, (result) => {
            setDefaultCursor();
            bootbox.alert(result);
        });
    });
};
initDataTables = (rootElement) => {
// Sortable and searchable tables

// Internet Exploder doesn't support URLSearchParams,
// but which caveman are still using IE?
// Fuck IE, Fuck Microsuck since Windows 8
    const urlParams = new URLSearchParams(window.location.search);
    // get current action from url
    // this is used as identifier when saving and loading the state
    const action = urlParams.get('action');
    $(rootElement).find(".tablesorter").DataTable({
        language: {
            url: $("body").data("datatables-translation")
        },
        deferRender: true,
        stateSave: true,
        stateDuration: 0,
        stateSaveCallback: (settings, data) => {
            console.log(settings, data);
            localStorage.setItem(
                    "DataTables_" + action + "_"
                    + settings.sInstance, JSON.stringify(data)
                    );
        },
        stateLoadCallback: (settings) =>
            JSON.parse(
                    localStorage.getItem(
                            "DataTables_" + action + "_" + settings.sInstance)
                    )
        ,
        drawCallback: (settings) => {
            $(`#${settings.sInstance}`).find("a.btn").click(
                    (event) => {
                const target = $(event.currentTarget);
                if ((target.hasClass("disabled") || target.attr("disabled")) && target.attr("href").length > 1) {
                    event.preventDefault();
                }
            });

        },
        columnDefs: [{targets: "no-sort", orderable: false}]
    });
};
<commit_msg>Update util.js<commit_after>/* global bootbox */

$(() => {
    $("input[type=checkbox].select-all").change(selectAllChecked);
    $("input[type=checkbox]").change(checkboxChecked);
    // check "Select All" checkbox if all checkboxes of this group are checked
    $("input[type=checkbox]").each((index, target) => {
        const item = $(target).data("select-all-checkbox");
        const group = $(target).data("checkbox-group");
        if (item !== null && group !== null) {
            checkSelectAllIfAllChecked(item, group);
        }
    });
    // scroll to the given anchor
    const params = new URLSearchParams(location.search);
    const jumpTo = params.get('jumpto');
    if (jumpTo && jumpTo.length > 0) {
        location.href = "#" + jumpTo;
    }
});

// scrolls to an anchor with animation
scrollToAnchor = (aid) => {
    const aTag = $("a[name='" + aid + "']");
    $('html,body').animate({
        scrollTop: aTag.offset().top
    }, 'slow');
};
refreshCodeMirrors = () => {
    $('.CodeMirror').each((i, el) =>
        el.CodeMirror.refresh()
    );
};

// shakes a div (animation)
// This is used when login fails
shake = (div) => {
    const interval = 100;
    const distance = 10;
    const times = 4;
    $(div).css('position', 'relative');
    for (let iter = 0; iter < (times + 1); iter++) {
        $(div).animate({
            left: ((iter % 2 === 0 ? distance : distance * -1))
        }, interval);
    }// for

    $(div).animate({
        left: 0
    }, interval);
};

// this bind an event to a checkbox to toggle a password field between clear
// text and stars
bindTogglePassword = (inputField, checkboxField) => {
    const input = $(inputField);
    const checkbox = $(checkboxField);
    $(checkbox).click(() => {
        if ($(checkbox).is(':checked')) {
            $(input).attr('type', 'text');
        } else {
            $(input).attr('type', 'password');
        }
    });
};
checkboxChecked = (event) => {
    const item = $(event.target).data("select-all-checkbox");
    const group = $(event.target).data("checkbox-group");
    checkSelectAllIfAllChecked(item, group);
};
checkSelectAllIfAllChecked = (item, group) => {
    if (!item) {
        return;
    }

    // if the count of the checked checkboxes in the group is equal to the count
    // of all checkboxes in this group
    const allSelected = $("input[type=checkbox][data-checkbox-group='" + group
            + "']:checked").length === $("input[type=checkbox][data-checkbox-group='"
            + group + "']").length;
    // check the "Select All" Checkbox, else uncheck it
    $(item).prop("checked", allSelected).change();
};
selectAllChecked = (event) => {
    const selectAllCheckbox = $(event.target);
    const target = $(selectAllCheckbox).data("target");
    $(target).prop("checked",
            $(selectAllCheckbox).
            is(":checked")).change();
};
setWaitCursor = () => {
    $('body').css('cursor', 'progress');
};
setDefaultCursor = () => {
    $('body').css('cursor', 'auto');
};
initRemoteAlerts = (rootElement) => {
    $(rootElement).find(".remote-alert").click((event) => {
        event.preventDefault();
        setWaitCursor();
        const url = $(event.currentTarget).data("url");
        $.get(url, (result) => {
            setDefaultCursor();
            bootbox.alert(result);
        });
    });
};
initDataTables = (rootElement) => {
    // Sortable and searchable tables
    // Internet Exploder doesn't support URLSearchParams,
    // but which caveman are still using IE?
    // Fuck IE, Fuck Microsuck since Windows 8
    const urlParams = new URLSearchParams(window.location.search);
    // get current action from url
    // this is used as identifier when saving and loading the state
    const action = urlParams.get('action');
    $(rootElement).find(".tablesorter").DataTable({
        language: {
            url: $("body").data("datatables-translation")
        },
        deferRender: true,
        stateSave: true,
        stateDuration: 0,
        stateSaveCallback: (settings, data) => {
            console.log(settings, data);
            localStorage.setItem(
                    "DataTables_" + action + "_"
                    + settings.sInstance, JSON.stringify(data)
                    );
        },
        stateLoadCallback: (settings) =>
            JSON.parse(
                    localStorage.getItem(
                            "DataTables_" + action + "_" + settings.sInstance)
                    )
        ,
        drawCallback: (settings) => {
            $(`#${settings.sInstance}`).find("a.btn").click(
                    (event) => {
                const target = $(event.currentTarget);
                if ((target.hasClass("disabled") || target.attr("disabled")) && target.attr("href").length > 1) {
                    event.preventDefault();
                }
            });

        },
        columnDefs: [{targets: "no-sort", orderable: false}]
    });
};
<|endoftext|>


<commit_before>(function(sc) {
  "use strict";

  require("../compiler");
  require("../scope");
  require("../node");

  var Token = sc.lang.compiler.Token;
  var Message = sc.lang.compiler.Message;

  var Scope = sc.lang.compiler.scope({
    begin: function() {
      var declared = this.getDeclaredVariable();
      this.stack.push({
        vars: {},
        args: {},
        declared: declared
      });
    }
  });

  function Parser(parent, lexer) {
    if (parent) {
      this.parent = parent;
      this.lexer = parent.lexer;
      this.state = parent.state;
      this.scope = parent.scope;
    } else {
      this.parent = null;
      this.lexer = lexer;
      this.state = {
        innerElements: false,
        immutableList: false,
        underscore: [],
        binaryPrecedence: {}
      };
      this.scope = new Scope(this);
    }
  }

  Parser.addParseMethod = function(name, method) {
    Parser.prototype["parse" + name] = method;
  };

  Object.defineProperty(Parser.prototype, "lookahead", {
    get: function() {
      return this.lexer.lookahead;
    }
  });

  Parser.prototype.lex = function() {
    return this.lexer.lex();
  };

  Parser.prototype.unlex = function(token) {
    this.lexer.unlex(token);
    return this;
  };

  Parser.prototype.expect = function(value) {
    var token = this.lexer.lex();
    if (token.type !== Token.Punctuator || token.value !== value) {
      this.throwUnexpected(token, value);
    }
    return token;
  };

  Parser.prototype.match = function(value) {
    return this.lexer.lookahead.value === value;
  };

  Parser.prototype.matchAny = function(list) {
    var value = this.lexer.lookahead.value;
    for (var i = 0, imax = list.length; i < imax; ++i) {
      if (list[i] === value) {
        return value;
      }
    }
    return null;
  };

  Parser.prototype.createMarker = function(node) {
    return this.lexer.createMarker(node);
  };

  Parser.prototype.hasNextToken = function() {
    return this.lookahead.type !== Token.EOF;
  };

  Parser.prototype.throwError = function() {
    return this.lexer.throwError.apply(this.lexer, arguments);
  };

  Parser.prototype.throwUnexpected = function(token) {
    switch (token.type) {
    case Token.EOF:
      this.throwError(token, Message.UnexpectedEOS);
      break;
    case Token.FloatLiteral:
    case Token.IntegerLiteral:
      this.throwError(token, Message.UnexpectedNumber);
      break;
    case Token.CharLiteral:
    case Token.StringLiteral:
    case Token.SymbolLiteral:
      this.throwError(token, Message.UnexpectedLiteral, token.type.toLowerCase());
      break;
    case Token.Identifier:
      this.throwError(token, Message.UnexpectedIdentifier);
      break;
    default:
      this.throwError(token, Message.UnexpectedToken, token.value);
      break;
    }
  };

  Parser.prototype.withScope = function(fn) {
    var result;

    this.scope.begin();
    result = fn.call(this);
    this.scope.end();

    return result;
  };

  sc.lang.compiler.Parser = Parser;
})(sc);
<commit_msg>:fire: fix to remove unused parser.state binaryPrecedence<commit_after>(function(sc) {
  "use strict";

  require("../compiler");
  require("../scope");
  require("../node");

  var Token = sc.lang.compiler.Token;
  var Message = sc.lang.compiler.Message;

  var Scope = sc.lang.compiler.scope({
    begin: function() {
      var declared = this.getDeclaredVariable();
      this.stack.push({
        vars: {},
        args: {},
        declared: declared
      });
    }
  });

  function Parser(parent, lexer) {
    if (parent) {
      this.parent = parent;
      this.lexer = parent.lexer;
      this.state = parent.state;
      this.scope = parent.scope;
    } else {
      this.parent = null;
      this.lexer = lexer;
      this.state = {
        innerElements: false,
        immutableList: false,
        underscore: []
      };
      this.scope = new Scope(this);
    }
  }

  Parser.addParseMethod = function(name, method) {
    Parser.prototype["parse" + name] = method;
  };

  Object.defineProperty(Parser.prototype, "lookahead", {
    get: function() {
      return this.lexer.lookahead;
    }
  });

  Parser.prototype.lex = function() {
    return this.lexer.lex();
  };

  Parser.prototype.unlex = function(token) {
    this.lexer.unlex(token);
    return this;
  };

  Parser.prototype.expect = function(value) {
    var token = this.lexer.lex();
    if (token.type !== Token.Punctuator || token.value !== value) {
      this.throwUnexpected(token, value);
    }
    return token;
  };

  Parser.prototype.match = function(value) {
    return this.lexer.lookahead.value === value;
  };

  Parser.prototype.matchAny = function(list) {
    var value = this.lexer.lookahead.value;
    for (var i = 0, imax = list.length; i < imax; ++i) {
      if (list[i] === value) {
        return value;
      }
    }
    return null;
  };

  Parser.prototype.createMarker = function(node) {
    return this.lexer.createMarker(node);
  };

  Parser.prototype.hasNextToken = function() {
    return this.lookahead.type !== Token.EOF;
  };

  Parser.prototype.throwError = function() {
    return this.lexer.throwError.apply(this.lexer, arguments);
  };

  Parser.prototype.throwUnexpected = function(token) {
    switch (token.type) {
    case Token.EOF:
      this.throwError(token, Message.UnexpectedEOS);
      break;
    case Token.FloatLiteral:
    case Token.IntegerLiteral:
      this.throwError(token, Message.UnexpectedNumber);
      break;
    case Token.CharLiteral:
    case Token.StringLiteral:
    case Token.SymbolLiteral:
      this.throwError(token, Message.UnexpectedLiteral, token.type.toLowerCase());
      break;
    case Token.Identifier:
      this.throwError(token, Message.UnexpectedIdentifier);
      break;
    default:
      this.throwError(token, Message.UnexpectedToken, token.value);
      break;
    }
  };

  Parser.prototype.withScope = function(fn) {
    var result;

    this.scope.begin();
    result = fn.call(this);
    this.scope.end();

    return result;
  };

  sc.lang.compiler.Parser = Parser;
})(sc);
<|endoftext|>


<commit_before>import React from 'react';
import { storiesOf } from '@kadira/storybook';
import AspectRatio from '../index';

import '../../aspect-ratio.css';

storiesOf('AspectRatio', module)
  .add('Image', () => (

Image with Aspect Ratio

      <AspectRatio ratio="3/4" style={{ maxWidth: '400px' }}>

      </AspectRatio>

  ))
  .add('Background Image', () => (

Background image with aspect ratio

      <AspectRatio
        ratio="3/4"
        style={{
          maxWidth: '300px',
          backgroundImage: 'url(https://c1.staticflickr.com/4/3896/14550191836_cc0675d906.jpg)',
          backgroundSize: 'cover',
        }}
      />

  ))
  .add('Iframe', () => (

Iframe with aspect ratio

      <AspectRatio ratio="560/315" style={{ maxWidth: '560px' }}>

      </AspectRatio>

  ));
<commit_msg>Update iframe url (the poor video was removed)<commit_after>import React from 'react';
import { storiesOf } from '@kadira/storybook';
import AspectRatio from '../index';

import '../../aspect-ratio.css';

storiesOf('AspectRatio', module)
  .add('Image', () => (

Image with Aspect Ratio

      <AspectRatio ratio="3/4" style={{ maxWidth: '400px' }}>

      </AspectRatio>

  ))
  .add('Background Image', () => (

Background image with aspect ratio

      <AspectRatio
        ratio="3/4"
        style={{
          maxWidth: '300px',
          backgroundImage: 'url(https://c1.staticflickr.com/4/3896/14550191836_cc0675d906.jpg)',
          backgroundSize: 'cover',
        }}
      />

  ))
  .add('Iframe', () => (

Iframe with aspect ratio

      <AspectRatio ratio="560/315" style={{ maxWidth: '560px' }}>

      </AspectRatio>

  ));
<|endoftext|>


<commit_before>var debug = require('debug')('json-schema');

var loopbackExplorer = require('loopback-explorer');
var Model = require('loopback').Model;

var config = require('../support/config');
var logger = require('../support/logger')
var Links = require('./links');
var modelPropertiesSanitizer = require('./model-properties-sanitizer');

var ItemSchema = module.exports = Model.extend('item-schema');

ItemSchema.findByCollectionName = function(collectionName, callback) {
    ItemSchema.findOne({ where: { collectionName: collectionName }}, function(err, jsonSchema) {
        if (err) {
            logger.error("Error fetching JSON Schema for collectionName:", collectionName, "Error:", err);
        } else if (jsonSchema === null) {
            logger.warn("JSON Schema for collectionName", collectionName, "not found.");
        }
        callback(err, jsonSchema);
    });
};

ItemSchema.prototype.allLinks = function() {
    var links = new Links(this.defaultLinks(), this.links);
    return links.all();
};

ItemSchema.prototype.customLinks = function() {
    var links = new Links(this.defaultLinks(), this.links);
    return links.custom();
};

ItemSchema.prototype.defaultLinks = function() {
    var itemUrlTemplate = this.itemUrlTemplate();
    var collectionUrl = this.collectionUrl();
    var schemaUrl = this.url();
    return [
        { rel: 'self', href: itemUrlTemplate },
        { rel: 'item', href: itemUrlTemplate },
        { rel: 'create', method: 'POST', href: collectionUrl, schema: { $ref: schemaUrl } },
        { rel: 'update', method: 'PUT', href: itemUrlTemplate },
        { rel: 'delete', method: 'DELETE', href: itemUrlTemplate }
    ];
};

ItemSchema.prototype.url = function() {
    var path = '/' + this.constructor.pluralModelName + '/' + this.id;
    return path;
};

ItemSchema.prototype.itemUrlTemplate = function() {
    return this.collectionUrl() + '/{id}';
};

ItemSchema.prototype.collectionUrl = function() {
    var path = '/' + this.collectionName;
    return path;
};

ItemSchema.prototype.update$schema = function() {
    if (!this.$schema) {
        this.$schema = "http://json-schema.org/draft-04/hyper-schema#";
    }
};

ItemSchema.prototype.registerLoopbackModel = function(app) {
    logger.debug('this:', this);
    logger.debug('this.modelName:', this.modelName);
    logger.debug('this.collectionName:', this.collectionName);

    var JsonSchemaModel = ItemSchema.dataSource.createModel(this.modelName, {}, { plural: this.collectionName });
    app.model(JsonSchemaModel);
    loopbackExplorer(app);
};

ItemSchema.prototype.collectionSchema = function() {
    return new config.CollectionSchemaClass(this);
};

ItemSchema.on('attached', function(app) {
    ItemSchema.afterInitialize = function() {
        modelPropertiesSanitizer.restore(this);
        this.links = this.allLinks();
    };

    ItemSchema.beforeSave = function(next) {
        this.update$schema();
        this.links = this.customLinks();
        modelPropertiesSanitizer.sanitize(this);
        next();
    };

    ItemSchema.afterSave = function(done) {
        modelPropertiesSanitizer.restore(this);
        this.registerLoopbackModel(app);
        this.links = this.allLinks();
        done();
    };
});
<commit_msg>Change registerLoopbackModel implementation to explicitly extend from Model.<commit_after>var debug = require('debug')('json-schema');

var loopbackExplorer = require('loopback-explorer');
var Model = require('loopback').Model;

var config = require('../support/config');
var logger = require('../support/logger')
var Links = require('./links');
var modelPropertiesSanitizer = require('./model-properties-sanitizer');

var ItemSchema = module.exports = Model.extend('item-schema');

ItemSchema.findByCollectionName = function(collectionName, callback) {
    ItemSchema.findOne({ where: { collectionName: collectionName }}, function(err, jsonSchema) {
        if (err) {
            logger.error("Error fetching JSON Schema for collectionName:", collectionName, "Error:", err);
        } else if (jsonSchema === null) {
            logger.warn("JSON Schema for collectionName", collectionName, "not found.");
        }
        callback(err, jsonSchema);
    });
};

ItemSchema.prototype.allLinks = function() {
    var links = new Links(this.defaultLinks(), this.links);
    return links.all();
};

ItemSchema.prototype.customLinks = function() {
    var links = new Links(this.defaultLinks(), this.links);
    return links.custom();
};

ItemSchema.prototype.defaultLinks = function() {
    var itemUrlTemplate = this.itemUrlTemplate();
    var collectionUrl = this.collectionUrl();
    var schemaUrl = this.url();
    return [
        { rel: 'self', href: itemUrlTemplate },
        { rel: 'item', href: itemUrlTemplate },
        { rel: 'create', method: 'POST', href: collectionUrl, schema: { $ref: schemaUrl } },
        { rel: 'update', method: 'PUT', href: itemUrlTemplate },
        { rel: 'delete', method: 'DELETE', href: itemUrlTemplate }
    ];
};

ItemSchema.prototype.url = function() {
    var path = '/' + this.constructor.pluralModelName + '/' + this.id;
    return path;
};

ItemSchema.prototype.itemUrlTemplate = function() {
    return this.collectionUrl() + '/{id}';
};

ItemSchema.prototype.collectionUrl = function() {
    var path = '/' + this.collectionName;
    return path;
};

ItemSchema.prototype.update$schema = function() {
    if (!this.$schema) {
        this.$schema = "http://json-schema.org/draft-04/hyper-schema#";
    }
};

ItemSchema.prototype.registerLoopbackModel = function(app) {
    logger.debug('this:', this);
    logger.debug('this.modelName:', this.modelName);
    logger.debug('this.collectionName:', this.collectionName);

    var dataSource = ItemSchema.dataSource;
    var JsonSchemaModel = Model.extend(this.modelName, {}, { plural: this.collectionName });
    JsonSchemaModel.attachTo(dataSource);
    app.model(JsonSchemaModel);
    loopbackExplorer(app);
};

ItemSchema.prototype.collectionSchema = function() {
    return new config.CollectionSchemaClass(this);
};

ItemSchema.on('attached', function(app) {
    ItemSchema.afterInitialize = function() {
        modelPropertiesSanitizer.restore(this);
        this.links = this.allLinks();
    };

    ItemSchema.beforeSave = function(next) {
        this.update$schema();
        this.links = this.customLinks();
        modelPropertiesSanitizer.sanitize(this);
        next();
    };

    ItemSchema.afterSave = function(done) {
        modelPropertiesSanitizer.restore(this);
        this.registerLoopbackModel(app);
        this.links = this.allLinks();
        done();
    };
});
<|endoftext|>


<commit_before>import { app } from '@mindhive/di'

export default (validateLoginDeniedReason = user => user.disabled && 'user disabled') =>
  ({ Accounts }) => {

    Accounts.validateLoginAttempt(
      ({
        connection,
        user,
        type,
        error,
        methodName,
      }) => {
        const deniedReason = validateLoginDeniedReason(user)
        let action = 'Login success'
        if (error) {
          action = 'Login failure'
        } else if (deniedReason) {
          action = `Login denied: ${deniedReason}`
        }
        app().audit.log(
          connection,
          methodName ? `call:${methodName}` : 'callback:validateAndAuditLogins',
          user,
          {
            action,
            data: {
              authService: type,
              errorMessage: error ? error.toString() : null,
            },
          }
        )
        return ! deniedReason
      }
    )

    Accounts.onLogout(
      ({
        connection,
        user,
      }) => {
        app().audit.log(
          connection,
          'callback:onLogout',
          user,
          {
            action: 'Logout',
          }
        )
      }
    )

  }
<commit_msg>Export defaultValidateLoginDeniedReason<commit_after>import { app } from '@mindhive/di'

export const defaultValidateLoginDeniedReason = user =>
  user.disabled && 'user disabled'

export default (validateLoginDeniedReason = defaultValidateLoginDeniedReason) =>
  ({ Accounts }) => {

    Accounts.validateLoginAttempt(
      ({
        connection,
        user,
        type,
        error,
        methodName,
      }) => {
        const deniedReason = validateLoginDeniedReason(user)
        let action = 'Login success'
        if (error) {
          action = 'Login failure'
        } else if (deniedReason) {
          action = `Login denied: ${deniedReason}`
        }
        app().audit.log(
          connection,
          methodName ? `call:${methodName}` : 'callback:validateAndAuditLogins',
          user,
          {
            action,
            data: {
              authService: type,
              errorMessage: error ? error.toString() : null,
            },
          }
        )
        return ! deniedReason
      }
    )

    Accounts.onLogout(
      ({
        connection,
        user,
      }) => {
        app().audit.log(
          connection,
          'callback:onLogout',
          user,
          {
            action: 'Logout',
          }
        )
      }
    )

  }
<|endoftext|>


<commit_before>'use strict';

/**
 * Config
 * @todo DRY
 */
module.exports = {
  middleware: require('./middleware'),
  routes: require('./routes'),
  options: require('./options')
};
<commit_msg>Redis config separated.<commit_after>'use strict';

/**
 * Redis Configuration
 */
module.exports = {
  port: 6379,
  host: '127.0.0.1',
  options: {}
};
<|endoftext|>


<commit_before>import { HmisClient } from '/imports/api/hmis-api';

Meteor.publish(
  'eligibleClients', function publishEligibleClients() {
    const self = this;
    let stopFunction = false;
    self.unblock();

    self.onStop(() => {
      stopFunction = true;
    });

    if (!this.userId) {
      return [];
    }

    try {
      const hc = HmisClient.create(this.userId);
      const eligibleClients = hc.api('house-matching').getEligibleClients();

      // populate the list without the details
      for (let i = 0; i < eligibleClients.length && !stopFunction; i += 1) {
        eligibleClients[i].clientDetails = {
          clientId: '',
          firstName: '',
          middleName: '',
          lastName: '',
          schema: '',
        };
        self.added('eligibleClients', eligibleClients[i].clientId, eligibleClients[i]);
      }
      self.ready();

      // Add client details (Name & link to profile) here.
      for (let i = 0; i < eligibleClients.length && !stopFunction; i += 1) {
        if (eligibleClients[i].links && eligibleClients[i].links.length > 0) {
          let schema = 'v2015';
          if (eligibleClients[i].links[0].href.indexOf('v2014') !== -1) {
            schema = 'v2014';
          }
          const clientDetails = hc.api('client').getClient(eligibleClients[i].clientId, schema);
          clientDetails.schema = schema;

          self.changed('eligibleClients', eligibleClients[i].clientId, {
            clientDetails,
          });
        }
      }
    } catch (err) {
      logger.error('publish eligibleClients', err);
    }
    return self.ready();
  }
);
<commit_msg>filter active list by ignoreMatchProcess<commit_after>import { HmisClient } from '/imports/api/hmis-api';

Meteor.publish(
  'eligibleClients', function publishEligibleClients() {
    const self = this;
    let stopFunction = false;
    self.unblock();

    self.onStop(() => {
      stopFunction = true;
    });

    if (!this.userId) {
      return [];
    }

    try {
      const hc = HmisClient.create(this.userId);
      let eligibleClients = hc.api('house-matching').getEligibleClients();

      // filter by ignoreMatchProcess field
      eligibleClients = _.filter(eligibleClients, (c) => c.ignoreMatchProcess === false);

      // populate the list without the details
      for (let i = 0; i < eligibleClients.length && !stopFunction; i += 1) {
        eligibleClients[i].clientDetails = {
          clientId: '',
          firstName: '',
          middleName: '',
          lastName: '',
          schema: '',
        };
        self.added('eligibleClients', eligibleClients[i].clientId, eligibleClients[i]);
      }
      self.ready();

      // Add client details (Name & link to profile) here.
      for (let i = 0; i < eligibleClients.length && !stopFunction; i += 1) {
        if (eligibleClients[i].links && eligibleClients[i].links.length > 0) {
          let schema = 'v2015';
          if (eligibleClients[i].links[0].href.indexOf('v2014') !== -1) {
            schema = 'v2014';
          }
          const clientDetails = hc.api('client').getClient(eligibleClients[i].clientId, schema);
          clientDetails.schema = schema;

          self.changed('eligibleClients', eligibleClients[i].clientId, {
            clientDetails,
          });
        }
      }
    } catch (err) {
      logger.error('publish eligibleClients', err);
    }
    return self.ready();
  }
);
<|endoftext|>


<commit_before>// -----------------------------------------
// UserLoop
// -----------------------------------------
//
var util = require('../util');
var EventEmitter = require('events').EventEmitter;
var Loop = require('./loop').Loop;
var Program = require('../user/program').Program;
var _ = require('underscore');

var USER_LOOP_OPTIONS = exports.USER_LOOP_OPTIONS = {
    program: undefined,
    duration: Infinity,
    numberOfTimes: Infinity,
    concurrency: 1,
    concurrencyProfile: undefined
};

var UserLoop = exports.UserLoop = function UserLoop(programOrSpec, args, conditions) {
    console.log('new user coming online');
    EventEmitter.call(this);
    if (programOrSpec === "object") {
        var spec = util.defaults(programOrSpec, USER_LOOP_OPTIONS);

        programOrSpec = spec.userProgram;

        if (_(programOrSpec).isString()) {
            programOrSpec = require(programOrSpec);
        }
        conditions = [];
        args = spec.programArguments;

        if (spec.numberOfTimes > 0 && spec.numberOfTimes < Infinity) {
            conditions.push(UserLoop.maxExecutions(spec.numberOfTimes));
        }
        if (spec.duration > 0 && spec.duration < Infinity) {
            conditions.push(UserLoop.timeLimit(spec.duration));
        }
    }

    this.id = util.uid();
    this.programFn = programOrSpec;
    this.programArgs = args || {};
    this.restartProgram();
    this.conditions = conditions || [];
    this.running = false;
};

util.inherits(UserLoop, Loop);

/** Start executing this.fun until any condition in this.conditions
returns false. When the loop completes the 'end' event is emitted. */
UserLoop.prototype.start = function() {
    var self = this,
        startLoop = function() {
            self.emit('start');
            console.log("Starting main program: " + self.programFn.program.name);
            self.loop_(self.headProgram);
        };

    if (self.running) { return; }
    self.running = true;
    process.nextTick(startLoop);
    return this;
};

UserLoop.prototype.stop = function() {
    this.running = false;
};

UserLoop.prototype.wireInDependentPrograms = function(parentProgram, dependents) {
    var self = this;
    _(dependents).each(function(dependent) {
        parentProgram.on(dependent.onEvent, function(eventArgs) {
            console.log('Starting dependent program: ' + dependent.program.name);

            // Creates and starts the child program
            var startChild = function() {
                var child = new Program(dependent.program.program, eventArgs);
                if (dependent.dependents) {
                    self.wireInDependentPrograms(child, dependent.dependents);
                }

                self.loop_(child);
            };

            if (!dependent.cardinality)
                dependent.cardinality = 1;

            // Support only triggering dependent programs some of the time
            if (dependent.cardinality < 1) {
                if (Number.random() < dependent.cardinality) {
                    startChild();
                }
            } else {
                _(dependent.cardinality).times(function() {
                    startChild();
                });
            }

        });
    });
};

UserLoop.prototype.restartProgram = function() {
    if (_(this.programFn).isFunction()) {
        this.headProgram = new Program(this.programFn, this.programArgs);
    } else {
        this.headProgram = new Program(this.programFn.program.program, this.programArgs);
        this.wireInDependentPrograms(this.headProgram, this.programFn.dependents);
    }

};

/** Checks conditions and schedules the next loop iteration. 'startiteration' is emitted before each
iteration and 'enditeration' is emitted after. */
UserLoop.prototype.loop_ = function(program) {
    var self = this, result, active,
        callfun = function() {
            result = null; active = true;
            if (program.finished()) {
                // TODO: How's this gonna work with dependent programs?
                // self.restartProgram();
                return;
            }

            var isRequest = program.pendingIsRequest();
            if (isRequest) {
                self.emit('startiteration');
            }
            program.next(function(res) {
                if (isRequest) {
                    self.emit('enditeration', res);
                }
                self.loop_(program);
            });
        };

    if (self.checkConditions_()) {
        process.nextTick(callfun);
    } else {
        self.running = false;
        self.emit('end');
    }
};
<commit_msg>Made userloop work (at least for clusters)<commit_after>// -----------------------------------------
// UserLoop
// -----------------------------------------
//
var util = require('../util');
var EventEmitter = require('events').EventEmitter;
var Loop = require('./loop').Loop;
var Program = require('../user/program').Program;
var _ = require('underscore');

var USER_LOOP_OPTIONS = exports.USER_LOOP_OPTIONS = {
    program: undefined,
    duration: Infinity,
    numberOfTimes: Infinity,
    concurrency: 1,
    concurrencyProfile: undefined
};

var UserLoop = exports.UserLoop = function UserLoop(programOrSpec, args, conditions) {
    console.log('new user coming online');
    EventEmitter.call(this);

    if (_(programOrSpec).isString()) {
        programOrSpec = require(programOrSpec);
    }

    this.id = util.uid();
    this.programFn = programOrSpec;
    this.programArgs = args || {};
    this.restartProgram();
    this.conditions = conditions || [];
    this.running = false;
};

util.inherits(UserLoop, Loop);

/** Start executing this.fun until any condition in this.conditions
returns false. When the loop completes the 'end' event is emitted. */
UserLoop.prototype.start = function() {
    var self = this,
        startLoop = function() {
            self.emit('start');
            console.log("Starting main program: " + self.programFn.program.name);
            self.loop_(self.headProgram);
        };

    if (self.running) { return; }
    self.running = true;
    process.nextTick(startLoop);
    return this;
};

UserLoop.prototype.stop = function() {
    this.running = false;
};

UserLoop.prototype.wireInDependentPrograms = function(parentProgram, dependents) {
    var self = this;
    _(dependents).each(function(dependent) {
        parentProgram.on(dependent.onEvent, function(eventArgs) {
            console.log('Starting dependent program: ' + dependent.program.name);

            // Creates and starts the child program
            var startChild = function() {
                var child = new Program(dependent.program.program, eventArgs);
                if (dependent.dependents) {
                    self.wireInDependentPrograms(child, dependent.dependents);
                }

                self.loop_(child);
            };

            if (!dependent.cardinality)
                dependent.cardinality = 1;

            // Support only triggering dependent programs some of the time
            if (dependent.cardinality < 1) {
                if (Number.random() < dependent.cardinality) {
                    startChild();
                }
            } else {
                _(dependent.cardinality).times(function() {
                    startChild();
                });
            }

        });
    });
};

UserLoop.prototype.restartProgram = function() {
    if (_(this.programFn).isFunction()) {
        this.headProgram = new Program(this.programFn, this.programArgs);
    } else {
        this.headProgram = new Program(this.programFn.program.program, this.programArgs);
        this.wireInDependentPrograms(this.headProgram, this.programFn.dependents);
    }

};

/** Checks conditions and schedules the next loop iteration. 'startiteration' is emitted before each
iteration and 'enditeration' is emitted after. */
UserLoop.prototype.loop_ = function(program) {
    var self = this, result, active,
        callfun = function() {
            result = null; active = true;
            if (program.finished()) {
                // TODO: How's this gonna work with dependent programs?
                // self.restartProgram();
                return;
            }

            var isRequest = program.pendingIsRequest();
            if (isRequest) {
                self.emit('startiteration');
            }
            program.next(function(res) {
                if (isRequest) {
                    self.emit('enditeration', res);
                }
                self.loop_(program);
            });
        };

    if (self.checkConditions_()) {
        process.nextTick(callfun);
    } else {
        self.running = false;
        self.emit('end');
    }
};
<|endoftext|>


<commit_before>import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import BackToLink from "../BackToLink";
import Button from "../Button";
import Checkbox from "../Checkbox";
import Collapsible from "../Collapsible";
import CollapsibleRow from "../CollapsibleRow";
import ContentLoadWrapper from "../ContentLoadWrapper";
import DatePicker from "../DatePicker";
import DayPicker from "../DayPicker";
import Dropdown from "../Dropdown";
import DropdownWithError from "../DropdownWithError";
import EditableField from "../EditableField";
//import FileUpload from "../FileUpload";
import Flag from "../Flag";
//import FolderPicker from "../FolderPicker";
// GridCell
import InputGroup from "../InputGroup";
import Label from "../Label";
import Modal from "../Modal";
import MultiLineInputWithError from "../MultiLineInputWithError";
import NumberSlider from "../NumberSlider";

import SingleLineInputWithError from "../SingleLineInputWithError";

import { ArrowDownIcon, TableEmptyState } from "../SvgIcons";

storiesOf("BackToLink", module)
    .add("with text", () => <BackToLink onClick={action("clicked")} text="Hello BackToLink"></BackToLink>);

storiesOf("Button", module)
    .add("with text", () => <Button onClick={action("clicked")}>Hello Button</Button>);

storiesOf("Checkbox", module)
    .add("with text", () => <Checkbox value={false} onChange={action("changed")} label="Hello Checkbox"></Checkbox>);

storiesOf("Collapsible", module)
    .add("with text", () => <Collapsible isOpened={true} autoScroll={true} scrollDelay={10}>

Add Term

            <InputGroup>
                <SingleLineInputWithError
                    inputId={"create-term-name"}
                    withLabel={true}
                    label="Required Term *"
                    value="Term Value"
                    onChange={action("changed")}
                    errorMessage="Error"
                />
            </InputGroup>
            <InputGroup>
                <MultiLineInputWithError
                    inputId={"create-term-description"}
                    withLabel={true}
                    label="Description"
                    value="Long descritpion here"
                    onChange={action("changed")}/>
            </InputGroup>

    </Collapsible>);

storiesOf("CollapsibleRow", module)
    .add("with text", () => <CollapsibleRow label={
Click Header To Expand
}
        keepCollapsedContent={true}
        closeOnBlur={false}
        secondaryButtonText="Close"
        buttonsAreHidden={false}
        collapsed={true}
        onChange={action("changed")}>
Test Content
</CollapsibleRow>);

storiesOf("ContentLoadWrapper", module)
    .add("with loading", () =>
        <ContentLoadWrapper loadComplete={false}
            svgSkeleton={
}>

                    Loading...

Item 1

Item 2

Item 3

        </ContentLoadWrapper>
    );

storiesOf("ContentLoadWrapper", module)
    .add("with content", () =>
        <ContentLoadWrapper loadComplete={true}
            svgSkeleton={
}>

Content

Item 1

Item 2

Item 3

        </ContentLoadWrapper>
    );

let startDate = new Date("December 17, 2018 03:24:00");
storiesOf("DatePicker", module)
    .add("with content", () =>

            <DatePicker
                date={startDate}
                updateDate={(date) => action("changed " + date.toString()) }
                isDateRange={false}
                hasTimePicker={true}
                showClearDateButton={false} />

);

storiesOf("DayPicker", module)
    .add("with content", () => <DayPicker />);

storiesOf("Dropdown", module)
    .add("with content", () => <Dropdown label="Test" options={[{ label: "Opt 1", value: 1 }, { label: "Opt 2", value: 2 }, { label: "Opt 3", value: 3 }]} selectedIndex={1}></Dropdown>);

storiesOf("DropdownWithError", module)
    .add("with content", () => <DropdownWithError error={true} errorMessage="Please select an item" label="Test" options={[{ label: "Opt 1", value: 1 }, { label: "Opt 2", value: 2 }, { label: "Opt 3", value: 3 }]}></DropdownWithError>);

storiesOf("EditableField", module)
    .add("with content", () => <EditableField label="Test" value="Content" onFocus={action("focus")} onEnter={action("enter")} />);

// TODO: Need FileUpload example here. It is complicated and most of the dependent required utils live in the Dnn.Admin.Experience project.

storiesOf("Flag", module)
    .add("with content", () => <Flag title="Test" culture="en-US" onClick={action("Clicked")} />);

// TODO: Need FolderPicker example here. Not sure what to put in for the serviceFramework prop

storiesOf("InputGroup", module)
    .add("with content", () =>
        <InputGroup>
            <SingleLineInputWithError
                inputId={"create-term-name"}
                withLabel={true}
                label="Required Term *"
                value="Term Value"
                onChange={action("changed")}
                errorMessage="Error"
            />
        </InputGroup>
    );

storiesOf("Label", module)
    .add("with content", () => );

storiesOf("Modal", module)
    .add("with content", () => <Modal isOpen={true} header="Test Modal">
Hello!
</Modal>);

storiesOf("MultiLineInputWithError", module)
    .add("with content", () => <MultiLineInputWithError
        inputId={"create-term-description"}
        withLabel={true}
        label="Description"
        value="Long descritpion here"
        onChange={action("changed")}/>);

storiesOf("NumberSlider", module)
    .add("with content", () => <NumberSlider min={1} max={100} step={5} value={50} onChange={action("Change")} />);

storiesOf("SingleLineInputWithError", module)
    .add("with content", () =>     <SingleLineInputWithError
        inputId={"create-term-name"}
        withLabel={true}
        label="Required Term *"
        value="Term Value"
        onChange={action("changed")}
        errorMessage="Error"
    />
    );<commit_msg>Add comments and TODO<commit_after>import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import BackToLink from "../BackToLink";
import Button from "../Button";
import Checkbox from "../Checkbox";
import Collapsible from "../Collapsible";
import CollapsibleRow from "../CollapsibleRow";
import ContentLoadWrapper from "../ContentLoadWrapper";
import DatePicker from "../DatePicker";
import DayPicker from "../DayPicker";
import Dropdown from "../Dropdown";
import DropdownWithError from "../DropdownWithError";
import EditableField from "../EditableField";
//import FileUpload from "../FileUpload";
import Flag from "../Flag";
//import FolderPicker from "../FolderPicker";
// GridCell
import InputGroup from "../InputGroup";
import Label from "../Label";
import Modal from "../Modal";
import MultiLineInputWithError from "../MultiLineInputWithError";
import NumberSlider from "../NumberSlider";

import SingleLineInputWithError from "../SingleLineInputWithError";

import { ArrowDownIcon, TableEmptyState } from "../SvgIcons";

storiesOf("BackToLink", module)
    .add("with text", () => <BackToLink onClick={action("clicked")} text="Hello BackToLink"></BackToLink>);

storiesOf("Button", module)
    .add("with text", () => <Button onClick={action("clicked")}>Hello Button</Button>);

storiesOf("Checkbox", module)
    .add("with text", () => <Checkbox value={false} onChange={action("changed")} label="Hello Checkbox"></Checkbox>);

storiesOf("Collapsible", module)
    .add("with text", () => <Collapsible isOpened={true} autoScroll={true} scrollDelay={10}>

Add Term

            <InputGroup>
                <SingleLineInputWithError
                    inputId={"create-term-name"}
                    withLabel={true}
                    label="Required Term *"
                    value="Term Value"
                    onChange={action("changed")}
                    errorMessage="Error"
                />
            </InputGroup>
            <InputGroup>
                <MultiLineInputWithError
                    inputId={"create-term-description"}
                    withLabel={true}
                    label="Description"
                    value="Long descritpion here"
                    onChange={action("changed")}/>
            </InputGroup>

    </Collapsible>);

storiesOf("CollapsibleRow", module)
    .add("with text", () => <CollapsibleRow label={
Click Header To Expand
}
        keepCollapsedContent={true}
        closeOnBlur={false}
        secondaryButtonText="Close"
        buttonsAreHidden={false}
        collapsed={true}
        onChange={action("changed")}>
Test Content
</CollapsibleRow>);

storiesOf("ContentLoadWrapper", module)
    .add("with loading", () =>
        <ContentLoadWrapper loadComplete={false}
            svgSkeleton={
}>

                    Loading...

Item 1

Item 2

Item 3

        </ContentLoadWrapper>
    );

storiesOf("ContentLoadWrapper", module)
    .add("with content", () =>
        <ContentLoadWrapper loadComplete={true}
            svgSkeleton={
}>

Content

Item 1

Item 2

Item 3

        </ContentLoadWrapper>
    );

let startDate = new Date("December 17, 2018 03:24:00");
storiesOf("DatePicker", module)
    .add("with content", () =>

            <DatePicker
                date={startDate}
                updateDate={(date) => action("changed " + date.toString()) }
                isDateRange={false}
                hasTimePicker={true}
                showClearDateButton={false} />

);

storiesOf("DayPicker", module)
    .add("with content", () => <DayPicker />);

storiesOf("Dropdown", module)
    .add("with content", () => <Dropdown label="Test" options={[{ label: "Opt 1", value: 1 }, { label: "Opt 2", value: 2 }, { label: "Opt 3", value: 3 }]} selectedIndex={1}></Dropdown>);

storiesOf("DropdownWithError", module)
    .add("with content", () => <DropdownWithError error={true} errorMessage="Please select an item" label="Test" options={[{ label: "Opt 1", value: 1 }, { label: "Opt 2", value: 2 }, { label: "Opt 3", value: 3 }]}></DropdownWithError>);

storiesOf("EditableField", module)
    .add("with content", () => <EditableField label="Test" value="Content" onFocus={action("focus")} onEnter={action("enter")} />);

// TODO: Need FileUpload example here. It is complicated and most of the dependent required utils live in the Dnn.Admin.Experience project.

storiesOf("Flag", module)
    .add("with content", () => <Flag title="Test" culture="en-US" onClick={action("Clicked")} />);

// TODO: Need FolderPicker example here. Not sure what to put in for the serviceFramework prop

storiesOf("InputGroup", module)
    .add("with content", () =>
        <InputGroup>
            <SingleLineInputWithError
                inputId={"create-term-name"}
                withLabel={true}
                label="Required Term *"
                value="Term Value"
                onChange={action("changed")}
                errorMessage="Error"
            />
        </InputGroup>
    );

storiesOf("Label", module)
    .add("with content", () => );

storiesOf("Modal", module)
    .add("with content", () => <Modal isOpen={true} header="Test Modal">
Hello!
</Modal>);

storiesOf("MultiLineInputWithError", module)
    .add("with content", () => <MultiLineInputWithError
        inputId={"create-term-description"}
        withLabel={true}
        label="Description"
        value="Long descritpion here"
        onChange={action("changed")}/>);

storiesOf("NumberSlider", module)
    .add("with content", () => <NumberSlider min={1} max={100} step={5} value={50} onChange={action("Change")} />);

// TODO: Need Pager example
// TODO: Need PermissionGrid example
// TODO: Need PersonaBarPage example
// TODO: Need PersonaBarPageBody example
// TODO: Need PersonaBarPageHeader example
// TODO: Need PortableTransitionModal example
// TODO: Need RadioButtons example
// TODO: Need ScrollBar example
// TODO: Need SearchableTags example
// TODO: Need SearchBox example
// TODO: Need Select example

storiesOf("SingleLineInputWithError", module)
    .add("with content", () =>     <SingleLineInputWithError
        inputId={"create-term-name"}
        withLabel={true}
        label="Required Term *"
        value="Term Value"
        onChange={action("changed")}
        errorMessage="Error"
    />
    );

// TODO: Need Sortable example
// TODO: Need Switch example
// TODO: Need Tags example
// TODO: Need TextOverflowWrapperNew example
// TODO: Need Tooltip example
// TODO: Need TransitionModal example
// TODO: Need TreeControlInteractor example
<|endoftext|>


<commit_before>var fs = require('fs');
var zlib = require('zlib');
var mime = require('mime');
var minifyHtml = require('html-minifier').minify;
var minifyCSS =  require('clean-css');
var UglifyJS = require('uglify-js');
var Fingerprint = require('./fingerprint');

// get event emitter module
var EventEmitter = require('events').EventEmitter;

var html_minifier_options = {
    removeComments: true,
    removeCommentsFromCDATA: true,
    removeCDATASectionsFromCDATA: true,
    removeAttributeQuotes: true,
    removeRedundantAttributes: true,
    removeEmptyAttributes: true,
    removeOptionalTags: true,
    collapseBooleanAttributes: true,
    collapseWhitespace: true
};

// remove fingerprint from script files
var reRemoveFingerprint = /\.[^\.]+\.js$/;

// save caches
var caches = {};

// this is not quaranteed to be stable! why?
zlib.Z_DEFAULT_COMPRESSION = 9;

// export the file cache factory
module.exports = factory;

/**
 * The file cache factory.
 *
 * @public
 * @param {string} The unique file cache name.
 * @param {object} Options for the file cache.
 */
function factory (name, options) {

    // return existing cache
    if (caches[name]) {
        return caches[name];
    }

    // clone cache class
    var cache = engine.clone(Cache);

    // set observer
    cache.obs = new EventEmitter();

    // create data container
    cache._data = {};

    // working dir
    cache._wd = options.wd || engine.paths.app_public;

    // dont wrap js code
    cache._noWrap = options.noWrap;

    // configure compression
    cache._noCompression = options.noCompression;

    // dont optimize html and css
    cache._dontOptimize = options.dontOptimize;

    // save cache
    caches[name] = cache;

    return cache;
}

/**
 * The file cache class.
 *
 * @class Cache
 */
var Cache = {

    /**
     * Remove a file from the cache. Or with no argument empty the cache.
     *
     * @public
     * @param {string} The file path.
     */
    rm: function (path) {

        // remove one item
        if (path) {

            delete this._data[path];

            // emit item remove event
            this.obs.emit('remove:' + path);

            return;
        }

        // remove all items
        this._data = {};
    },

    /**
     * Get a file, or get all files in the cache.
     *
     * @public
     * @param {string} The file path.
     */
    get: function (path) {

        // return one item
        if (path) {
            return this._data[path];
        }

        // return all items
        return this._data;
    },

    /**
     * Save an file in the cache.
     *
     * @public
     * @param {string} The file path.
     * @param {function} The callback function.
     * @param {array} Give an array with allowed paths. (optional)
     * @param {string} A string, which is prepended to a path.
     */
    set: function (path, callback, allowedFiles, prependPath) {
        var self = this;

        // clean path and get figerprint info
        var pathInfo = Fingerprint.cleanPath(path);

        // create a clean path without the fingerprint
        var cleanPath = pathInfo.path;

        // check if the file is allowed
        if (allowedFiles) {

            var found = false;

            // try to find the file
            for (var i = 0; i < allowedFiles.length; ++i) {
                if (allowedFiles[i].replace(/^\.\//, '') === path) {
                    found = true;
                    break;
                }
            }

            // return not found if requested file is not found in "allowedFiles"
            if (!found) {
                var err = new Error('File not found.');
                console.error('lib/file.js#158', err.toString());
                return callback(err);
            }
        }

        // path must be absolute
        if (cleanPath.indexOf('../') !== -1) {
            var err = new Error('Invalid path.');
            console.error('lib/file.js#166', err.toString());
            return callback(err);
        }

        // check if file is already in cache
        if (self._data[cleanPath]) {
            return callback(null, self._data[cleanPath], true);
        }

        var mimeType = mime.lookup(cleanPath);

        // TODO this looks a little whacky.. find a better way to distinct core client and module client files
        var readPath = self._wd + (allowedFiles ? (prependPath || '') : '') + (cleanPath[0] === '/' ? cleanPath.substr(1) : cleanPath);

        fs.readFile(readPath, function (err, data) {

            if (err) {
                console.error('lib/file.js#183', err.toString());
                return callback(err);
            }

            // minify js, html or css
            if (!self._dontOptimize) {
                switch (mimeType) {
                    case 'text/html':
                        //data = minifyHtml(data.toString(), html_minifier_options);
                        break;
                    case 'text/css':
                        data = new minifyCSS({processImport: false}).minify(data.toString()).styles;
                        break;
                    case 'application/javascript':

                        // wrap javascript files, except the engine client
                        if (cleanPath !== 'engine.js' && !self._noWrap) {
                            data = "E('" + (prependPath || '') + cleanPath + "',function(require,module,exports){\n" + data.toString() + "\nreturn module});";
                        }

                        if (engine.production && (data = UglifyJS.minify(data.toString(), {fromString: true}))) {
                            data = data.code;
                        }
                        break;
                }
            }

            // don't compress data
            if (self._noCompression) {
                return createCacheObject.call(self, cleanPath, readPath, data, mimeType, pathInfo.fingerprint, callback);
            }

            zlib.gzip(data, function (err, data) {

                if (err) {
                    console.error('lib/file.js#218', err.toString());
                    return callback(err);
                }

                createCacheObject.call(self, cleanPath, readPath, data, mimeType, pathInfo.fingerprint, callback);
            });
        });
    }
};

/**
 * Create an cache object with the data buffer and the http headers.
 *
 * @private
 * @param {string} The file name without the fingerprint.
 * @param {string} The file path wihtout the fingerprint.
 * @param {blob} The file data.
 * @param {string} The mime type.
 * @param {string} The fingerprint.
 * @param {function} The callback function.
 */
function createCacheObject (cleanPath, readPath, data, mimeType, fingerprint, callback) {
    var self = this;

    fs.stat(readPath, function (err, stats) {

        if (err) {
            console.error('lib/file.js#245', err.toString());
            return callback(err);
        }

        // watch for file changes
        if (self._data[cleanPath] !== null) {
            fs.watch(readPath, function (event, filename) {

                // remove cached data on file change, to force reload
                if (event === 'change' && self._data[cleanPath]) {
                    self._data[cleanPath] = null;

                    // emit item change event
                    self.obs.emit('change:' + cleanPath);
                }
            });
        }

        data = {
            data: data,

            // create http headers
            // TODO send not modified for non scrips and css
            http: {
                'Cache-Control': 'public, max-age=' + (fingerprint ? engine.http.maxAge_fingerprint : engine.http.maxAge),
                'Vary': 'Accept-Encoding',
                'Last-Modified': stats.mtime,
                'Content-Encoding': 'gzip',
                'Content-Length': data.length,
                'Content-Type': mimeType
            }
        };

        // append fingerprint also as etag
        if (fingerprint) {
            data.http.Etag = fingerprint;
        }

        // know if item was removed due a composition change or manual
        var changed = self._data[cleanPath] === null ? true : false;

        // save zipped data in cache
        self._data[cleanPath] = data;

        // return data
        callback(null, data, changed);
    });
}
<commit_msg>generate detail caching headers only for files without fingerprints<commit_after>var fs = require('fs');
var zlib = require('zlib');
var mime = require('mime');
var minifyHtml = require('html-minifier').minify;
var minifyCSS =  require('clean-css');
var UglifyJS = require('uglify-js');
var Fingerprint = require('./fingerprint');

// get event emitter module
var EventEmitter = require('events').EventEmitter;

var html_minifier_options = {
    removeComments: true,
    removeCommentsFromCDATA: true,
    removeCDATASectionsFromCDATA: true,
    removeAttributeQuotes: true,
    removeRedundantAttributes: true,
    removeEmptyAttributes: true,
    removeOptionalTags: true,
    collapseBooleanAttributes: true,
    collapseWhitespace: true
};

// remove fingerprint from script files
var reRemoveFingerprint = /\.[^\.]+\.js$/;

// save caches
var caches = {};

// this is not quaranteed to be stable! why?
zlib.Z_DEFAULT_COMPRESSION = 9;

// export the file cache factory
module.exports = factory;

/**
 * The file cache factory.
 *
 * @public
 * @param {string} The unique file cache name.
 * @param {object} Options for the file cache.
 */
function factory (name, options) {

    // return existing cache
    if (caches[name]) {
        return caches[name];
    }

    // clone cache class
    var cache = engine.clone(Cache);

    // set observer
    cache.obs = new EventEmitter();

    // create data container
    cache._data = {};

    // working dir
    cache._wd = options.wd || engine.paths.app_public;

    // dont wrap js code
    cache._noWrap = options.noWrap;

    // configure compression
    cache._noCompression = options.noCompression;

    // dont optimize html and css
    cache._dontOptimize = options.dontOptimize;

    // save cache
    caches[name] = cache;

    return cache;
}

/**
 * The file cache class.
 *
 * @class Cache
 */
var Cache = {

    /**
     * Remove a file from the cache. Or with no argument empty the cache.
     *
     * @public
     * @param {string} The file path.
     */
    rm: function (path) {

        // remove one item
        if (path) {

            delete this._data[path];

            // emit item remove event
            this.obs.emit('remove:' + path);

            return;
        }

        // remove all items
        this._data = {};
    },

    /**
     * Get a file, or get all files in the cache.
     *
     * @public
     * @param {string} The file path.
     */
    get: function (path) {

        // return one item
        if (path) {
            return this._data[path];
        }

        // return all items
        return this._data;
    },

    /**
     * Save an file in the cache.
     *
     * @public
     * @param {string} The file path.
     * @param {function} The callback function.
     * @param {array} Give an array with allowed paths. (optional)
     * @param {string} A string, which is prepended to a path.
     */
    set: function (path, callback, allowedFiles, prependPath) {
        var self = this;

        // clean path and get figerprint info
        var pathInfo = Fingerprint.cleanPath(path);

        // create a clean path without the fingerprint
        var cleanPath = pathInfo.path;

        // check if the file is allowed
        if (allowedFiles) {

            var found = false;

            // try to find the file
            for (var i = 0; i < allowedFiles.length; ++i) {
                if (allowedFiles[i].replace(/^\.\//, '') === path) {
                    found = true;
                    break;
                }
            }

            // return not found if requested file is not found in "allowedFiles"
            if (!found) {
                var err = new Error('File not found.');
                console.error('lib/file.js#158', err.toString());
                return callback(err);
            }
        }

        // path must be absolute
        if (cleanPath.indexOf('../') !== -1) {
            var err = new Error('Invalid path.');
            console.error('lib/file.js#166', err.toString());
            return callback(err);
        }

        // check if file is already in cache
        if (self._data[cleanPath]) {
            return callback(null, self._data[cleanPath], true);
        }

        var mimeType = mime.lookup(cleanPath);

        // TODO this looks a little whacky.. find a better way to distinct core client and module client files
        var readPath = self._wd + (allowedFiles ? (prependPath || '') : '') + (cleanPath[0] === '/' ? cleanPath.substr(1) : cleanPath);

        fs.readFile(readPath, function (err, data) {

            if (err) {
                console.error('lib/file.js#183', err.toString());
                return callback(err);
            }

            // minify js, html or css
            if (!self._dontOptimize) {
                switch (mimeType) {
                    case 'text/html':
                        //data = minifyHtml(data.toString(), html_minifier_options);
                        break;
                    case 'text/css':
                        data = new minifyCSS({processImport: false}).minify(data.toString()).styles;
                        break;
                    case 'application/javascript':

                        // wrap javascript files, except the engine client
                        if (cleanPath !== 'engine.js' && !self._noWrap) {
                            data = "E('" + (prependPath || '') + cleanPath + "',function(require,module,exports){\n" + data.toString() + "\nreturn module});";
                        }

                        if (engine.production && (data = UglifyJS.minify(data.toString(), {fromString: true}))) {
                            data = data.code;
                        }
                        break;
                }
            }

            // don't compress data
            if (self._noCompression) {
                return createCacheObject.call(self, cleanPath, readPath, data, mimeType, pathInfo.fingerprint, callback);
            }

            zlib.gzip(data, function (err, data) {

                if (err) {
                    console.error('lib/file.js#218', err.toString());
                    return callback(err);
                }

                createCacheObject.call(self, cleanPath, readPath, data, mimeType, pathInfo.fingerprint, callback);
            });
        });
    }
};

/**
 * Create an cache object with the data buffer and the http headers.
 *
 * @private
 * @param {string} The file name without the fingerprint.
 * @param {string} The file path wihtout the fingerprint.
 * @param {blob} The file data.
 * @param {string} The mime type.
 * @param {string} The fingerprint.
 * @param {function} The callback function.
 */
function createCacheObject (cleanPath, readPath, data, mimeType, fingerprint, callback) {
    var self = this;

    fs.stat(readPath, function (err, stats) {

        if (err) {
            console.error('lib/file.js#245', err.toString());
            return callback(err);
        }

        // watch for file changes
        if (self._data[cleanPath] !== null) {
            fs.watch(readPath, function (event, filename) {

                // remove cached data on file change, to force reload
                if (event === 'change' && self._data[cleanPath]) {
                    self._data[cleanPath] = null;

                    // emit item change event
                    self.obs.emit('change:' + cleanPath);
                }
            });
        }

        data = {
            data: data,

            // create http headers
            // TODO send not modified for non scrips and css
            http: {
                'Vary': 'Accept-Encoding',
                'Cache-Control': 'public, max-age=' + (fingerprint ? engine.http.maxAge_fingerprint : engine.http.maxAge),
                'Content-Encoding': 'gzip',
                'Content-Length': data.length,
                'Content-Type': mimeType
            }
        };

        // handle files without fingerprint
        if (!fingerprint) {
            data.http.Etag = stats.mtime.getTime().toString(36).slice(-7);
            data.http['Last-Modified'] = stats.mtime;
        }

        // know if item was removed due a composition change or manual
        var changed = self._data[cleanPath] === null ? true : false;

        // save zipped data in cache
        self._data[cleanPath] = data;

        // return data
        callback(null, data, changed);
    });
}
<|endoftext|>