<commit_before>'use babel';

import FlatpakBuilderRunnerView from './flatpak-builder-runner-view';
import { CompositeDisposable } from 'atom';
import execSeries from 'exec-series';
import Utils from './Utils.js';

export default {

  config: {
    "repoName": {
      "description": "The default repository name.",
      "type": "string",
      "default": "local"
    },

    "repoPath": {
      "description": "The default repository to use when building. It will be relative to the user's home directory.",
      "type": "string",
      "default": "local-flatpak-repo"
    },

    "buildDir": {
      "description": "The default build directory parent to use when building.",
      "type": "string",
      "default": "build_dirs"
    }
  },

  flatpakBuilderRunnerView: null,
  modalPanel: null,
  subscriptions: null,

  activate(state) {
    this.flatpakBuilderRunnerView = new FlatpakBuilderRunnerView(state.flatpakBuilderRunnerViewState);
    this.modalPanel = atom.workspace.addModalPanel({
      item: this.flatpakBuilderRunnerView.getElement(),
      visible: false
    });

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'flatpak-builder-runner:addrepo': () => this.addrepo()
    }));

    this.subscriptions.add(atom.commands.add('.tree-view .file', {
      'flatpak-builder-runner:build': (target) => this.build(target),
      'flatpak-builder-runner:install': (target) => this.install(target),
      'flatpak-builder-runner:run': (target) => this.run(target),
      'flatpak-builder-runner:uninstall': (target) => this.uninstall(target)
    }));
  },

  deactivate() {
    this.modalPanel.destroy();
    this.subscriptions.dispose();
    this.flatpakBuilderRunnerView.destroy();
  },

  serialize() {
    return {
      flatpakBuilderRunnerViewState: this.flatpakBuilderRunnerView.serialize()
    };
  },

  build(eventTarget) {
    let target = eventTarget.currentTarget;
    console.log(target.getPath());
    var filename = target.getPath().split('\\').pop().split('/').pop();
    let buildDir = atom.config.get('flatpak-builder-runner.buildDir');
    let repoPath = atom.config.get('flatpak-builder-runner.repoPath');
    let command = 'flatpak-builder --force-clean --repo=' + process.env.HOME + '/' + repoPath + ' ' + buildDir + '/' + filename + ' ' + target.getPath();

    Utils.execCommand(command);
  },

  install(eventTarget) {
    let target = eventTarget.currentTarget;
    let repoName = atom.config.get('flatpak-builder-runner.repoName');
    var filename = target.getPath().split('\\').pop().split('/').pop();

    var manifestJSON = Utils.loadJSON(target);

    let command = 'flatpak --user install ' + repoName + ' ' + manifestJSON["app-id"];
    Utils.execCommand(command);
  },

  run(eventTarget) {
    let target = eventTarget.currentTarget;
    let repoName = atom.config.get('flatpak-builder-runner.repoName');
    var filename = target.getPath().split('\\').pop().split('/').pop();

    var manifestJSON = Utils.loadJSON(target);
    let command = 'flatpak run ' + manifestJSON["app-id"];
    execSeries([command]);
    console.log(command);
    atom.notifications.addInfo(command);
  },

  uninstall(eventTarget) {
    let target = eventTarget.currentTarget;
    let repoName = atom.config.get('flatpak-builder-runner.repoName');
    var filename = target.getPath().split('\\').pop().split('/').pop();

    var manifestJSON = Utils.loadJSON(target)
    let command = 'flatpak --user uninstall ' + manifestJSON["app-id"];

    Utils.execCommand(command);
  },

  addrepo() {
    let repoPath = atom.config.get('flatpak-builder-runner.repoPath');
    let repoName = atom.config.get('flatpak-builder-runner.repoName');

    let command = "flatpak --user remote-add --no-gpg-verify " + repoName + " " + process.env.HOME + "/" + repoPath;

    atom.notifications.addInfo(command);

    Utils.execCommand(command);
  }
}
<commit_msg>remove vestiges of generated package code<commit_after>'use babel';

import { CompositeDisposable } from 'atom';
import execSeries from 'exec-series';
import Utils from './Utils.js';

export default {

  config: {
    "repoName": {
      "description": "The default repository name.",
      "type": "string",
      "default": "local"
    },

    "repoPath": {
      "description": "The default repository to use when building. It will be relative to the user's home directory.",
      "type": "string",
      "default": "local-flatpak-repo"
    },

    "buildDir": {
      "description": "The default build directory parent to use when building.",
      "type": "string",
      "default": "build_dirs"
    }
  },

  subscriptions: null,

  activate(state) {

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'flatpak-builder-runner:addrepo': () => this.addrepo()
    }));

    this.subscriptions.add(atom.commands.add('.tree-view .file', {
      'flatpak-builder-runner:build': (target) => this.build(target),
      'flatpak-builder-runner:install': (target) => this.install(target),
      'flatpak-builder-runner:run': (target) => this.run(target),
      'flatpak-builder-runner:uninstall': (target) => this.uninstall(target)
    }));
  },

  deactivate() {
    this.subscriptions.dispose();
  },

  serialize() {
    return {};
  },

  build(eventTarget) {
    let target = eventTarget.currentTarget;
    console.log(target.getPath());
    var filename = target.getPath().split('\\').pop().split('/').pop();
    let buildDir = atom.config.get('flatpak-builder-runner.buildDir');
    let repoPath = atom.config.get('flatpak-builder-runner.repoPath');
    let command = 'flatpak-builder --force-clean --repo=' + process.env.HOME + '/' + repoPath + ' ' + buildDir + '/' + filename + ' ' + target.getPath();

    Utils.execCommand(command);
  },

  install(eventTarget) {
    let target = eventTarget.currentTarget;
    let repoName = atom.config.get('flatpak-builder-runner.repoName');
    var filename = target.getPath().split('\\').pop().split('/').pop();

    var manifestJSON = Utils.loadJSON(target);

    let command = 'flatpak --user install ' + repoName + ' ' + manifestJSON["app-id"];
    Utils.execCommand(command);
  },

  run(eventTarget) {
    let target = eventTarget.currentTarget;
    let repoName = atom.config.get('flatpak-builder-runner.repoName');
    var filename = target.getPath().split('\\').pop().split('/').pop();

    var manifestJSON = Utils.loadJSON(target);
    let command = 'flatpak run ' + manifestJSON["app-id"];
    execSeries([command]);
    console.log(command);
    atom.notifications.addInfo(command);
  },

  uninstall(eventTarget) {
    let target = eventTarget.currentTarget;
    let repoName = atom.config.get('flatpak-builder-runner.repoName');
    var filename = target.getPath().split('\\').pop().split('/').pop();

    var manifestJSON = Utils.loadJSON(target)
    let command = 'flatpak --user uninstall ' + manifestJSON["app-id"];

    Utils.execCommand(command);
  },

  addrepo() {
    let repoPath = atom.config.get('flatpak-builder-runner.repoPath');
    let repoName = atom.config.get('flatpak-builder-runner.repoName');

    let command = "flatpak --user remote-add --no-gpg-verify " + repoName + " " + process.env.HOME + "/" + repoPath;

    atom.notifications.addInfo(command);

    Utils.execCommand(command);
  }
}
<|endoftext|>


<commit_before>/* global BugLinks, DateFilters, socorro */

$(function() {
  'use strict';

  var searchContainer = $('#search-form');
  var form = $('form', searchContainer);
  var resultsURL = form.data('results-url');
  var simpleSearchContainer = $('#simple-search');

  var contentElt = $('#search_results');
  var sortInput = $('input[name=_sort]', form);
  var facetsInput = $('input[name=_facets]', form);
  var columnsInput = $('input[name=_columns_fake]', form);

  /**
   * Run a search.
   *
   * Update the public API URL, push a new history state, query the search
   * results and show them.
   */
  function search(noHistory, page) {
    if (!page) {
      page = 1;
    }

    var params = getParams();
    updatePublicApiUrl(params);

    // Set the page after updating the public API URL, because we don't want
    // that page parameter in the URL. It is only useful in the UI.
    params.page = page;

    var url = prepareResultsQueryString(params);
    queryResults(resultsURL + url);

    if (!noHistory) {
      pushHistoryState(params, url);
    }
  }

  /**
   * Display results from a search query.
   */
  function showResults(data) {
    contentElt.empty().append(data);

    if ($('.no-data', contentElt).length) {
      // There are no results, no need to do more.
      return;
    }

    // Determine which tab should be open. If there is a hash,
    // we let jQuery UI's tabs open the correct one. Otherwise,
    // we want to open the last one of the list.
    var activeTab = null;
    if (!location.hash) {
      activeTab = -1;
    }

    var params = getParams();
    var url = prepareResultsQueryString(params);

    contentElt.tabs({
      active: activeTab,
      activate: function(event, ui) {
        // Make sure the hash is changed when switching tabs.
        var hash = '#' + ui.newPanel.attr('id');
        pushHistoryState(params, url, hash);
      },
      create: function(event, ui) {
        // Put the first tab's id in the hash of the URL.
        var hash = '#' + ui.panel.attr('id');
        pushHistoryState(params, url, hash, true);
      },
    });

    // Handle server-side sorting.
    $('.tablesorter.facet').tablesorter();
    $('#reports-list').tablesorter({
      headers: {
        0: {
          // disable the first column, `Crash ID`
          sorter: false,
        },
      },
    });

    // Make sure there are more than 1 page of results. If not,
    // do not activate server-side sorting, rely on the
    // default client-side sorting.
    if ($('.pagination a', contentElt).length) {
      $('.sort-header', contentElt).click(function(e) {
        e.preventDefault();

        var thisElt = $(this);

        // Update the sort field.
        var fieldName = thisElt.data('field-name');
        var sortArr = sortInput.select2('val');

        // First remove all previous mentions of that field.
        sortArr = sortArr.filter(function(item) {
          return item !== fieldName && item !== '-' + fieldName;
        });

        // Now add it in the order that follows this sequence:
        // ascending -> descending -> none
        if (thisElt.hasClass('headerSortDown')) {
          sortArr.unshift('-' + fieldName);
        } else if (!thisElt.hasClass('headerSortDown') && !thisElt.hasClass('headerSortUp')) {
          sortArr.unshift(fieldName);
        }

        sortInput.select2('val', sortArr);
        search();
      });
    }

    // Enhance bug links.
    BugLinks.enhance();
  }

  /**
   * Query the search results and show them.
   */
  function queryResults(url) {
    // Show loader.
    try {
      contentElt.tabs('destroy');
    } catch (e) {
      // It is possible that no tabs existed before, and that is fine.
    }
    contentElt.empty().append($('<div>', { class: 'loader' }));

    // If a tracker is available, track that AJAX call.
    Analytics.trackPageview(url);

    $.ajax({
      url: url,
      success: showResults,
      error: function(jqXHR) {
        var errorContent = $('<div>', { class: 'error' });

        if (jqXHR.status >= 400 && jqXHR.status < 500) {
          errorContent
            .append($('<h3>', { text: 'Oops, an error occured' }))
            .append($('<p>', { text: 'Please fix the following issues:' }))
            .append($(jqXHR.responseText));
        } else {
          // We have no interest data to display as a constructive
          // error message to the user. So we'll have to show a
          // generic error message.
          errorContent.append($('<h3>', { text: 'An unexpected error occured :(' })).append(
            $('<p>', {
              text: 'We have been automatically informed ' + 'of that error, and are working on a ' + 'solution. ',
            })
          );
        }

        contentElt.empty().append(errorContent);
      },
      dataType: 'HTML',
    });
  }

  /**
   * Return a query string made from parameters, with the addition of search
   * options (aggregations, columns, sort).
   */
  function prepareResultsQueryString(params) {
    var sortArr = sortInput.select2('data');
    params._sort = sortArr.map(function(x) {
      return x.id;
    });

    var facets = facetsInput.select2('data');
    if (facets) {
      params._facets = facets.map(function(x) {
        return x.id;
      });
    }

    var columns = columnsInput.select2('data');
    if (columns) {
      params._columns = columns.map(function(x) {
        return x.id;
      });
    }

    var queryString = Qs.stringify(params, { indices: false });
    return '?' + queryString;
  }

  /**
   * Update the public API URL field in the form options.
   */
  function updatePublicApiUrl(params) {
    // Update the public API URL.
    var queryString = Qs.stringify(params, { indices: false });
    queryString = queryString.replace(/!/g, '%21');
    var BASE_URL = location.protocol + '//' + location.host;
    $('input[name=_public_api_url]', form).val(BASE_URL + form.data('public-api-url') + '?' + queryString);
  }

  /**
   * Push a new browser history state.
   */
  function pushHistoryState(params, url, hash, replace) {
    var func = (replace && window.history.replaceState) || window.history.pushState;
    if (!hash) {
      hash = location.hash;
    }
    func.call(window.history, params, 'Search results', url + hash);
  }

  /**
   * Return the current parameters defined by the search form.
   */
  function getParams() {
    var params = form.dynamicForm('getParams');

    // Add Simple Search parameters.
    $('input.simple-search-input', simpleSearchContainer).each(function(i, item) {
      var name = item.name;
      var value = $(item).select2('val');
      if (value.length) {
        if (params[name]) {
          params[name] = params[name].concat(value);
        } else {
          params[name] = value;
        }
      }
    });

    // Add dates from the date filters.
    params.date = DateFilters.getDates();

    return params;
  }

  /**
   * Returns true if a value starts with a known operator.
   */
  function hasOperator(value) {
    var operators = form.dynamicForm('getOperatorsList');
    return operators.some(function(operator) {
      if (operator === 'has') {
        return false; // we don't care about that one
      }
      return value.indexOf(operator) === 0;
    });
  }

  /**
   * Update the search form with new parameters.
   */
  function setParams(params) {
    // Set Simple Search parameters.
    $('input', simpleSearchContainer).each(function(i, item) {
      if (item.name in params) {
        var values = params[item.name];
        if (!Array.isArray(values)) {
          values = [values];
        }
        // Sort values, simple ones (with no operator at the start)
        // will go into the simple search select tags, while others
        // will stay in `params` and end up in the advanced form.
        var simpleValues = [];
        params[item.name] = values.map(function(value) {
          if (!hasOperator(value)) {
            simpleValues.push(value);
          } else {
            return value;
          }
        });
        $(item).select2('val', simpleValues);
      }
    });

    // Set date filters values.
    if (params.date) {
      DateFilters.setDates(params.date);
      delete params.date;
    }

    form.dynamicForm('setParams', params);
  }

  /**
   * Add a new line to the search form with specified field and value.
   * The operator will be 'is exactly' or the default operator for the field.
   */
  function addTerm(name, value) {
    form.dynamicForm('newLine', {
      field: name,
      operator: '=', // will fall back to the default operator if 'is exactly' is not implemented for that field
      value: value,
    });
  }

  /**
   * Handler for browser history state changes.
   */
  window.onpopstate = function(e) {
    var queryString;
    var params = e.state;

    if (params === null) {
      // No state was added, check if the URL contains parameters
      queryString = window.location.search.substring(1);
      params = socorro.search.parseQueryString(queryString);
    }

    if (params) {
      // If there are some parameters, fill the form with those params
      // and run the search and show results
      setParams(params);
      search(true);
    } else {
      // If there are no parameters, empty the form and show the default content
      form.dynamicForm('setParams', {});
      form.dynamicForm('newLine');
      $('select', simpleSearchContainer).select2('val', null);
      contentElt.empty().append($('<p>', { text: 'Run a search to get some results. ' }));
    }
  };

  /**
   * Show the search form and initialize parts that were hidden. This
   * function is called once the dynamicForm library is done loading the
   * advanced search form.
   */
  function showForm() {
    $('.loader', searchContainer).remove();

    form.show();

    initSimpleSearch();
    initFormButtons();
    initFormOptions();
  }

  /**
   * Initialize the search form, populating it with whatever parameters
   * are passed in the query string.
   */
  function initForm() {
    // Create the advanced search form.
    var queryString = window.location.search.substring(1);
    var initialParams = socorro.search.parseQueryString(queryString);

    var formCallback = function() {
      showForm();

      // By default, we simply create a new line in the form.
      form.dynamicForm('newLine');
    };

    // If there are initial params, that means we should run the
    // corresponding search right after the form has finished loading.
    if (initialParams) {
      var page = 1;
      if (initialParams.page) {
        page = initialParams.page;
      }
      var dontRun = initialParams._dont_run === '1';

      // Sanitize parameters.
      initialParams = socorro.search.getFilteredParams(initialParams);

      // When the form has finished loading, we get pass it our
      // initial parameters for it to sanitize them. Then we run
      // a regular search, which will use the sane parameters from
      // the dynamicForm library. This will avoid strange behaviors
      // that can be caused by manually set parameters, for example.
      formCallback = function() {
        showForm();

        setParams(initialParams);
        if (!dontRun) {
          search(false, page);
        }
      };
    }

    form.dynamicForm(
      form.data('fields-url'),
      null,
      '#search-params-fieldset',
      formCallback,
      socorro.search.sortResults
    );
  }

  /**
   * Initialize the simplified search form.
   */
  function initSimpleSearch() {
    $('input[type=text]', simpleSearchContainer).each(function() {
      var elt = $(this);
      elt.select2({
        width: 'element',
        tags: elt.data('choices'),
      });
    });
  }

  /**
   * Bind the search buttons' events.
   */
  function initFormButtons() {
    $('button[type=submit]', form).click(function(e) {
      e.preventDefault();
      search();
    });

    $('.new-line', form).click(function(e) {
      e.preventDefault();
      form.dynamicForm('newLine');
    });

    $('.customize', form).click(function(e) {
      e.preventDefault();
      var params = getParams();
      var url = prepareResultsQueryString(params);

      window.location = form.data('custom-url') + url;
    });
  }

  /**
   * Initialize the form options fields, handling aggregations, sorting and more.
   */
  function initFormOptions() {
    var COLUMNS = $('#mainbody').data('columns');
    var FACETS = $('#mainbody').data('facets');
    var sortFields = [];
    COLUMNS.forEach(function(item) {
      sortFields.push(item);
      sortFields.push({
        id: '-' + item.id,
        text: item.text + ' (DESC)',
      });
    });

    sortInput.select2({
      data: sortFields,
      multiple: true,
      width: 'element',
      sortResults: socorro.search.sortResults,
    });
    facetsInput.select2({
      data: FACETS,
      multiple: true,
      width: 'element',
      sortResults: socorro.search.sortResults,
    });
    columnsInput.select2({
      data: COLUMNS,
      multiple: true,
      width: 'element',
      sortResults: socorro.search.sortResults,
    });

    // Make the columns input sortable
    columnsInput.on('change', function() {
      $('input[name=_columns]').val(columnsInput.val());
    });

    columnsInput
      .select2('container')
      .find('ul.select2-choices')
      .sortable({
        containment: 'parent',
        start: function() {
          columnsInput.select2('onSortStart');
        },
        update: function() {
          columnsInput.select2('onSortEnd');
        },
      });

    // Show or hide advanced options.
    var optionsElt = $('fieldset.options', form);
    $('h4', optionsElt).on('click', function() {
      $('h4 + div', optionsElt).toggle();
      $('span', this).toggleClass('hide');
    });
    $('h4 + div', optionsElt).hide();
  }

  /**
   * Bind terms of the results to add new form lines.
   */
  function initContentBinding() {
    // Make every value a link that adds a new line to the form.
    contentElt.on('click', '.term', function(e) {
      e.preventDefault();

      addTerm($(this).data('field'), $(this).data('content'));

      // And then run the new, corresponding search.
      search();
    });
  }

  /**
   * Start it all!
   */
  function initialize() {
    initForm();
    initContentBinding();
  }
  initialize();
});
<commit_msg>fix bug 1239020: add _facets to Public API URL<commit_after>/* global BugLinks, DateFilters, socorro */

$(function() {
  'use strict';

  var searchContainer = $('#search-form');
  var form = $('form', searchContainer);
  var resultsURL = form.data('results-url');
  var simpleSearchContainer = $('#simple-search');

  var contentElt = $('#search_results');
  var sortInput = $('input[name=_sort]', form);
  var facetsInput = $('input[name=_facets]', form);
  var columnsInput = $('input[name=_columns_fake]', form);

  /**
   * Run a search.
   *
   * Update the public API URL, push a new history state, query the search
   * results and show them.
   */
  function search(noHistory, page) {
    if (!page) {
      page = 1;
    }

    var params = getParams();
    updatePublicApiUrl(params);

    // Set the page after updating the public API URL, because we don't want
    // that page parameter in the URL. It is only useful in the UI.
    params.page = page;

    var url = prepareResultsQueryString(params);
    queryResults(resultsURL + url);

    if (!noHistory) {
      pushHistoryState(params, url);
    }
  }

  /**
   * Display results from a search query.
   */
  function showResults(data) {
    contentElt.empty().append(data);

    if ($('.no-data', contentElt).length) {
      // There are no results, no need to do more.
      return;
    }

    // Determine which tab should be open. If there is a hash,
    // we let jQuery UI's tabs open the correct one. Otherwise,
    // we want to open the last one of the list.
    var activeTab = null;
    if (!location.hash) {
      activeTab = -1;
    }

    var params = getParams();
    var url = prepareResultsQueryString(params);

    contentElt.tabs({
      active: activeTab,
      activate: function(event, ui) {
        // Make sure the hash is changed when switching tabs.
        var hash = '#' + ui.newPanel.attr('id');
        pushHistoryState(params, url, hash);
      },
      create: function(event, ui) {
        // Put the first tab's id in the hash of the URL.
        var hash = '#' + ui.panel.attr('id');
        pushHistoryState(params, url, hash, true);
      },
    });

    // Handle server-side sorting.
    $('.tablesorter.facet').tablesorter();
    $('#reports-list').tablesorter({
      headers: {
        0: {
          // disable the first column, `Crash ID`
          sorter: false,
        },
      },
    });

    // Make sure there are more than 1 page of results. If not,
    // do not activate server-side sorting, rely on the
    // default client-side sorting.
    if ($('.pagination a', contentElt).length) {
      $('.sort-header', contentElt).click(function(e) {
        e.preventDefault();

        var thisElt = $(this);

        // Update the sort field.
        var fieldName = thisElt.data('field-name');
        var sortArr = sortInput.select2('val');

        // First remove all previous mentions of that field.
        sortArr = sortArr.filter(function(item) {
          return item !== fieldName && item !== '-' + fieldName;
        });

        // Now add it in the order that follows this sequence:
        // ascending -> descending -> none
        if (thisElt.hasClass('headerSortDown')) {
          sortArr.unshift('-' + fieldName);
        } else if (!thisElt.hasClass('headerSortDown') && !thisElt.hasClass('headerSortUp')) {
          sortArr.unshift(fieldName);
        }

        sortInput.select2('val', sortArr);
        search();
      });
    }

    // Enhance bug links.
    BugLinks.enhance();
  }

  /**
   * Query the search results and show them.
   */
  function queryResults(url) {
    // Show loader.
    try {
      contentElt.tabs('destroy');
    } catch (e) {
      // It is possible that no tabs existed before, and that is fine.
    }
    contentElt.empty().append($('<div>', { class: 'loader' }));

    // If a tracker is available, track that AJAX call.
    Analytics.trackPageview(url);

    $.ajax({
      url: url,
      success: showResults,
      error: function(jqXHR) {
        var errorContent = $('<div>', { class: 'error' });

        if (jqXHR.status >= 400 && jqXHR.status < 500) {
          errorContent
            .append($('<h3>', { text: 'Oops, an error occured' }))
            .append($('<p>', { text: 'Please fix the following issues:' }))
            .append($(jqXHR.responseText));
        } else {
          // We have no interest data to display as a constructive
          // error message to the user. So we'll have to show a
          // generic error message.
          errorContent.append($('<h3>', { text: 'An unexpected error occured :(' })).append(
            $('<p>', {
              text: 'We have been automatically informed ' + 'of that error, and are working on a ' + 'solution. ',
            })
          );
        }

        contentElt.empty().append(errorContent);
      },
      dataType: 'HTML',
    });
  }

  /**
   * Return a query string made from parameters, with the addition of search
   * options (aggregations, columns, sort).
   */
  function prepareResultsQueryString(params) {
    var sortArr = sortInput.select2('data');
    params._sort = sortArr.map(function(x) {
      return x.id;
    });

    var facets = facetsInput.select2('data');
    if (facets) {
      params._facets = facets.map(function(x) {
        return x.id;
      });
    }

    var columns = columnsInput.select2('data');
    if (columns) {
      params._columns = columns.map(function(x) {
        return x.id;
      });
    }

    var queryString = Qs.stringify(params, { indices: false });
    return '?' + queryString;
  }

  /**
   * Update the public API URL field in the form options.
   */
  function updatePublicApiUrl(params) {
    // Update the public API URL.
    var queryString = Qs.stringify(params, { indices: false });
    queryString = queryString.replace(/!/g, '%21');
    var BASE_URL = location.protocol + '//' + location.host;
    $('input[name=_public_api_url]', form).val(BASE_URL + form.data('public-api-url') + '?' + queryString);
  }

  /**
   * Push a new browser history state.
   */
  function pushHistoryState(params, url, hash, replace) {
    var func = (replace && window.history.replaceState) || window.history.pushState;
    if (!hash) {
      hash = location.hash;
    }
    func.call(window.history, params, 'Search results', url + hash);
  }

  /**
   * Return the current parameters defined by the search form.
   */
  function getParams() {
    var params = form.dynamicForm('getParams');

    // Add Simple Search parameters.
    $('input.simple-search-input', simpleSearchContainer).each(function(i, item) {
      var name = item.name;
      var value = $(item).select2('val');
      if (value.length) {
        if (params[name]) {
          params[name] = params[name].concat(value);
        } else {
          params[name] = value;
        }
      }
    });

    // Add dates from the date filters.
    params.date = DateFilters.getDates();

    // Add facets
    var facets = facetsInput.select2('data');
    if (facets) {
      params._facets = facets.map(function(x) { return x.id; });
    }

    return params;
  }

  /**
   * Returns true if a value starts with a known operator.
   */
  function hasOperator(value) {
    var operators = form.dynamicForm('getOperatorsList');
    return operators.some(function(operator) {
      if (operator === 'has') {
        return false; // we don't care about that one
      }
      return value.indexOf(operator) === 0;
    });
  }

  /**
   * Update the search form with new parameters.
   */
  function setParams(params) {
    // Set Simple Search parameters.
    $('input', simpleSearchContainer).each(function(i, item) {
      if (item.name in params) {
        var values = params[item.name];
        if (!Array.isArray(values)) {
          values = [values];
        }
        // Sort values, simple ones (with no operator at the start)
        // will go into the simple search select tags, while others
        // will stay in `params` and end up in the advanced form.
        var simpleValues = [];
        params[item.name] = values.map(function(value) {
          if (!hasOperator(value)) {
            simpleValues.push(value);
          } else {
            return value;
          }
        });
        $(item).select2('val', simpleValues);
      }
    });

    // Set date filters values.
    if (params.date) {
      DateFilters.setDates(params.date);
      delete params.date;
    }

    form.dynamicForm('setParams', params);
  }

  /**
   * Add a new line to the search form with specified field and value.
   * The operator will be 'is exactly' or the default operator for the field.
   */
  function addTerm(name, value) {
    form.dynamicForm('newLine', {
      field: name,
      operator: '=', // will fall back to the default operator if 'is exactly' is not implemented for that field
      value: value,
    });
  }

  /**
   * Handler for browser history state changes.
   */
  window.onpopstate = function(e) {
    var queryString;
    var params = e.state;

    if (params === null) {
      // No state was added, check if the URL contains parameters
      queryString = window.location.search.substring(1);
      params = socorro.search.parseQueryString(queryString);
    }

    if (params) {
      // If there are some parameters, fill the form with those params
      // and run the search and show results
      setParams(params);
      search(true);
    } else {
      // If there are no parameters, empty the form and show the default content
      form.dynamicForm('setParams', {});
      form.dynamicForm('newLine');
      $('select', simpleSearchContainer).select2('val', null);
      contentElt.empty().append($('<p>', { text: 'Run a search to get some results. ' }));
    }
  };

  /**
   * Show the search form and initialize parts that were hidden. This
   * function is called once the dynamicForm library is done loading the
   * advanced search form.
   */
  function showForm() {
    $('.loader', searchContainer).remove();

    form.show();

    initSimpleSearch();
    initFormButtons();
    initFormOptions();
  }

  /**
   * Initialize the search form, populating it with whatever parameters
   * are passed in the query string.
   */
  function initForm() {
    // Create the advanced search form.
    var queryString = window.location.search.substring(1);
    var initialParams = socorro.search.parseQueryString(queryString);

    var formCallback = function() {
      showForm();

      // By default, we simply create a new line in the form.
      form.dynamicForm('newLine');
    };

    // If there are initial params, that means we should run the
    // corresponding search right after the form has finished loading.
    if (initialParams) {
      var page = 1;
      if (initialParams.page) {
        page = initialParams.page;
      }
      var dontRun = initialParams._dont_run === '1';

      // Sanitize parameters.
      initialParams = socorro.search.getFilteredParams(initialParams);

      // When the form has finished loading, we get pass it our
      // initial parameters for it to sanitize them. Then we run
      // a regular search, which will use the sane parameters from
      // the dynamicForm library. This will avoid strange behaviors
      // that can be caused by manually set parameters, for example.
      formCallback = function() {
        showForm();

        setParams(initialParams);
        if (!dontRun) {
          search(false, page);
        }
      };
    }

    form.dynamicForm(
      form.data('fields-url'),
      null,
      '#search-params-fieldset',
      formCallback,
      socorro.search.sortResults
    );
  }

  /**
   * Initialize the simplified search form.
   */
  function initSimpleSearch() {
    $('input[type=text]', simpleSearchContainer).each(function() {
      var elt = $(this);
      elt.select2({
        width: 'element',
        tags: elt.data('choices'),
      });
    });
  }

  /**
   * Bind the search buttons' events.
   */
  function initFormButtons() {
    $('button[type=submit]', form).click(function(e) {
      e.preventDefault();
      search();
    });

    $('.new-line', form).click(function(e) {
      e.preventDefault();
      form.dynamicForm('newLine');
    });

    $('.customize', form).click(function(e) {
      e.preventDefault();
      var params = getParams();
      var url = prepareResultsQueryString(params);

      window.location = form.data('custom-url') + url;
    });
  }

  /**
   * Initialize the form options fields, handling aggregations, sorting and more.
   */
  function initFormOptions() {
    var COLUMNS = $('#mainbody').data('columns');
    var FACETS = $('#mainbody').data('facets');
    var sortFields = [];
    COLUMNS.forEach(function(item) {
      sortFields.push(item);
      sortFields.push({
        id: '-' + item.id,
        text: item.text + ' (DESC)',
      });
    });

    sortInput.select2({
      data: sortFields,
      multiple: true,
      width: 'element',
      sortResults: socorro.search.sortResults,
    });
    facetsInput.select2({
      data: FACETS,
      multiple: true,
      width: 'element',
      sortResults: socorro.search.sortResults,
    });
    columnsInput.select2({
      data: COLUMNS,
      multiple: true,
      width: 'element',
      sortResults: socorro.search.sortResults,
    });

    // Make the columns input sortable
    columnsInput.on('change', function() {
      $('input[name=_columns]').val(columnsInput.val());
    });

    columnsInput
      .select2('container')
      .find('ul.select2-choices')
      .sortable({
        containment: 'parent',
        start: function() {
          columnsInput.select2('onSortStart');
        },
        update: function() {
          columnsInput.select2('onSortEnd');
        },
      });

    // Show or hide advanced options.
    var optionsElt = $('fieldset.options', form);
    $('h4', optionsElt).on('click', function() {
      $('h4 + div', optionsElt).toggle();
      $('span', this).toggleClass('hide');
    });
    $('h4 + div', optionsElt).hide();
  }

  /**
   * Bind terms of the results to add new form lines.
   */
  function initContentBinding() {
    // Make every value a link that adds a new line to the form.
    contentElt.on('click', '.term', function(e) {
      e.preventDefault();

      addTerm($(this).data('field'), $(this).data('content'));

      // And then run the new, corresponding search.
      search();
    });
  }

  /**
   * Start it all!
   */
  function initialize() {
    initForm();
    initContentBinding();
  }
  initialize();
});
<|endoftext|>


<commit_before>var async = require('async');

module.exports = function (swig, widgets, randomString) {

	swig.setDefaults({ cache: false });

	var factories = {},
		renderWidgets = [],
		templateCache = {};

	var renderTemplate = function(templatePath, model, callback) {

		var templateFcn = templateCache[templatePath];
		if (!templateFcn) {
			templateCache[templatePath] = templateFcn = swig.compileFile(templatePath);
		}
		model = model || {};
		model.widgets = factories;
		renderWidgets = [];
		var html = templateFcn(model);
		async.parallel(renderWidgets, function (err, renderedWidgets) {
			if (err) return callback(err);
			renderedWidgets.forEach(function (renderedWidget) {
				html = html.replace(renderedWidget.placeHolder, renderedWidget.html);
			});
			callback(null, html);
		});
	};

	widgets
		.sort(function (a, b) {
			return a.path.length - b.path.length;
		})
		.forEach(function (widget) {

			var nameSpace = factories,
				name = widget.path[widget.path.length - 1];

			for (var i = 0; i < widget.path.length - 1; i++) {
				nameSpace[widget.path[i]] = nameSpace[widget.path[i]] || {};
				nameSpace = nameSpace[widget.path[i]];
			}

			nameSpace[name] = function (params) {

				params = params || {};

				var identifier = 'widget-' + randomString(),
					cssClass = 'widget-' + widget.path.join('_'),
					output = '',
					placeHolder = '<!-' + identifier + '->';

				if (widget.wrap) output += ('
\n');
				output += placeHolder;
				if (widget.wrap) output += ('\n
');

				renderWidgets.push(function (widgetRenderedCallback) {
					var that = {};
					widget.test.bind(that)(params, function (err, result) {
						if (err) return widgetRenderedCallback(err);
						if (!result) return widgetRenderedCallback(null, {
							placeHolder: output,
							html: ''
						});
						widget.factory.bind(that)(params, function (err, widgetModel, options) {
							if (err) return widgetRenderedCallback(err);
							options = options || {};
							var templatePath = widget.template;
							if (options.template) templatePath = path.join(widget.dir, options.template);
							widgetModel.widgets = result.widgets;
							renderTemplate(templatePath, widgetModel, function (err, widgetHtml) {
								if (err) return widgetRenderedCallback(err);
								widgetRenderedCallback(null, {
									placeHolder: placeHolder,
									html: widgetHtml
								});
							});
						});
					});
				});

				return output;
			};
		});

	return renderTemplate;

};
<commit_msg>Added missing path<commit_after>var async = require('async'),
	path = require('path');

module.exports = function (swig, widgets, randomString) {

	swig.setDefaults({ cache: false });

	var factories = {},
		renderWidgets = [],
		templateCache = {};

	var renderTemplate = function(templatePath, model, callback) {

		var templateFcn = templateCache[templatePath];
		if (!templateFcn) {
			templateCache[templatePath] = templateFcn = swig.compileFile(templatePath);
		}
		model = model || {};
		model.widgets = factories;
		renderWidgets = [];
		var html = templateFcn(model);
		async.parallel(renderWidgets, function (err, renderedWidgets) {
			if (err) return callback(err);
			renderedWidgets.forEach(function (renderedWidget) {
				html = html.replace(renderedWidget.placeHolder, renderedWidget.html);
			});
			callback(null, html);
		});
	};

	widgets
		.sort(function (a, b) {
			return a.path.length - b.path.length;
		})
		.forEach(function (widget) {

			var nameSpace = factories,
				name = widget.path[widget.path.length - 1];

			for (var i = 0; i < widget.path.length - 1; i++) {
				nameSpace[widget.path[i]] = nameSpace[widget.path[i]] || {};
				nameSpace = nameSpace[widget.path[i]];
			}

			nameSpace[name] = function (params) {

				params = params || {};

				var identifier = 'widget-' + randomString(),
					cssClass = 'widget-' + widget.path.join('_'),
					output = '',
					placeHolder = '<!-' + identifier + '->';

				if (widget.wrap) output += ('
\n');
				output += placeHolder;
				if (widget.wrap) output += ('\n
');

				renderWidgets.push(function (widgetRenderedCallback) {
					var that = {};
					widget.test.bind(that)(params, function (err, result) {
						if (err) return widgetRenderedCallback(err);
						if (!result) return widgetRenderedCallback(null, {
							placeHolder: output,
							html: ''
						});
						widget.factory.bind(that)(params, function (err, widgetModel, options) {
							if (err) return widgetRenderedCallback(err);
							options = options || {};
							var templatePath = widget.template;
							if (options.template) templatePath = path.join(widget.dir, options.template);
							widgetModel.widgets = result.widgets;
							renderTemplate(templatePath, widgetModel, function (err, widgetHtml) {
								if (err) return widgetRenderedCallback(err);
								widgetRenderedCallback(null, {
									placeHolder: placeHolder,
									html: widgetHtml
								});
							});
						});
					});
				});

				return output;
			};
		});

	return renderTemplate;

};
<|endoftext|>


<commit_before>'use strict';

angular.module('GreatMath.session-scheduler', ['GreatMath.topic-registry'])

.factory('sessionScheduler', ['topicRegistry',function(topicRegistry) {
  //rows are weeks,
  //columns are topics
  var distributionTable=[
    [1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1],
    [0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0],
    [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
    [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],
    [1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1],
    [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
    [1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1],
    [0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0],
    [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
    [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],
    [1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1],
    [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
    [1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1],
    [0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0],
    [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
    [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],
    [1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1],
    [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
    [1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1]

  ];

  function scheduleKeySkillsQuestions(week,weekIndex,resolve){

    topicRegistry.getTopics({class:'keySkills'},function(topics){
      //distributionTable.forEach(function(row,weekIndex){
        /*while(schedule.weeks.length<=weekIndex){
          schedule.weeks.push({});
        }*/
        var row=distributionTable[weekIndex];
        week.keySkills={questionSpecs:[]};
        row.forEach(function(cell,topicIndex){
          if(cell>0){
            var topicId=null;
            if(topicIndex<topics.length){
              topicId=topics[topicIndex].id;
            }
            week.keySkills.questionSpecs.push({id:topicId});
          }
        });
      //});
      resolve();
    });

  }

  function scheduleMentalStrategyQuestion(week,weekIndex,callback){

    topicRegistry.getTopics({class:'mentalStrategies'},function(topics){
      var row = distributionTable[weekIndex];
      row.forEach(function(cell,topicIndex){
        //0 means no questions on that topic this week.
        //1 ( or anything greater than 0, for that matter) means there is one question on that topic this week
        if(cell>0 ){
          week.mentalStrategies.topics.push({
            id: topicIndex>=topics.length ? null : topics[topicIndex].id //use null as placeholder when there are less than 26 topics
          });
        }
      });

      //times tables
      week.timesTable={
        questionSpecs:[]
      };
      topicRegistry.getTopics({class:'timesTable'},function(ttTopics){
        var ttTopicId;
        if(ttTopics.length==0){
          ttTopicId=null;
        }else{
          ttTopicId=ttTopics[0].id;
        }
        for(var questionNumber=1;questionNumber<=10;questionNumber++){
          var multiplier = 2 + Math.floor( 9 * Math.random());

          week.timesTable.questionSpecs.push(
          {
            id         : ttTopicId,
            multiplier : multiplier
          });

        }
        callback();

      });
    });

  }

  return {
    /*calls the callback with an array of objects.  Each object represents a week and contains 3 arrays of topic ids

    */
    createDistribution : function (callback){
        var numberOfWeeks                             = 39;
        var numberOfSessionsPerWeek                   =  5;
        var numberOfTimesTableQuestionsPerSession     = 10;
        var numberOfKeySkillsQuestionsPerSession      = 10;
        var i=0;

        var result = {
          weeks:[
          ]
        };
        for(var i=0;i<numberOfWeeks;i++){
          var weekIndex=i;
          var week = {
            number : weekIndex+1,
            mentalStrategies:{
              topics : []
            }
          };
          result.weeks.push(week);
          //mental strategies
          (function(week,weekIndex){
            scheduleMentalStrategyQuestion(
              week,
              weekIndex,
              function(){//TODO refactor: use promise pattern
                scheduleKeySkillsQuestions(
                  week,
                  weekIndex,
                  function(){
                    setTimeout(function(){
                      callback(result);
                    },0);
                  }
                );
              }
            );
          })(week,
            weekIndex);

        }

    }
  };
}]);<commit_msg>refactor<commit_after>'use strict';

angular.module('GreatMath.session-scheduler', ['GreatMath.topic-registry'])

.factory('sessionScheduler', ['topicRegistry',function(topicRegistry) {
  //rows are weeks,
  //columns are topics
  var distributionTable=[
    [1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1],
    [0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0],
    [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
    [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],
    [1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1],
    [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
    [1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1],
    [0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0],
    [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
    [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],
    [1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1],
    [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
    [1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1],
    [0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0],
    [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
    [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],
    [1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1],
    [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
    [1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1]

  ];

  function scheduleKeySkillsQuestions(week,weekIndex,done){

    topicRegistry.getTopics({class:'keySkills'},function(topics){
      var row=distributionTable[weekIndex];
      week.keySkills={questionSpecs:[]};
      row.forEach(function(cell,topicIndex){
        if(cell>0){
          var topicId=null;
          if(topicIndex<topics.length){
            topicId=topics[topicIndex].id;
          }
          week.keySkills.questionSpecs.push({id:topicId});
        }
      });
      done();
    });

  }

  function scheduleTimesTableQuestions(week,weekIndex,done){
    //times tables
    week.timesTable={
      questionSpecs:[]
    };
    topicRegistry.getTopics({class:'timesTable'},function(ttTopics){
      var ttTopicId;
      if(ttTopics.length==0){
        ttTopicId=null;
      }else{
        ttTopicId=ttTopics[0].id;
      }
      for(var questionNumber=1;questionNumber<=10;questionNumber++){
        var multiplier = 2 + Math.floor( 9 * Math.random());

        week.timesTable.questionSpecs.push(
        {
          id         : ttTopicId,
          multiplier : multiplier
        });

      }
      done();
    });
  }

  function scheduleMentalStrategyQuestion(week,weekIndex,done){

    topicRegistry.getTopics({class:'mentalStrategies'},function(topics){
      var row = distributionTable[weekIndex];
      row.forEach(function(cell,topicIndex){
        //0 means no questions on that topic this week.
        //1 ( or anything greater than 0, for that matter) means there is one question on that topic this week
        if(cell>0 ){
          week.mentalStrategies.topics.push({
            id: topicIndex>=topics.length ? null : topics[topicIndex].id //use null as placeholder when there are less than 26 topics
          });
        }
      });
      done();

    });

  }

  return {
    /*calls the callback with an array of objects.  Each object represents a week and contains 3 arrays of topic ids

    */
    createDistribution : function (callback){
        var numberOfWeeks                             = 39;
        var numberOfSessionsPerWeek                   =  5;
        var numberOfTimesTableQuestionsPerSession     = 10;
        var numberOfKeySkillsQuestionsPerSession      = 10;
        var i=0;

        var result = {
          weeks:[
          ]
        };
        for(var i=0;i<numberOfWeeks;i++){
          var weekIndex=i;
          var week = {
            number : weekIndex+1,
            mentalStrategies:{
              topics : []
            }
          };
          result.weeks.push(week);
          //mental strategies
          (function(week,weekIndex){
            scheduleMentalStrategyQuestion(
              week,
              weekIndex,
              function(){//TODO refactor: use promise pattern
                scheduleTimesTableQuestions(
                  week,
                  weekIndex,
                  function(){
                    scheduleKeySkillsQuestions(
                      week,
                      weekIndex,
                      function(){
                        setTimeout(function(){
                          callback(result);
                        },0);
                      }
                    );
                  }
                );
              }
            );
          })(week,
            weekIndex);

        }

    }
  };
}]);<|endoftext|>


<commit_before>'use strict';

var merge = require('merge-stream');
var symlink = require('gulp-sym')

module.exports = function (gulp, plugins) {
    return function () {
        var config = {
            src: {
                html: 'src/**/*.html',
                py: 'src/**/*.py',
                css: 'src/css/**/*.css',
                ico: 'src/assets/ico/*.ico',
            },
            target: {
                html: 'dist/templates',
                py: 'dist',
                css: 'dist/assets/css',
                ico: 'dist/assets/ico',
            }
        }

        var html = gulp.src(config.src.html)
            .pipe(gulp.dest(config.target.html));

        var py = gulp.src(config.src.py)
            .pipe(gulp.dest(config.target.py));

        var css = gulp.src(config.src.css)
            .pipe(gulp.dest(config.target.css));

        var ico = gulp.src(config.src.ico)
            .pipe(gulp.dest(config.target.ico));

        var systemJs = gulp.src('./src/assets/systemjs.config.js')
            .pipe(gulp.dest('dist/assets'));

        return merge(html, py, css, ico, systemJs);
    }
};
<commit_msg>Update copySrc.js<commit_after>'use strict';

var merge = require('merge-stream');
var symlink = require('gulp-sym')

module.exports = function (gulp, plugins) {
    return function () {
        var config = {
            src: {
                html: 'src/**/*.html',
                py: 'src/**/*.py',
                css: 'src/css/**/*.css',
                ico: 'src/assets/ico/*.ico',
            },
            target: {
                html: 'dist',
                py: 'dist',
                css: 'dist/assets/css',
                ico: 'dist/assets/ico',
            }
        }

        var html = gulp.src(config.src.html)
            .pipe(gulp.dest(config.target.html));

        var py = gulp.src(config.src.py)
            .pipe(gulp.dest(config.target.py));

        var css = gulp.src(config.src.css)
            .pipe(gulp.dest(config.target.css));

        var ico = gulp.src(config.src.ico)
            .pipe(gulp.dest(config.target.ico));

        var systemJs = gulp.src('./src/assets/systemjs.config.js')
            .pipe(gulp.dest('dist/assets'));

        return merge(html, py, css, ico, systemJs);
    }
};
<|endoftext|>


<commit_before>/**
 * Created by ksinger on 17.06.2016.
 */

;

import Medium from '../mediumjs-es6';
import angular from 'angular';
import 'ng-redux';
import Immutable from 'immutable';
import 'angular-sanitize';
import { dispatchEvent, attach, delay } from '../utils';
import { actionCreators }  from '../actions/actions';

window.Medium4dbg = Medium;

function genComponentConf() {
    let template = `

                {{ self.charactersLeft() }} characters left

    `;

    const serviceDependencies = ['$scope', '$element', '$ngRedux', /*injections as strings here*/];

    class Controller {
        constructor(/* arguments <- serviceDependencies */) {
            attach(this, serviceDependencies, arguments);
            window.ntf4dbg = this;

            this.characterLimit = 140; //TODO move to conf

            const selectFromState = (state) => ({
                draftId: state.getIn(['router', 'currentParams', 'draftId'])
            })
            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);

            this.initMedium();

            this.mediumMountNg().bind('input', e => {
                this.drafts__change__title({
                    draftId: this.draftId,
                    title: this.value(),
                });
            });
        }
        charactersLeft() {
            return this.characterLimit - this.medium.value().length;
        }
        valid() {
            return this.charactersLeft() >= 0;
        }

        value() {
            return this.medium
                .value()
                /*
                 * the replace fixes odd behaviour of FF. it inserts
                 * a `<br>` at the end after the first space is
                 * typed -- unless the space is the first character
                 * in the field.
                 */
                .replace(/
$/, '');
        }

        initMedium() {
            // initialising editor. see http://jakiestfu.github.io/Medium.js/docs/
            this.medium = new Medium({
                element: this.mediumMount(),

                modifier: 'auto',
                placeholder: 'What',
                autoHR: false, //if true, inserts
 after two empty lines
                mode: Medium.inlineMode, // no newlines, no styling
                //mode: Medium.partialMode, // allows newlines, no styling
                //maxLength: this.maxChars, // -1 would disable it
                tags: {
                    /*
                     'break': 'br',
                     'horizontalRule': 'hr',
                     'paragraph': 'p',
                     'outerLevel': ['pre', 'blockquote', 'figure'],
                     'innerLevel': ['a', 'b', 'u', 'i', 'img', 'strong']
                     */
                    //'outerLevel': [], //should disable all tags
                    //'innerLevel': [], //should disable all tags
                },
                attributes: {
                    //remove: ['style', 'class'] //TODO does this remove the ng-class?
                    remove: ['style'] //TODO does this remove the ng-class?
                },
                /*
                pasteAsText: true,
                beforeInvokeElement: function () {
                 //this = Medium.Element
                     console.log('beforeInvokeElement: ', this)
                 },
                 beforeInsertHtml: function () {
                 //this = Medium.Html
                     console.log('beforeInsertHtml: ', this)
                 },
                 beforeAddTag: function (tag, shouldFocus, isEditable, afterElement) {
                     console.log('beforeAddTag: ', this, arguments)
                 },
                 keyContext: null, //what does this do?
                 pasteEventHandler: function(e) {
                    //default paste event handler
                    //enables paste (dunno why) but also breaks the inlineMode (suddenly there's
 elements)
                    console.log('pasteEventHandler: ', this, arguments)
                 }
                */

            });

            //remove the inline-styles placed by medium.js
            this.medium.placeholder.style = "";
            this.mediumMount().addEventListener('blur', e =>
                delay(0) //push to end end of task-queue (and thus all other `blur`-listeners
                    .then(() => {
                        const style = this.medium.placeholder.style;
                        style.minHeight = 0;
                        style.minWidth = 0;
                    })
            );
        }

        mediumMountNg() {
            if(!this._mediumMount) {
                this._mediumMount = this.textFieldNg().find('.medium-mount')
            }
            return this._mediumMount;
        }

        mediumMount() {
            return this.mediumMountNg()[0];
        }

        textFieldNg() {
            if(!this._textField) {
                this._textField = this.$element.find('.wdt__text');
            }
            return this._textField;
        }
        textField() {
            return this.textFieldNg()[0];
        }
    }
    Controller.$inject = serviceDependencies;

    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        scope: { },
        template: template
    }
}

export default angular.module('won.owner.components.needTextfield', [ ])
    .directive('needTextfield', genComponentConf)
    .name;<commit_msg>altered need-textfield to extract tags and description if necessary<commit_after>/**
 * Created by ksinger on 17.06.2016.
 */

;

import Medium from '../mediumjs-es6';
import angular from 'angular';
import 'ng-redux';
import Immutable from 'immutable';
import 'angular-sanitize';
import { dispatchEvent, attach, delay } from '../utils';
import { actionCreators }  from '../actions/actions';

window.Medium4dbg = Medium;

function genComponentConf() {
    let template = `

                {{ self.charactersLeft() }} characters left

    `;

    const serviceDependencies = ['$scope', '$element', '$ngRedux', /*injections as strings here*/];

    class Controller {
        constructor(/* arguments <- serviceDependencies */) {
            attach(this, serviceDependencies, arguments);
            window.ntf4dbg = this;

            this.characterLimit = 140; //TODO move to conf

            const selectFromState = (state) => ({
                draftId: state.getIn(['router', 'currentParams', 'draftId'])
            })
            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);

            this.initMedium();

            this.mediumMountNg().bind('input', e => {
                var description;
                var title;
                var tags;

                angular.element(".medium-mount p").removeClass("medium_title");

                if(angular.element(".medium-mount p") && angular.element(".medium-mount p").length > 1){
                    angular.element(".medium-mount p:first").addClass("medium_title");
                    title  = angular.element(".medium-mount p.medium_title").text();

                    if(angular.element(".medium-mount p:not('.medium_title')") && angular.element(".medium-mount p:not('.medium_title')").length > 0){
                        description = "";
                        angular.element(".medium-mount p:not('.medium_title')").each(function(){
                            description += angular.element(this).text() +"\n";
                        });
                    }
                }else {
                    title  = angular.element(".medium-mount p:first").text();
                    description = undefined;
                }

                //ADD TAGS
                var titleTags = title? title.match(/#(\w+)/gi) : [];
                var descriptionTags = description? description.match(/#(\w+)/gi) : [];

                tags = angular.element.unique(
                    angular.element.merge(
                        titleTags ? titleTags : [],
                        descriptionTags ? descriptionTags : []
                    )
                );

                for(var i=0; i<tags.length; i++){
                    tags[i] = tags[i].substr(1);
                }

                //SAVE TO STATE
                this.drafts__change__description({
                    draftId: this.draftId,
                    description : description
                });

                this.drafts__change__title({
                    draftId: this.draftId,
                    title: title.replace(" ","") //TODO: MOVE THIS HACK TO VIEW LEVEL
                });

                this.drafts__change__tags({
                    draftId: this.draftId,
                    tags: tags && tags.length > 0? tags : undefined
                });
            });
        }
        charactersLeft() {
            return this.characterLimit - this.medium.value().length;
        }
        valid() {
            return this.charactersLeft() >= 0;
        }

        value() {
            return this.medium
                .value()
                /*
                 * the replace fixes odd behaviour of FF. it inserts
                 * a `<br>` at the end after the first space is
                 * typed -- unless the space is the first character
                 * in the field.
                 */
                .replace(/
$/, '');
        }

        initMedium() {
            // initialising editor. see http://jakiestfu.github.io/Medium.js/docs/
            this.medium = new Medium({
                element: this.mediumMount(),

                modifier: 'auto',
                placeholder: 'What',
                autoHR: false, //if true, inserts
 after two empty lines
                //mode: Medium.inlineMode, // no newlines, no styling
                mode: Medium.partialMode, // allows newlines, no styling
                //maxLength: this.maxChars, // -1 would disable it
                tags: {
                    /*
                     'break': 'br',
                     'horizontalRule': 'hr',
                     'paragraph': 'p',
                     'outerLevel': ['pre', 'blockquote', 'figure'],
                     'innerLevel': ['a', 'b', 'u', 'i', 'img', 'strong']
                     */
                    //'outerLevel': [], //should disable all tags
                    //'innerLevel': [], //should disable all tags
                },
                attributes: {
                    //remove: ['style', 'class'] //TODO does this remove the ng-class?
                    remove: ['style'] //TODO does this remove the ng-class?
                },
                /*
                pasteAsText: true,
                beforeInvokeElement: function () {
                 //this = Medium.Element
                     console.log('beforeInvokeElement: ', this)
                 },
                 beforeInsertHtml: function () {
                 //this = Medium.Html
                     console.log('beforeInsertHtml: ', this)
                 },
                 beforeAddTag: function (tag, shouldFocus, isEditable, afterElement) {
                     console.log('beforeAddTag: ', this, arguments)
                 },
                 keyContext: null, //what does this do?
                 pasteEventHandler: function(e) {
                    //default paste event handler
                    //enables paste (dunno why) but also breaks the inlineMode (suddenly there's
 elements)
                    console.log('pasteEventHandler: ', this, arguments)
                 }
                */

            });

            //remove the inline-styles placed by medium.js
            this.medium.placeholder.style = "";
            this.mediumMount().addEventListener('blur', e =>
                delay(0) //push to end end of task-queue (and thus all other `blur`-listeners
                    .then(() => {
                        const style = this.medium.placeholder.style;
                        style.minHeight = 0;
                        style.minWidth = 0;
                    })
            );
        }

        mediumMountNg() {
            if(!this._mediumMount) {
                this._mediumMount = this.textFieldNg().find('.medium-mount')
            }
            return this._mediumMount;
        }

        mediumMount() {
            return this.mediumMountNg()[0];
        }

        textFieldNg() {
            if(!this._textField) {
                this._textField = this.$element.find('.wdt__text');
            }
            return this._textField;
        }
        textField() {
            return this.textFieldNg()[0];
        }
    }
    Controller.$inject = serviceDependencies;

    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        scope: { },
        template: template
    }
}

export default angular.module('won.owner.components.needTextfield', [ ])
    .directive('needTextfield', genComponentConf)
    .name;<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

var util = require('util');
var _ = require('@sailshq/lodash');
var LifecycleCallbackBuilder = require('./utils/system/lifecycle-callback-builder');
var TransformerBuilder = require('./utils/system/transformer-builder');
var hasSchemaCheck = require('./utils/system/has-schema-check');

/**
 * MetaModel
 *
 * Construct a new MetaModel instance (e.g. `User` or `WLModel`) with methods for
 * interacting with a set of structured database records.
 *
 * > This file contains the entry point for all ORM methods (e.g. User.find())
 * > In other words, `User` is a MetaModel INSTANCE  (sorry! I know it's weird with
 * > the capital "U"!)
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage:
 * ```
 * var WLModel = new MetaModel(orm, { adapter: require('sails-disk') });
 * // (sorry about the capital "W" in the instance!)
 * ```
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * @param {Dictionary} orm
 *
 * @param {Dictionary} adapterWrapper
 *        @property {Dictionary} adapter
 *                  The adapter definition.
 *                  ************************************************************
 *                  FUTURE: probably just remove this second argument.  Instead of
 *                  passing it in, it seems like we should just look up the
 *                  appropriate adapter at the top of this constructor function
 *                  (or even just attach `._adapter` in userland- after instantiating
 *                  the new MetaModel instance-- e.g. "WLModel").  The only code that
 *                  directly runs `new MetaModel()` or `new SomeCustomizedMetaModel()`
 *                  is inside of Waterline core anyway.)
 *                  ************************************************************
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * @constructs {MetaModel}
 *             The base MetaModel from whence other MetaModels are customized.
 *             Remember: running `new MetaModel()` yields an instance like `User`,
 *             which is itself generically called a WLModel.
 *
 *             > This is kind of confusing, mainly because capitalization.  And
 *             > it feels silly to nitpick about something so confusing.  But at
 *             > least this way we know what everything's called, and it's consistent.
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */

var MetaModel = module.exports = function MetaModel (orm, adapterWrapper) {

  // Attach a private reference to the adapter definition indicated by
  // this model's configured `datastore`.
  this._adapter = adapterWrapper.adapter;

  // Attach a private reference to the ORM.
  this._orm = orm;
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // > Note that we also alias it as `this.waterline`.
  this.waterline = orm;
  // ^^^
  // FUTURE: remove this alias in Waterline v1.0
  // (b/c it implies that `this.waterline` might be the stateless export from
  // the Waterline package itself, rather than what it actually is: a configured
  // ORM instance)
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  // Initialize the `attributes` of our new MetaModel instance (e.g. `User.attributes`)
  // to an empty dictionary, unless they're already set.
  if (_.isUndefined(this.attributes)) {
    this.attributes = {};
  }
  else {
    if (!_.isObject(this.attributes)) {
      throw new Error('Consistency violation: When instantiating this new instance of MetaModel, it became clear (within the constructor) that `this.attributes` was already set, and not a dictionary: '+util.inspect(this.attributes, {depth: 5})+'');
    }
    else {
      // FUTURE: Consider not allowing this, because it's weird.
    }
  }

  // Set the `adapter` property to an empty dictionary if it is not already truthy.
  this.adapter = this.adapter || {};
  // ^^TODO: can we remove this now?

  // Build a dictionary of all lifecycle callbacks applicable to this model, and
  // attach it as a private property (`_callbacks`).
  this._callbacks = LifecycleCallbackBuilder(this);
  //^^FUTURE: bust this utility apart to make it stateless like the others
  //
  //^^FUTURE: Also, document what's going on here as far as timing-- i.e. answering questions
  //like "when are model settings from the original model definition applied?" and
  //"How are they set?".

  // Set the `hasSchema` flag for this model.
  // > This is based on a handful of factors, including the original model definition,
  // > ORM-wide default model settings, and (if defined) an implicit default from the
  // > adapter itself.
  this.hasSchema = hasSchemaCheck(this);
  // ^^FUTURE: change utility's name to either the imperative mood (e.g. `getSchemafulness()`)
  // or interrogative mood (`isSchemaful()`) for consistency w/ the other utilities
  // (and to avoid confusion, because the name of the flag makes it kind of crazy in this case.)

  // Build a TransformerBuilder instance and attach it as a private property (`_transformer`).
  this._transformer = new TransformerBuilder(this.schema);
  // ^^FUTURE: bust this utility apart to make it stateless like the others

  return this;
  // ^^FUTURE: remove this `return` (it shouldn't be necessary)
};

//  ██╗███╗   ██╗███████╗████████╗ █████╗ ███╗   ██╗ ██████╗███████╗
//  ██║████╗  ██║██╔════╝╚══██╔══╝██╔══██╗████╗  ██║██╔════╝██╔════╝
//  ██║██╔██╗ ██║███████╗   ██║   ███████║██╔██╗ ██║██║     █████╗
//  ██║██║╚██╗██║╚════██║   ██║   ██╔══██║██║╚██╗██║██║     ██╔══╝
//  ██║██║ ╚████║███████║   ██║   ██║  ██║██║ ╚████║╚██████╗███████╗
//  ╚═╝╚═╝  ╚═══╝╚══════╝   ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═══╝ ╚═════╝╚══════╝
//
//  ███╗   ███╗███████╗████████╗██╗  ██╗ ██████╗ ██████╗ ███████╗
//  ████╗ ████║██╔════╝╚══██╔══╝██║  ██║██╔═══██╗██╔══██╗██╔════╝
//  ██╔████╔██║█████╗     ██║   ███████║██║   ██║██║  ██║███████╗
//  ██║╚██╔╝██║██╔══╝     ██║   ██╔══██║██║   ██║██║  ██║╚════██║
//  ██║ ╚═╝ ██║███████╗   ██║   ██║  ██║╚██████╔╝██████╔╝███████║
//  ╚═╝     ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝
//
// INSTANCE METHODS
//
// Now extend the MetaModel constructor's `prototype` with each built-in model method.
// > This allows for the use of `Foo.find()`, etc., and it's equivalent to attaching
// > each method individually (e.g. `MetaModel.prototype.find = ()->{}`), just with
// > slightly better performance characteristics.
_.extend(
  MetaModel.prototype,
  {
    // DQL
    find: require('./methods/find'),
    findOne: require('./methods/find-one'),
    findOrCreate: require('./methods/find-or-create'),
    stream: require('./methods/stream'),
    count: require('./methods/count'),
    sum: require('./methods/sum'),
    avg: require('./methods/avg'),

    // DML
    create: require('./methods/create'),
    createEach: require('./methods/create-each'),
    update: require('./methods/update'),
    destroy: require('./methods/destroy'),
    addToCollection: require('./methods/add-to-collection'),
    removeFromCollection: require('./methods/remove-from-collection'),
    replaceCollection: require('./methods/replace-collection'),

    // Misc.
    validate: require('./methods/validate'),
  }
);

//  ███████╗████████╗ █████╗ ████████╗██╗ ██████╗
//  ██╔════╝╚══██╔══╝██╔══██╗╚══██╔══╝██║██╔════╝
//  ███████╗   ██║   ███████║   ██║   ██║██║
//  ╚════██║   ██║   ██╔══██║   ██║   ██║██║
//  ███████║   ██║   ██║  ██║   ██║   ██║╚██████╗
//  ╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝   ╚═╝ ╚═════╝
//
//  ███╗   ███╗███████╗████████╗██╗  ██╗ ██████╗ ██████╗ ███████╗
//  ████╗ ████║██╔════╝╚══██╔══╝██║  ██║██╔═══██╗██╔══██╗██╔════╝
//  ██╔████╔██║█████╗     ██║   ███████║██║   ██║██║  ██║███████╗
//  ██║╚██╔╝██║██╔══╝     ██║   ██╔══██║██║   ██║██║  ██║╚════██║
//  ██║ ╚═╝ ██║███████╗   ██║   ██║  ██║╚██████╔╝██████╔╝███████║
//  ╚═╝     ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝
//
// STATIC METHODS
//
// Now add properties to the MetaModel constructor itself.
// (i.e. static properties)

/**
 * MetaModel.extend()
 *
 * Build & return a new constructor based on the existing constructor in the
 * current runtime context (`this`) -- which happens to be our base model
 * constructor (MetaModel).  This also attaches the specified properties to
 * the new constructor's prototype.
 *
 *
 * > Originally taken from `.extend()` in Backbone source:
 * > http://backbonejs.org/docs/backbone.html#section-189
 * >
 * > Although this is called `extend()`, note that it does not actually modify
 * > the original MetaModel constructor.  Instead, it first builds a shallow
 * > clone of the original constructor and then extends THAT.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * @param {Dictionary?}  protoProps
 *        Optional extra set of properties to attach to the new ctor's prototype.
 *        (& possibly a brand of breakfast cereal)
 *
 * @param {Dictionary?}  staticProps
 *        NO LONGER SUPPORTED: An optional, extra set of properties to attach
 *        directly to the new ctor.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * @returns {Function}  [The new constructor -- e.g. `SomeCustomizedMetaModel`]
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * @this {Function}  [The original constructor -- BaseMetaModel]
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
MetaModel.extend = function (protoProps, staticProps) {
  var thisConstructor = this;

  // Sanity checks:

  // If a prototypal properties were provided, and one of them is under the `constructor` key,
  // then freak out.  This is no longer supported, and shouldn't still be in use anywhere.
  if (protoProps && _.has(protoProps, 'constructor')) {
    throw new Error('Consistency violation: The first argument (`protoProps`) provided to Waterline.Model.extend() should never have a `constructor` property. (This kind of usage is no longer supported.)');
  }

  // If any additional custom static properties were specified, then freak out.
  // This is no longer supported, and shouldn't still be in use anywhere.
  if (!_.isUndefined(staticProps)) {
    throw new Error('Consistency violation: Unrecognized extra argument provided to Waterline.Model.extend() (`staticProps` is no longer supported.)');
  }

  //--•
  // Now proceed with the classical, Backbone-flavor extending.

  var newConstructor;
  if (protoProps && _.has(protoProps, 'constructor')) {
    newConstructor = protoProps.constructor;
  } else {
    newConstructor = function() { return thisConstructor.apply(this, arguments); };
  }

  // Shallow-copy all of the static properties (top-level props of original constructor)
  // over to the new constructor.
  _.extend(newConstructor, thisConstructor, staticProps);

  // Create an ad hoc "Surrogate" -- a short-lived, bionic kind of a constructor
  // that serves as an intermediary... or maybe more of an organ donor?  Surrogate
  // is probably still best.  Anyway it's some dark stuff, that's for sure.  Because
  // what happens next is that we give it a reference to our original ctor's prototype
  // and constructor, then "new up" an instance for us-- but only so that we can cut out
  // that newborn instance's `prototype` and put it where the prototype for our new ctor
  // is supposed to go.
  //
  // > Why?  Well for one thing, this is important so that our new constructor appears
  // > to "inherit" from our original constructor.  But likely a more prescient motive
  // > is so that our new ctor is a proper clone.  That is, it's no longer entangled with
  // > the original constructor.
  // > (More or less anyway.  If there are any deeply nested things, like an `attributes`
  // > dictionary -- those could still contain deep, entangled references to stuff from the
  // > original ctor's prototype.
  var Surrogate = function() { this.constructor = newConstructor; };
  Surrogate.prototype = thisConstructor.prototype;
  newConstructor.prototype = new Surrogate();

  // If extra `protoProps` were provided, merge them onto our new ctor's prototype.
  // (now that it's a legitimately separate thing that we can safely modify)
  if (protoProps) {
    _.extend(newConstructor.prototype, protoProps);
  }

  // Set a proprietary `__super__` key to keep track of the original ctor's prototype.
  // (see http://stackoverflow.com/questions/8596861/super-in-backbone#comment17856929_8614228)
  newConstructor.__super__ = thisConstructor.prototype;

  // Return our new ctor.
  return newConstructor;

};

<commit_msg>rip out unneeded default setting<commit_after>/**
 * Module dependencies
 */

var util = require('util');
var _ = require('@sailshq/lodash');
var LifecycleCallbackBuilder = require('./utils/system/lifecycle-callback-builder');
var TransformerBuilder = require('./utils/system/transformer-builder');
var hasSchemaCheck = require('./utils/system/has-schema-check');

/**
 * MetaModel
 *
 * Construct a new MetaModel instance (e.g. `User` or `WLModel`) with methods for
 * interacting with a set of structured database records.
 *
 * > This file contains the entry point for all ORM methods (e.g. User.find())
 * > In other words, `User` is a MetaModel INSTANCE  (sorry! I know it's weird with
 * > the capital "U"!)
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage:
 * ```
 * var WLModel = new MetaModel(orm, { adapter: require('sails-disk') });
 * // (sorry about the capital "W" in the instance!)
 * ```
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * @param {Dictionary} orm
 *
 * @param {Dictionary} adapterWrapper
 *        @property {Dictionary} adapter
 *                  The adapter definition.
 *                  ************************************************************
 *                  FUTURE: probably just remove this second argument.  Instead of
 *                  passing it in, it seems like we should just look up the
 *                  appropriate adapter at the top of this constructor function
 *                  (or even just attach `._adapter` in userland- after instantiating
 *                  the new MetaModel instance-- e.g. "WLModel").  The only code that
 *                  directly runs `new MetaModel()` or `new SomeCustomizedMetaModel()`
 *                  is inside of Waterline core anyway.)
 *                  ************************************************************
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * @constructs {MetaModel}
 *             The base MetaModel from whence other MetaModels are customized.
 *             Remember: running `new MetaModel()` yields an instance like `User`,
 *             which is itself generically called a WLModel.
 *
 *             > This is kind of confusing, mainly because capitalization.  And
 *             > it feels silly to nitpick about something so confusing.  But at
 *             > least this way we know what everything's called, and it's consistent.
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */

var MetaModel = module.exports = function MetaModel (orm, adapterWrapper) {

  // Attach a private reference to the adapter definition indicated by
  // this model's configured `datastore`.
  this._adapter = adapterWrapper.adapter;

  // Attach a private reference to the ORM.
  this._orm = orm;
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // > Note that we also alias it as `this.waterline`.
  this.waterline = orm;
  // ^^^
  // FUTURE: remove this alias in Waterline v1.0
  // (b/c it implies that `this.waterline` might be the stateless export from
  // the Waterline package itself, rather than what it actually is: a configured
  // ORM instance)
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  // Initialize the `attributes` of our new MetaModel instance (e.g. `User.attributes`)
  // to an empty dictionary, unless they're already set.
  if (_.isUndefined(this.attributes)) {
    this.attributes = {};
  }
  else {
    if (!_.isObject(this.attributes)) {
      throw new Error('Consistency violation: When instantiating this new instance of MetaModel, it became clear (within the constructor) that `this.attributes` was already set, and not a dictionary: '+util.inspect(this.attributes, {depth: 5})+'');
    }
    else {
      // FUTURE: Consider not allowing this, because it's weird.
    }
  }

  // Build a dictionary of all lifecycle callbacks applicable to this model, and
  // attach it as a private property (`_callbacks`).
  this._callbacks = LifecycleCallbackBuilder(this);
  //^^FUTURE: bust this utility apart to make it stateless like the others
  //
  //^^FUTURE: Also, document what's going on here as far as timing-- i.e. answering questions
  //like "when are model settings from the original model definition applied?" and
  //"How are they set?".

  // Set the `hasSchema` flag for this model.
  // > This is based on a handful of factors, including the original model definition,
  // > ORM-wide default model settings, and (if defined) an implicit default from the
  // > adapter itself.
  this.hasSchema = hasSchemaCheck(this);
  // ^^FUTURE: change utility's name to either the imperative mood (e.g. `getSchemafulness()`)
  // or interrogative mood (`isSchemaful()`) for consistency w/ the other utilities
  // (and to avoid confusion, because the name of the flag makes it kind of crazy in this case.)

  // Build a TransformerBuilder instance and attach it as a private property (`_transformer`).
  this._transformer = new TransformerBuilder(this.schema);
  // ^^FUTURE: bust this utility apart to make it stateless like the others

  return this;
  // ^^FUTURE: remove this `return` (it shouldn't be necessary)
};

//  ██╗███╗   ██╗███████╗████████╗ █████╗ ███╗   ██╗ ██████╗███████╗
//  ██║████╗  ██║██╔════╝╚══██╔══╝██╔══██╗████╗  ██║██╔════╝██╔════╝
//  ██║██╔██╗ ██║███████╗   ██║   ███████║██╔██╗ ██║██║     █████╗
//  ██║██║╚██╗██║╚════██║   ██║   ██╔══██║██║╚██╗██║██║     ██╔══╝
//  ██║██║ ╚████║███████║   ██║   ██║  ██║██║ ╚████║╚██████╗███████╗
//  ╚═╝╚═╝  ╚═══╝╚══════╝   ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═══╝ ╚═════╝╚══════╝
//
//  ███╗   ███╗███████╗████████╗██╗  ██╗ ██████╗ ██████╗ ███████╗
//  ████╗ ████║██╔════╝╚══██╔══╝██║  ██║██╔═══██╗██╔══██╗██╔════╝
//  ██╔████╔██║█████╗     ██║   ███████║██║   ██║██║  ██║███████╗
//  ██║╚██╔╝██║██╔══╝     ██║   ██╔══██║██║   ██║██║  ██║╚════██║
//  ██║ ╚═╝ ██║███████╗   ██║   ██║  ██║╚██████╔╝██████╔╝███████║
//  ╚═╝     ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝
//
// INSTANCE METHODS
//
// Now extend the MetaModel constructor's `prototype` with each built-in model method.
// > This allows for the use of `Foo.find()`, etc., and it's equivalent to attaching
// > each method individually (e.g. `MetaModel.prototype.find = ()->{}`), just with
// > slightly better performance characteristics.
_.extend(
  MetaModel.prototype,
  {
    // DQL
    find: require('./methods/find'),
    findOne: require('./methods/find-one'),
    findOrCreate: require('./methods/find-or-create'),
    stream: require('./methods/stream'),
    count: require('./methods/count'),
    sum: require('./methods/sum'),
    avg: require('./methods/avg'),

    // DML
    create: require('./methods/create'),
    createEach: require('./methods/create-each'),
    update: require('./methods/update'),
    destroy: require('./methods/destroy'),
    addToCollection: require('./methods/add-to-collection'),
    removeFromCollection: require('./methods/remove-from-collection'),
    replaceCollection: require('./methods/replace-collection'),

    // Misc.
    validate: require('./methods/validate'),
  }
);

//  ███████╗████████╗ █████╗ ████████╗██╗ ██████╗
//  ██╔════╝╚══██╔══╝██╔══██╗╚══██╔══╝██║██╔════╝
//  ███████╗   ██║   ███████║   ██║   ██║██║
//  ╚════██║   ██║   ██╔══██║   ██║   ██║██║
//  ███████║   ██║   ██║  ██║   ██║   ██║╚██████╗
//  ╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝   ╚═╝ ╚═════╝
//
//  ███╗   ███╗███████╗████████╗██╗  ██╗ ██████╗ ██████╗ ███████╗
//  ████╗ ████║██╔════╝╚══██╔══╝██║  ██║██╔═══██╗██╔══██╗██╔════╝
//  ██╔████╔██║█████╗     ██║   ███████║██║   ██║██║  ██║███████╗
//  ██║╚██╔╝██║██╔══╝     ██║   ██╔══██║██║   ██║██║  ██║╚════██║
//  ██║ ╚═╝ ██║███████╗   ██║   ██║  ██║╚██████╔╝██████╔╝███████║
//  ╚═╝     ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝
//
// STATIC METHODS
//
// Now add properties to the MetaModel constructor itself.
// (i.e. static properties)

/**
 * MetaModel.extend()
 *
 * Build & return a new constructor based on the existing constructor in the
 * current runtime context (`this`) -- which happens to be our base model
 * constructor (MetaModel).  This also attaches the specified properties to
 * the new constructor's prototype.
 *
 *
 * > Originally taken from `.extend()` in Backbone source:
 * > http://backbonejs.org/docs/backbone.html#section-189
 * >
 * > Although this is called `extend()`, note that it does not actually modify
 * > the original MetaModel constructor.  Instead, it first builds a shallow
 * > clone of the original constructor and then extends THAT.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * @param {Dictionary?}  protoProps
 *        Optional extra set of properties to attach to the new ctor's prototype.
 *        (& possibly a brand of breakfast cereal)
 *
 * @param {Dictionary?}  staticProps
 *        NO LONGER SUPPORTED: An optional, extra set of properties to attach
 *        directly to the new ctor.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * @returns {Function}  [The new constructor -- e.g. `SomeCustomizedMetaModel`]
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * @this {Function}  [The original constructor -- BaseMetaModel]
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
MetaModel.extend = function (protoProps, staticProps) {
  var thisConstructor = this;

  // Sanity checks:

  // If a prototypal properties were provided, and one of them is under the `constructor` key,
  // then freak out.  This is no longer supported, and shouldn't still be in use anywhere.
  if (protoProps && _.has(protoProps, 'constructor')) {
    throw new Error('Consistency violation: The first argument (`protoProps`) provided to Waterline.Model.extend() should never have a `constructor` property. (This kind of usage is no longer supported.)');
  }

  // If any additional custom static properties were specified, then freak out.
  // This is no longer supported, and shouldn't still be in use anywhere.
  if (!_.isUndefined(staticProps)) {
    throw new Error('Consistency violation: Unrecognized extra argument provided to Waterline.Model.extend() (`staticProps` is no longer supported.)');
  }

  //--•
  // Now proceed with the classical, Backbone-flavor extending.

  var newConstructor;
  if (protoProps && _.has(protoProps, 'constructor')) {
    newConstructor = protoProps.constructor;
  } else {
    newConstructor = function() { return thisConstructor.apply(this, arguments); };
  }

  // Shallow-copy all of the static properties (top-level props of original constructor)
  // over to the new constructor.
  _.extend(newConstructor, thisConstructor, staticProps);

  // Create an ad hoc "Surrogate" -- a short-lived, bionic kind of a constructor
  // that serves as an intermediary... or maybe more of an organ donor?  Surrogate
  // is probably still best.  Anyway it's some dark stuff, that's for sure.  Because
  // what happens next is that we give it a reference to our original ctor's prototype
  // and constructor, then "new up" an instance for us-- but only so that we can cut out
  // that newborn instance's `prototype` and put it where the prototype for our new ctor
  // is supposed to go.
  //
  // > Why?  Well for one thing, this is important so that our new constructor appears
  // > to "inherit" from our original constructor.  But likely a more prescient motive
  // > is so that our new ctor is a proper clone.  That is, it's no longer entangled with
  // > the original constructor.
  // > (More or less anyway.  If there are any deeply nested things, like an `attributes`
  // > dictionary -- those could still contain deep, entangled references to stuff from the
  // > original ctor's prototype.
  var Surrogate = function() { this.constructor = newConstructor; };
  Surrogate.prototype = thisConstructor.prototype;
  newConstructor.prototype = new Surrogate();

  // If extra `protoProps` were provided, merge them onto our new ctor's prototype.
  // (now that it's a legitimately separate thing that we can safely modify)
  if (protoProps) {
    _.extend(newConstructor.prototype, protoProps);
  }

  // Set a proprietary `__super__` key to keep track of the original ctor's prototype.
  // (see http://stackoverflow.com/questions/8596861/super-in-backbone#comment17856929_8614228)
  newConstructor.__super__ = thisConstructor.prototype;

  // Return our new ctor.
  return newConstructor;

};

<|endoftext|>


<commit_before>/*
	Customized plugin added by David Walsh (:davidwalsh, dwalsh@mozilla.com)

	This plugin inserts boilerplate live sample content
*/
CKEDITOR.plugins.add('mdn-sampler', {

	requires: ['selection'],

	init: function(editor) {

		editor.addCommand('mdnSampler', {
			exec: function (editor, data) {

				// Inject heading
				var heading = new CKEDITOR.dom.element('h2', editor.document);
				heading.setText(gettext('Sample Title'));
				editor.insertElement(heading);

				// Inject Pre[html]
				var htmlPre = new CKEDITOR.dom.element('pre', editor.document);
				htmlPre.setText(gettext('Sample HTML Content'));
				htmlPre.setAttribute('class', 'brush:html;');
				editor.insertElement(htmlPre);

				// Inject Pre[css]
				var cssPre = new CKEDITOR.dom.element('pre', editor.document);
				cssPre.setText(gettext('Sample CSS Content'));
				cssPre.setAttribute('class', 'brush:css;');
				editor.insertElement(cssPre);

				// Inject Pre[js]
				var jsPre = new CKEDITOR.dom.element('pre', editor.document);
				jsPre.setText(gettext('Sample JS Content'));
				jsPre.setAttribute('class', 'brush:js;');
				editor.insertElement(jsPre);
			}
		});

		var label = 'Insert Code Sample Template';
		editor.ui.addButton('mdn-sampler', {
			label: label,
			title: label,
			className: 'cke_button_mdn_sampler',
			command: 'mdnSampler',
			iconOffset: 43,
			icon: editor.skinPath + 'icons.png'
		});
	}
});<commit_msg>Update to class format per Les<commit_after>/*
	Customized plugin added by David Walsh (:davidwalsh, dwalsh@mozilla.com)

	This plugin inserts boilerplate live sample content
*/
CKEDITOR.plugins.add('mdn-sampler', {

	requires: ['selection'],

	init: function(editor) {

		editor.addCommand('mdnSampler', {
			exec: function (editor, data) {

				// Inject heading
				var heading = new CKEDITOR.dom.element('h2', editor.document);
				heading.setText(gettext('Sample Title'));
				editor.insertElement(heading);

				// Inject Pre[html]
				var htmlPre = new CKEDITOR.dom.element('pre', editor.document);
				htmlPre.setText(gettext('Sample HTML Content'));
				htmlPre.setAttribute('class', 'brush: html');
				editor.insertElement(htmlPre);

				// Inject Pre[css]
				var cssPre = new CKEDITOR.dom.element('pre', editor.document);
				cssPre.setText(gettext('Sample CSS Content'));
				cssPre.setAttribute('class', 'brush: css');
				editor.insertElement(cssPre);

				// Inject Pre[js]
				var jsPre = new CKEDITOR.dom.element('pre', editor.document);
				jsPre.setText(gettext('Sample JS Content'));
				jsPre.setAttribute('class', 'brush: js');
				editor.insertElement(jsPre);
			}
		});

		var label = 'Insert Code Sample Template';
		editor.ui.addButton('mdn-sampler', {
			label: label,
			title: label,
			className: 'cke_button_mdn_sampler',
			command: 'mdnSampler',
			iconOffset: 43,
			icon: editor.skinPath + 'icons.png'
		});
	}
});<|endoftext|>