<commit_before>var db = require('mongoose');
var numeral = require('numeral');
var moment = require('moment');

/**
 * get
 *
 * Returns a list of all transactions
 * optionally filtering by category
 * or date.
 */

var get = function get(req, res, next) {
  var t = db.model('transactions').find();
  var category = req.params.category;
  var year = req.params.year;
  var month = req.params.month;

  if (category) t = t.where({ category: category });
  // if (year) {
  //   t = t.where({  });
  // }

  t.exec(function(err, list) {
    if (err) next(err);
    res.send(list);
  });
};

/**
 * post
 *
 * Creates a new transaction given
 * some data.
 */

var post = function post(req, res, next) {
  var description = req.body.description || 'Transaction';
  var amount = req.body.amount || 0;
  var category = req.body.category || 'Default';
  var date = req.body.date || new Date();

  db.model('transactions').create({
    description: description,
    amount: amount,
    category: category,
    date: date
  }, function(err) {
    if (err) next(err);
    res.redirect('/');
  });
};

/**
 * put
 *
 * Updates a specific transaction
 * given some data and an id.
 */

var put = function put(req, res, next) {
  var id = req.body.id;
  var description = req.body.description;
  var amount = req.body.amount;
  var category = req.body.category;
  var date = req.body.date;

  db.model('transactions').findById(id, function(err, tr) {
    if (err) next(err);

    if (description) tr.description = description.trim();
    if (amount) tr.amount = parseFloat(amount.replace('$', ''));
    if (category) tr.category = category.trim();
    if (date) tr.date = new Date(date);

    tr.save(function(err) {
      if (err) next(err);
      tr.amount = numeral(tr.amount).format('$0,0.00');
      res.send(tr);
    });
  });
};

/**
 * remove
 *
 * Deletes a specific transaction
 * given an id.
 */

var remove = function remove(req, res, next) {
  db.model('transactions').findByIdAndRemove(req.params.id, function(err) {
    if (err) next(err);
    res.send({ status: 200, message: 'Successfully removed transaction ' + req.params.id });
  });
};

/**
 * categories
 *
 * Returns a list of all distinct
 * categories of transactions.
 */

var categories = function categories(cb, next) {
  var t = db.model('transactions').find();
  t.distinct('category');
  t.exec(function(err, list) {
    if (err) next(err);
    cb(list);
  });
};

/**
 * isCategory
 *
 * Given a category and an object,
 * returns boolean of object.category
 * and category. Curried.
 *
 * @param {String} category The category to check
 * @param {Object} obj The object to check
 */

function isCategory(category) {
  return function(obj) {
    return category === obj.category;
  };
}

/**
 * render
 *
 * Renders the view for transactions.
 * By default, renders transactions for current month.
 * Can also render by category and/or year + month.
 */

var render = function render(req, res, next) {
  var category = req.params.category;
  var year = req.params.year;
  var month = req.params.month;
  var currentDate = {
    year: parseInt(year, 10),
    month: parseInt(month, 10) - 1
  };

  var date = month && year ? moment().set(currentDate) : moment();
  var dateMin = date.clone().startOf('month');
  var dateMax = date.clone().endOf('month');

  var transactions = db.model('transactions')
    .find()
    .where('date')
      .gte(dateMin.toDate())
      .lte(dateMax.toDate());

  // if (category) {
  //   transactions = transactions
  //     .where('category')
  //     .equals(category);
  // }

  transactions.exec(function(err, list) {
    if (err) next(err);

    // sort by date
    var sorted = list.sort(function(a, b) {
      if (a.date < b.date) return -1;
      if (a.date > b.date) return 1;
      return 0;
    }).reverse();

    // filter by category
    if (category) sorted = sorted.filter(isCategory(category));

    // get all unique categories
    var categories = list.map(function(t) {
      return t.category;
    }).filter(function(t, i, arr) {
      return arr.indexOf(t) === i;
    });

    // total all amounts
    var total = sorted.reduce(function(sum, t) {
      return sum += t.amount;
    }, 0);

    // total income
    var totalIn = sorted.reduce(function(sum, t) {
      if (t.amount > 0) return sum += t.amount;
      return sum;
    }, 0);

    // total expenses
    var totalOut = sorted.reduce(function(sum, t) {
      if (t.amount < 0) return sum += t.amount;
      return sum;
    }, 0);

    res.render('index', {
      viewName: 'transactions',
      transactions: sorted,
      totals: {
        all: total,
        income: totalIn,
        expenses: totalOut
      },
      category: category || '',
      categories: categories,
      date: date.format('MMMM YYYY'),
      currentDate: {
        month: date.format('MM'),
        year: date.format('YYYY')
      }
    });
  });
};

module.exports = {
  get: get,
  post: post,
  put: put,
  remove: remove,
  categories: categories,
  render: render
};
<commit_msg>category dropdown list should be sorted<commit_after>var db = require('mongoose');
var numeral = require('numeral');
var moment = require('moment');

/**
 * get
 *
 * Returns a list of all transactions
 * optionally filtering by category
 * or date.
 */

var get = function get(req, res, next) {
  var t = db.model('transactions').find();
  var category = req.params.category;
  var year = req.params.year;
  var month = req.params.month;

  if (category) t = t.where({ category: category });
  // if (year) {
  //   t = t.where({  });
  // }

  t.exec(function(err, list) {
    if (err) next(err);
    res.send(list);
  });
};

/**
 * post
 *
 * Creates a new transaction given
 * some data.
 */

var post = function post(req, res, next) {
  var description = req.body.description || 'Transaction';
  var amount = req.body.amount || 0;
  var category = req.body.category || 'Default';
  var date = req.body.date || new Date();

  db.model('transactions').create({
    description: description,
    amount: amount,
    category: category,
    date: date
  }, function(err) {
    if (err) next(err);
    res.redirect('/');
  });
};

/**
 * put
 *
 * Updates a specific transaction
 * given some data and an id.
 */

var put = function put(req, res, next) {
  var id = req.body.id;
  var description = req.body.description;
  var amount = req.body.amount;
  var category = req.body.category;
  var date = req.body.date;

  db.model('transactions').findById(id, function(err, tr) {
    if (err) next(err);

    if (description) tr.description = description.trim();
    if (amount) tr.amount = parseFloat(amount.replace('$', ''));
    if (category) tr.category = category.trim();
    if (date) tr.date = new Date(date);

    tr.save(function(err) {
      if (err) next(err);
      tr.amount = numeral(tr.amount).format('$0,0.00');
      res.send(tr);
    });
  });
};

/**
 * remove
 *
 * Deletes a specific transaction
 * given an id.
 */

var remove = function remove(req, res, next) {
  db.model('transactions').findByIdAndRemove(req.params.id, function(err) {
    if (err) next(err);
    res.send({ status: 200, message: 'Successfully removed transaction ' + req.params.id });
  });
};

/**
 * categories
 *
 * Returns a list of all distinct
 * categories of transactions.
 */

var categories = function categories(cb, next) {
  var t = db.model('transactions').find();
  t.distinct('category');
  t.exec(function(err, list) {
    if (err) next(err);
    cb(list);
  });
};

/**
 * isCategory
 *
 * Given a category and an object,
 * returns boolean of object.category
 * and category. Curried.
 *
 * @param {String} category The category to check
 * @param {Object} obj The object to check
 */

function isCategory(category) {
  return function(obj) {
    return category === obj.category;
  };
}

/**
 * render
 *
 * Renders the view for transactions.
 * By default, renders transactions for current month.
 * Can also render by category and/or year + month.
 */

var render = function render(req, res, next) {
  var category = req.params.category;
  var year = req.params.year;
  var month = req.params.month;
  var currentDate = {
    year: parseInt(year, 10),
    month: parseInt(month, 10) - 1
  };

  var date = month && year ? moment().set(currentDate) : moment();
  var dateMin = date.clone().startOf('month');
  var dateMax = date.clone().endOf('month');

  var transactions = db.model('transactions')
    .find()
    .where('date')
      .gte(dateMin.toDate())
      .lte(dateMax.toDate());

  // if (category) {
  //   transactions = transactions
  //     .where('category')
  //     .equals(category);
  // }

  transactions.exec(function(err, list) {
    if (err) next(err);

    // sort by date
    var sorted = list.sort(function(a, b) {
      if (a.date < b.date) return -1;
      if (a.date > b.date) return 1;
      return 0;
    }).reverse();

    // filter by category
    if (category) sorted = sorted.filter(isCategory(category));

    // get all unique categories
    var categories = list.map(function(t) {
      return t.category;
    }).filter(function(t, i, arr) {
      return arr.indexOf(t) === i;
    }).sort();

    // total all amounts
    var total = sorted.reduce(function(sum, t) {
      return sum += t.amount;
    }, 0);

    // total income
    var totalIn = sorted.reduce(function(sum, t) {
      if (t.amount > 0) return sum += t.amount;
      return sum;
    }, 0);

    // total expenses
    var totalOut = sorted.reduce(function(sum, t) {
      if (t.amount < 0) return sum += t.amount;
      return sum;
    }, 0);

    res.render('index', {
      viewName: 'transactions',
      transactions: sorted,
      totals: {
        all: total,
        income: totalIn,
        expenses: totalOut
      },
      category: category || '',
      categories: categories,
      date: date.format('MMMM YYYY'),
      currentDate: {
        month: date.format('MM'),
        year: date.format('YYYY')
      }
    });
  });
};

module.exports = {
  get: get,
  post: post,
  put: put,
  remove: remove,
  categories: categories,
  render: render
};
<|endoftext|>


<commit_before>{
    "filelist":[
        "/js/react/browser.min.js",
        "/js/react/react.min.js",
        "/js/react/react-dom.min.js"
      ]
}
<commit_msg>Update react.js<commit_after>{
    "tpls":[],
    "filelist":[
        "/js/react/browser.min.js",
        "/js/react/react.min.js",
        "/js/react/react-dom.min.js"
      ]
}
<|endoftext|>


<commit_before>"use strict";

const npa = require("npm-package-arg");
const semver = require("semver");

/**
 * Represents a node in a PackageGraph.
 * @constructor
 * @param {!<Package>} pkg - A Package object to build the node from.
 */
class PackageGraphNode {
  constructor(pkg) {
    Object.defineProperties(this, {
      // immutable properties
      name: {
        enumerable: true,
        value: pkg.name,
      },
      location: {
        value: pkg.location,
      },
      // properties that might change over time
      version: {
        get() {
          return pkg.version;
        },
      },
      pkg: {
        get() {
          return pkg;
        },
      },
    });

    this.externalDependencies = new Map();
    this.localDependencies = new Map();
    this.localDependents = new Map();
  }

  /**
   * Determine if the Node satisfies a resolved semver range.
   * @see https://github.com/npm/npm-package-arg#result-object
   *
   * @param {!Result} resolved npm-package-arg Result object
   * @returns {Boolean}
   */
  satisfies({ gitCommittish, gitRange, fetchSpec }) {
    return semver.satisfies(this.version, gitCommittish || gitRange || fetchSpec);
  }
}

/**
 * A PackageGraph.
 * @constructor
 * @param {!Array.<Package>} packages An array of Packages to build the graph out of.
 * @param {String} graphType ("allDependencies" or "dependencies")
 *    Pass "dependencies" to create a graph of only dependencies,
 *    excluding the devDependencies that would normally be included.
 * @param {Boolean} forceLocal Force all local dependencies to be linked.
 */
class PackageGraph extends Map {
  constructor(packages, graphType = "allDependencies", forceLocal) {
    super(packages.map(pkg => [pkg.name, new PackageGraphNode(pkg)]));

    this.forEach((currentNode, currentName) => {
      const graphDependencies =
        graphType === "dependencies"
          ? Object.assign({}, currentNode.pkg.optionalDependencies, currentNode.pkg.dependencies)
          : Object.assign(
              {},
              currentNode.pkg.devDependencies,
              currentNode.pkg.optionalDependencies,
              currentNode.pkg.dependencies
            );

      Object.keys(graphDependencies).forEach(depName => {
        const depNode = this.get(depName);
        // Yarn decided to ignore https://github.com/npm/npm/pull/15900 and implemented "link:"
        // As they apparently have no intention of being compatible, we have to do it for them.
        // @see https://github.com/yarnpkg/yarn/issues/4212
        const spec = graphDependencies[depName].replace(/^link:/, "file:");
        const resolved = npa.resolve(depName, spec, currentNode.location);

        if (!depNode) {
          // it's an external dependency, store the resolution and bail
          return currentNode.externalDependencies.set(depName, resolved);
        }

        if (forceLocal || resolved.fetchSpec === depNode.location || depNode.satisfies(resolved)) {
          // a local file: specifier OR a matching semver
          currentNode.localDependencies.set(depName, resolved);
          depNode.localDependents.set(currentName, currentNode);
        } else {
          // non-matching semver of a local dependency
          currentNode.externalDependencies.set(depName, resolved);
        }
      });
    });
  }

  /**
   * Takes a list of Packages and returns a list of those same Packages with any Packages
   * they depend on. i.e if packageA depended on packageB `graph.addDependencies([packageA])`
   * would return [packageA, packageB].
   *
   * @param {!Array.<Package>} filteredPackages The packages to include dependencies for.
   * @return {Array.<Package>} The packages with any dependencies that weren't already included.
   */
  addDependencies(filteredPackages) {
    return this.extendList(filteredPackages, "localDependencies");
  }

  /**
   * Takes a list of Packages and returns a list of those same Packages with any Packages
   * that depend on them. i.e if packageC depended on packageD `graph.addDependents([packageD])`
   * would return [packageD, packageC].
   *
   * @param {!Array.<Package>} filteredPackages The packages to include dependents for.
   * @return {Array.<Package>} The packages with any dependents that weren't already included.
   */
  addDependents(filteredPackages) {
    return this.extendList(filteredPackages, "localDependents");
  }

  /**
   * Extends a list of packages by traversing on a given property, which must refer to a
   * `PackageGraphNode` property that is a collection of `PackageGraphNode`s
   *
   * @param {!Array.<Package>} packageList The list of packages to extend
   * @param {!String} nodeProp The property on `PackageGraphNode` used to traverse
   * @return {Array.<Package>} The packages with any additional packages found by traversing
   *                           nodeProp
   */
  extendList(packageList, nodeProp) {
    // the current list of packages we are expanding using breadth-first-search
    const search = new Set(packageList.map(({ name }) => this.get(name)));

    // an intermediate list of matched PackageGraphNodes
    const result = [];

    search.forEach(currentNode => {
      // anything searched for is always a result
      result.push(currentNode);

      currentNode[nodeProp].forEach((meta, depName) => {
        const depNode = this.get(depName);

        if (depNode !== currentNode && !search.has(depNode)) {
          search.add(depNode);
        }
      });
    });

    // actual Package instances, not PackageGraphNodes
    return result.map(node => node.pkg);
  }

  /**
   * Return a tuple of cycle paths and nodes, which have been removed from the graph.
   * @returns [Set<String[]>, Set<PackageGraphNode>]
   */
  partitionCycles() {
    const cyclePaths = new Set();
    const cycleNodes = new Set();

    this.forEach((currentNode, currentName) => {
      const seen = new Set();

      const visits = walk => (dependentNode, dependentName, siblingDependents) => {
        const step = walk.concat(dependentName);

        if (seen.has(dependentNode)) {
          return;
        }

        seen.add(dependentNode);

        if (dependentNode === currentNode) {
          // a direct cycle
          cycleNodes.add(currentNode);
          cyclePaths.add(step);

          return;
        }

        if (siblingDependents.has(currentName)) {
          // a transitive cycle
          const cycleDependentName = Array.from(dependentNode.localDependencies).find(([key]) =>
            currentNode.localDependents.has(key)
          );
          const pathToCycle = step
            .slice()
            .reverse()
            .concat(cycleDependentName);

          cycleNodes.add(dependentNode);
          cyclePaths.add(pathToCycle);
        }

        dependentNode.localDependents.forEach(visits(step));
      };

      currentNode.localDependents.forEach(visits([currentName]));
    });

    if (cycleNodes.size) {
      this.prune(...cycleNodes);
    }

    return [cyclePaths, cycleNodes];
  }

  /**
   * Remove all candidate nodes.
   * @param {PackageGraphNode[]} candidates
   */
  prune(...candidates) {
    if (candidates.length === this.size) {
      return this.clear();
    }

    candidates.forEach(node => this.remove(node));
  }

  /**
   * Delete by value (instead of key), as well as removing pointers
   * to itself in the other node's internal collections.
   * @param {PackageGraphNode} candidateNode instance to remove
   */
  remove(candidateNode) {
    this.delete(candidateNode.name);

    this.forEach(node => {
      // remove incoming edges ("indegree")
      node.localDependencies.delete(candidateNode.name);

      // remove outgoing edges ("outdegree")
      node.localDependents.delete(candidateNode.name);
    });
  }
}

module.exports = PackageGraph;
<commit_msg>feat(package-graph): Add `rawPackageList` getter<commit_after>"use strict";

const npa = require("npm-package-arg");
const semver = require("semver");

/**
 * Represents a node in a PackageGraph.
 * @constructor
 * @param {!<Package>} pkg - A Package object to build the node from.
 */
class PackageGraphNode {
  constructor(pkg) {
    Object.defineProperties(this, {
      // immutable properties
      name: {
        enumerable: true,
        value: pkg.name,
      },
      location: {
        value: pkg.location,
      },
      // properties that might change over time
      version: {
        get() {
          return pkg.version;
        },
      },
      pkg: {
        get() {
          return pkg;
        },
      },
    });

    this.externalDependencies = new Map();
    this.localDependencies = new Map();
    this.localDependents = new Map();
  }

  /**
   * Determine if the Node satisfies a resolved semver range.
   * @see https://github.com/npm/npm-package-arg#result-object
   *
   * @param {!Result} resolved npm-package-arg Result object
   * @returns {Boolean}
   */
  satisfies({ gitCommittish, gitRange, fetchSpec }) {
    return semver.satisfies(this.version, gitCommittish || gitRange || fetchSpec);
  }
}

/**
 * A PackageGraph.
 * @constructor
 * @param {!Array.<Package>} packages An array of Packages to build the graph out of.
 * @param {String} graphType ("allDependencies" or "dependencies")
 *    Pass "dependencies" to create a graph of only dependencies,
 *    excluding the devDependencies that would normally be included.
 * @param {Boolean} forceLocal Force all local dependencies to be linked.
 */
class PackageGraph extends Map {
  constructor(packages, graphType = "allDependencies", forceLocal) {
    super(packages.map(pkg => [pkg.name, new PackageGraphNode(pkg)]));

    this.forEach((currentNode, currentName) => {
      const graphDependencies =
        graphType === "dependencies"
          ? Object.assign({}, currentNode.pkg.optionalDependencies, currentNode.pkg.dependencies)
          : Object.assign(
              {},
              currentNode.pkg.devDependencies,
              currentNode.pkg.optionalDependencies,
              currentNode.pkg.dependencies
            );

      Object.keys(graphDependencies).forEach(depName => {
        const depNode = this.get(depName);
        // Yarn decided to ignore https://github.com/npm/npm/pull/15900 and implemented "link:"
        // As they apparently have no intention of being compatible, we have to do it for them.
        // @see https://github.com/yarnpkg/yarn/issues/4212
        const spec = graphDependencies[depName].replace(/^link:/, "file:");
        const resolved = npa.resolve(depName, spec, currentNode.location);

        if (!depNode) {
          // it's an external dependency, store the resolution and bail
          return currentNode.externalDependencies.set(depName, resolved);
        }

        if (forceLocal || resolved.fetchSpec === depNode.location || depNode.satisfies(resolved)) {
          // a local file: specifier OR a matching semver
          currentNode.localDependencies.set(depName, resolved);
          depNode.localDependents.set(currentName, currentNode);
        } else {
          // non-matching semver of a local dependency
          currentNode.externalDependencies.set(depName, resolved);
        }
      });
    });
  }

  get rawPackageList() {
    return Array.from(this.values()).map(node => node.pkg);
  }

  /**
   * Takes a list of Packages and returns a list of those same Packages with any Packages
   * they depend on. i.e if packageA depended on packageB `graph.addDependencies([packageA])`
   * would return [packageA, packageB].
   *
   * @param {!Array.<Package>} filteredPackages The packages to include dependencies for.
   * @return {Array.<Package>} The packages with any dependencies that weren't already included.
   */
  addDependencies(filteredPackages) {
    return this.extendList(filteredPackages, "localDependencies");
  }

  /**
   * Takes a list of Packages and returns a list of those same Packages with any Packages
   * that depend on them. i.e if packageC depended on packageD `graph.addDependents([packageD])`
   * would return [packageD, packageC].
   *
   * @param {!Array.<Package>} filteredPackages The packages to include dependents for.
   * @return {Array.<Package>} The packages with any dependents that weren't already included.
   */
  addDependents(filteredPackages) {
    return this.extendList(filteredPackages, "localDependents");
  }

  /**
   * Extends a list of packages by traversing on a given property, which must refer to a
   * `PackageGraphNode` property that is a collection of `PackageGraphNode`s
   *
   * @param {!Array.<Package>} packageList The list of packages to extend
   * @param {!String} nodeProp The property on `PackageGraphNode` used to traverse
   * @return {Array.<Package>} The packages with any additional packages found by traversing
   *                           nodeProp
   */
  extendList(packageList, nodeProp) {
    // the current list of packages we are expanding using breadth-first-search
    const search = new Set(packageList.map(({ name }) => this.get(name)));

    // an intermediate list of matched PackageGraphNodes
    const result = [];

    search.forEach(currentNode => {
      // anything searched for is always a result
      result.push(currentNode);

      currentNode[nodeProp].forEach((meta, depName) => {
        const depNode = this.get(depName);

        if (depNode !== currentNode && !search.has(depNode)) {
          search.add(depNode);
        }
      });
    });

    // actual Package instances, not PackageGraphNodes
    return result.map(node => node.pkg);
  }

  /**
   * Return a tuple of cycle paths and nodes, which have been removed from the graph.
   * @returns [Set<String[]>, Set<PackageGraphNode>]
   */
  partitionCycles() {
    const cyclePaths = new Set();
    const cycleNodes = new Set();

    this.forEach((currentNode, currentName) => {
      const seen = new Set();

      const visits = walk => (dependentNode, dependentName, siblingDependents) => {
        const step = walk.concat(dependentName);

        if (seen.has(dependentNode)) {
          return;
        }

        seen.add(dependentNode);

        if (dependentNode === currentNode) {
          // a direct cycle
          cycleNodes.add(currentNode);
          cyclePaths.add(step);

          return;
        }

        if (siblingDependents.has(currentName)) {
          // a transitive cycle
          const cycleDependentName = Array.from(dependentNode.localDependencies).find(([key]) =>
            currentNode.localDependents.has(key)
          );
          const pathToCycle = step
            .slice()
            .reverse()
            .concat(cycleDependentName);

          cycleNodes.add(dependentNode);
          cyclePaths.add(pathToCycle);
        }

        dependentNode.localDependents.forEach(visits(step));
      };

      currentNode.localDependents.forEach(visits([currentName]));
    });

    if (cycleNodes.size) {
      this.prune(...cycleNodes);
    }

    return [cyclePaths, cycleNodes];
  }

  /**
   * Remove all candidate nodes.
   * @param {PackageGraphNode[]} candidates
   */
  prune(...candidates) {
    if (candidates.length === this.size) {
      return this.clear();
    }

    candidates.forEach(node => this.remove(node));
  }

  /**
   * Delete by value (instead of key), as well as removing pointers
   * to itself in the other node's internal collections.
   * @param {PackageGraphNode} candidateNode instance to remove
   */
  remove(candidateNode) {
    this.delete(candidateNode.name);

    this.forEach(node => {
      // remove incoming edges ("indegree")
      node.localDependencies.delete(candidateNode.name);

      // remove outgoing edges ("outdegree")
      node.localDependents.delete(candidateNode.name);
    });
  }
}

module.exports = PackageGraph;
<|endoftext|>


<commit_before>import builder from 'bodybuilder'
import config from '../lib/config'

/**
 * Create slugify -> "create-slugify" permalink  of text
 * @param {String} text
 */
export function slugify (text) {
  return text.toString().toLowerCase()
    .replace(/\s+/g, '-') // Replace spaces with -
    .replace(/&/g, '-and-') // Replace & with 'and'
    .replace(/[^\w-]+/g, '') // Remove all non-word chars
    .replace(/--+/g, '-') // Replace multiple - with single -
}

/**
 * Re-format category path to be suitable for breadcrumb
 * @param {Array} categoryPath
 */
export function breadCrumbRoutes (categoryPath) {
  const tmpRts = []
  for (let sc of categoryPath) {
    tmpRts.push({
      name: sc.name,
      route_link: '/c/' + sc.slug
    })
  }

  return tmpRts
}

/**
 * Return configurable product thumbnail depending on the configurable_children
 * @param {object} product
 * @param {bool} ignoreConfig
 */
export function productThumbnailPath (product, ignoreConfig = false) {
  let thumbnail = product.image
  if ((product.type_id && product.type_id === 'configurable') && product.hasOwnProperty('configurable_children') &&
    product.configurable_children.length && (ignoreConfig || !product.is_configured) &&
    ('image' in product.configurable_children[0])
  ) {
    thumbnail = product.configurable_children[0].image
    if (!thumbnail || thumbnail === 'no_selection') {
      const childWithImg = product.configurable_children.find(f => f.image && f.image !== 'no_selection')
      if (childWithImg) thumbnail = childWithImg.image
    }
  }
  return thumbnail
}

export function buildFilterProductsQuery (currentCategory, chosenFilters, defaultFilters = null) {
  let filterQr = baseFilterProductsQuery(currentCategory, defaultFilters == null ? config.products.defaultFilters : defaultFilters)
  let attrFilterBuilder = (filterQr, attrPostfix = '') => {
    for (let code of Object.keys(chosenFilters)) {
      const filter = chosenFilters[code]

      if (filter.attribute_code !== 'price') {
        filterQr = filterQr.andFilter('match', filter.attribute_code + attrPostfix, filter.id)
      } else { // multi should be possible filter here?
        const rangeqr = {}
        if (filter.from) {
          rangeqr['gte'] = filter.from
        }
        if (filter.to) {
          rangeqr['lte'] = filter.to
        }
        filterQr = filterQr.andFilter('range', filter.attribute_code, rangeqr)
      }
    }
    return filterQr
  }
  filterQr = filterQr.orFilter('bool', (b) => attrFilterBuilder(b).notFilter('match', 'type_id', 'configurable'))
    .orFilter('bool', (b) => attrFilterBuilder(b, '_options').filter('match', 'type_id', 'configurable')) // the queries can vary based on the product type
  return filterQr
}

export function baseFilterProductsQuery (parentCategory, filters = []) { // TODO add aggregation of color_options and size_options fields
  let searchProductQuery = builder().andFilter('range', 'visibility', { 'gte': 2, 'lte': 4 }/** Magento visibility in search & categories */)

  // add filters to query
  for (let attrToFilter of filters) {
    if (attrToFilter !== 'price') {
      searchProductQuery = searchProductQuery.aggregation('terms', attrToFilter)
      searchProductQuery = searchProductQuery.aggregation('terms', attrToFilter + '_options')
    } else {
      searchProductQuery = searchProductQuery.aggregation('terms', attrToFilter)
      searchProductQuery.aggregation('range', 'price', {
        ranges: [
          { from: 0, to: 50 },
          { from: 50, to: 100 },
          { from: 100, to: 150 },
          { from: 150 }
        ]
      })
    }
  }

  let childCats = [parentCategory.id]
  if (parentCategory.children_data) {
    let recurCatFinderBuilder = (category) => {
      if (!category) {
        return
      }

      if (!category.children_data) {
        return
      }

      for (let sc of category.children_data) {
        if (sc && sc.id) {
          childCats.push(sc.id)
        }
        recurCatFinderBuilder(sc)
      }
    }
    recurCatFinderBuilder(parentCategory)
  }
  searchProductQuery = searchProductQuery.filter('terms', 'category.category_id', childCats)
  return searchProductQuery
}
<commit_msg>Image fix<commit_after>import builder from 'bodybuilder'
import config from '../lib/config'

/**
 * Create slugify -> "create-slugify" permalink  of text
 * @param {String} text
 */
export function slugify (text) {
  return text.toString().toLowerCase()
    .replace(/\s+/g, '-') // Replace spaces with -
    .replace(/&/g, '-and-') // Replace & with 'and'
    .replace(/[^\w-]+/g, '') // Remove all non-word chars
    .replace(/--+/g, '-') // Replace multiple - with single -
}

/**
 * Re-format category path to be suitable for breadcrumb
 * @param {Array} categoryPath
 */
export function breadCrumbRoutes (categoryPath) {
  const tmpRts = []
  for (let sc of categoryPath) {
    tmpRts.push({
      name: sc.name,
      route_link: '/c/' + sc.slug
    })
  }

  return tmpRts
}

/**
 * Return configurable product thumbnail depending on the configurable_children
 * @param {object} product
 * @param {bool} ignoreConfig
 */
export function productThumbnailPath (product, ignoreConfig = false) {
  let thumbnail = product.image
  if ((product.type_id && product.type_id === 'configurable') && product.hasOwnProperty('configurable_children') &&
    product.configurable_children.length && (ignoreConfig || !product.is_configured) &&
    ('image' in product.configurable_children[0])
  ) {
    thumbnail = product.configurable_children[0].image
    if (!thumbnail || thumbnail === 'no_selection') {
      const childWithImg = product.configurable_children.find(f => f.image && f.image !== 'no_selection')
      if (childWithImg) {
        thumbnail = childWithImg.image
      } else {
        thumbnail = product.image
      }
    }
  }
  return thumbnail
}

export function buildFilterProductsQuery (currentCategory, chosenFilters, defaultFilters = null) {
  let filterQr = baseFilterProductsQuery(currentCategory, defaultFilters == null ? config.products.defaultFilters : defaultFilters)
  let attrFilterBuilder = (filterQr, attrPostfix = '') => {
    for (let code of Object.keys(chosenFilters)) {
      const filter = chosenFilters[code]

      if (filter.attribute_code !== 'price') {
        filterQr = filterQr.andFilter('match', filter.attribute_code + attrPostfix, filter.id)
      } else { // multi should be possible filter here?
        const rangeqr = {}
        if (filter.from) {
          rangeqr['gte'] = filter.from
        }
        if (filter.to) {
          rangeqr['lte'] = filter.to
        }
        filterQr = filterQr.andFilter('range', filter.attribute_code, rangeqr)
      }
    }
    return filterQr
  }
  filterQr = filterQr.orFilter('bool', (b) => attrFilterBuilder(b).notFilter('match', 'type_id', 'configurable'))
    .orFilter('bool', (b) => attrFilterBuilder(b, '_options').filter('match', 'type_id', 'configurable')) // the queries can vary based on the product type
  return filterQr
}

export function baseFilterProductsQuery (parentCategory, filters = []) { // TODO add aggregation of color_options and size_options fields
  let searchProductQuery = builder().andFilter('range', 'visibility', { 'gte': 2, 'lte': 4 }/** Magento visibility in search & categories */)

  // add filters to query
  for (let attrToFilter of filters) {
    if (attrToFilter !== 'price') {
      searchProductQuery = searchProductQuery.aggregation('terms', attrToFilter)
      searchProductQuery = searchProductQuery.aggregation('terms', attrToFilter + '_options')
    } else {
      searchProductQuery = searchProductQuery.aggregation('terms', attrToFilter)
      searchProductQuery.aggregation('range', 'price', {
        ranges: [
          { from: 0, to: 50 },
          { from: 50, to: 100 },
          { from: 100, to: 150 },
          { from: 150 }
        ]
      })
    }
  }

  let childCats = [parentCategory.id]
  if (parentCategory.children_data) {
    let recurCatFinderBuilder = (category) => {
      if (!category) {
        return
      }

      if (!category.children_data) {
        return
      }

      for (let sc of category.children_data) {
        if (sc && sc.id) {
          childCats.push(sc.id)
        }
        recurCatFinderBuilder(sc)
      }
    }
    recurCatFinderBuilder(parentCategory)
  }
  searchProductQuery = searchProductQuery.filter('terms', 'category.category_id', childCats)
  return searchProductQuery
}
<|endoftext|>


<commit_before>var $ = require('jquery');
var Backbone = require('backbone');
var EditorHelpers = require('builder/components/form-components/editors/editor-helpers-extend');
var _ = require('underscore');
var CustomListCollection = require('builder/components/custom-list/custom-list-collection');
var selectedItemTemplate = require('./select-item.tpl');
var CustomListItemView = require('builder/components/custom-list/custom-list-item-view');
var itemListTemplate = require('builder/components/custom-list/custom-list-item.tpl');
var template = require('./select.tpl');
var PopupManager = require('builder/components/popup-manager');
var SelectListView = require('./select-list-view');
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');

var ENTER_KEY_CODE = 13;

Backbone.Form.editors.Select = Backbone.Form.editors.Base.extend({

  tagName: 'div',
  className: 'u-ellipsis Editor-formSelect',

  events: {
    'click .js-button': '_onButtonClick',
    'keydown .js-button': '_onButtonKeyDown',
    'focus .js-button': function () {
      this.trigger('focus', this);
    },
    'blur': function () {
      this.trigger('blur', this);
    }
  },

  options: {
    selectedItemTemplate: selectedItemTemplate,
    itemListTemplate: itemListTemplate,
    customListItemView: CustomListItemView
  },

  initialize: function (opts) {
    Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
    EditorHelpers.setOptions(this, opts);

    this.template = opts.template || template;
    this.dialogMode = this.options.dialogMode || 'nested';

    if (this.options.mouseOverAction) {
      this._mouseOverAction = this.options.mouseOverAction;
    }

    if (this.options.mouseOutAction) {
      this._mouseOutAction = this.options.mouseOutAction;
    }

    if (this.options.editorAttrs && this.options.editorAttrs.help) {
      this._help = this.options.editorAttrs.help;
    }

    if (this.options.options != null) {
      this.collection = new CustomListCollection(this.options.options);
    } else {
      this.collection = this.options.collection;
    }

    if (this.collection.isAsync === undefined) {
      throw new Error('collection must implement isAsync method.');
    }

    this._initViews();

    this.setValue(this.model.get(this.options.keyAttr));

    this._initBinds();
  },

  render: function () {
    var isEmpty = !this.collection.length;
    var isNull = !this._hasValue();
    this._isDisabled = !isEmpty ? this.options.disabled : true;
    var placeholder = this._getPlaceholder(this._isDisabled);
    var label = isNull ? placeholder : this._getLabel();
    var isLoading = this._isLoading();

    this.$el.html(this.template({
      keyAttr: this.options.keyAttr,
      isEmpty: isEmpty,
      label: label,
      isDisabled: this._isDisabled,
      isNull: isNull,
      isLoading: isLoading,
      help: this._help || ''
    }));

    // we are replacing the html, so we need to re append if nested mode
    if (this.dialogMode === 'nested') {
      this._popupManager.append(this.dialogMode);
    }

    if (!isLoading) {
      this._renderSelected();
    }

    return this;
  },

  _initBinds: function () {
    var hide = function () {
      this._listView.hide();
      this._popupManager.untrack();
      this._onToggleSelected();
    }.bind(this);

    this.applyESCBind(hide);

    this.listenTo(this.collection, 'change:selected', this._onItemSelected);

    if (this.collection.isAsync()) {
      this.listenTo(this.collection.stateModel, 'change:state', this.render);
    }
  },

  _initViews: function () {
    this._listView = new SelectListView({
      collection: this.collection,
      showSearch: this.options.showSearch,
      allowFreeTextInput: this.options.allowFreeTextInput,
      typeLabel: this.options.keyAttr,
      itemTemplate: this.options.itemListTemplate,
      itemView: this.options.customListItemView,
      position: this.options.position,
      searchPlaceholder: this.options.searchPlaceholder,
      selectModel: this.options.defaultValue && this.model,
      mouseOverAction: this._mouseOverAction,
      mouseOutAction: this._mouseOutAction
    });

    this._popupManager = new PopupManager(this.cid, this.$el, this._listView.$el);
    this._popupManager.append(this.dialogMode);
  },

  _getPlaceholder: function (isDisabled) {
    var keyAttr = this.options.keyAttr;
    var placeholder;

    if (isDisabled) {
      placeholder = this.options.disabledPlaceholder || _t('components.backbone-forms.select.disabled-placeholder', { keyAttr: keyAttr });
    } else {
      placeholder = this.options.placeholder || _t('components.backbone-forms.select.placeholder', { keyAttr: keyAttr });
    }

    return placeholder;
  },

  _hasValue: function () {
    var name = this.model.get(this.options.keyAttr);
    return name != null && name !== '';
  },

  _getLabel: function () {
    var name = this.model.get(this.options.keyAttr);
    var mdl = this.collection.findWhere({val: name});
    return mdl && mdl.getName() || name || '';
  },

  _isLoading: function () {
    var isLoading = this.options.loading;

    if (this.collection.isAsync()) {
      isLoading = this.collection.stateModel.get('state') === 'fetching';
    }

    return isLoading;
  },

  _destroyBinds: function () {
    this.stopListening(this.collection);
    Backbone.Form.editors.Base.prototype._destroyBinds.call(this);
  },

  _onItemSelected: function (model) {
    this._listView.hide();
    this._popupManager.untrack();
    this._renderButton(model);
    this.trigger('change', this);
  },

  _onButtonClick: function () {
    if (this._isDisabled) {
      return;
    }

    this._listView.toggle();
    this._listView.isVisible() ? this._popupManager.track() : this._popupManager.untrack();
    this._onToggleSelected();
  },

  _onButtonKeyDown: function (event) {
    if (this._isDisabled) {
      return;
    }

    if (event.which === ENTER_KEY_CODE) {
      event.preventDefault();

      if (!this._listView.isVisible()) {
        event.stopPropagation();
        this._listView.toggle();
      } else {
        this._popupManager.track();
      }
    }
  },

  getValue: function () {
    var item = this.collection.getSelectedItem();
    if (item) {
      return item.getValue();
    } else {
      return this.value;
    }
  },

  setValue: function (value) {
    var selectedModel = this.collection.setSelected(value);
    if (selectedModel) {
      this._renderButton(selectedModel);
    } else {
      this.render();
    }
    this.value = value;
  },

  _renderSelected: function () {
    var selectedModel = this.collection.getSelectedItem();
    if (selectedModel) {
      this._renderButton(selectedModel);
    }
  },

  _getButton: function () {
    return this.$('.js-button');
  },

  _getButtonTemplateData: function (model) {
    return _.extend({}, model.attributes, { label: model.getName() });
  },

  _renderButton: function (model) {
    var button = this._getButton();
    var data = this._getButtonTemplateData(model);
    var $html = this.options.selectedItemTemplate(data);

    button
      .removeClass('is-empty')
      .html($html);

    this._initButtonBinds();

    if (this._help) {
      this._removeTooltip();

      this._helpTooltip = new TipsyTooltipView({
        el: this.$('.js-help'),
        gravity: 'w',
        title: function () {
          return $(this).data('tooltip');
        }
      });
    }

    return button;
  },

  _removeTooltip: function () {
    if (this._helpTooltip) {
      this._helpTooltip.clean();
    }
  },

  _initButtonBinds: function () {
    var button = this._getButton();

    button
      .on('mouseover', this._onMouseOver.bind(this))
      .on('mouseout', this._onMouseOut.bind(this));
  },

  _destroyButtonBinds: function () {
    var button = this._getButton();

    button
      .off('mouseover', this._onMouseOver.bind(this))
      .off('mouseout', this._onMouseOut.bind(this));
  },

  remove: function () {
    this._removeTooltip();
    this._popupManager && this._popupManager.destroy();
    this._listView && this._listView.clean();
    this._destroyButtonBinds();
    this._destroyBinds();
    Backbone.Form.editors.Base.prototype.remove.call(this);
  },

  _onToggleSelected: function () {
    var visible = this._listView.isVisible();

    this.$el.toggleClass('is-active', visible);

    if (!visible) {
      this._getButton().blur();
    }
  },

  _onMouseOver: function () {
    this._mouseOverAction && this._mouseOverAction();
  },

  _onMouseOut: function () {
    this._mouseOutAction && this._mouseOutAction();
  },

  clean: function () {
    this.$el.remove();
  }
});
<commit_msg>Add isSourceType false by default to select-view<commit_after>var $ = require('jquery');
var Backbone = require('backbone');
var EditorHelpers = require('builder/components/form-components/editors/editor-helpers-extend');
var _ = require('underscore');
var CustomListCollection = require('builder/components/custom-list/custom-list-collection');
var selectedItemTemplate = require('./select-item.tpl');
var CustomListItemView = require('builder/components/custom-list/custom-list-item-view');
var itemListTemplate = require('builder/components/custom-list/custom-list-item.tpl');
var template = require('./select.tpl');
var PopupManager = require('builder/components/popup-manager');
var SelectListView = require('./select-list-view');
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');

var ENTER_KEY_CODE = 13;

Backbone.Form.editors.Select = Backbone.Form.editors.Base.extend({

  tagName: 'div',
  className: 'u-ellipsis Editor-formSelect',

  events: {
    'click .js-button': '_onButtonClick',
    'keydown .js-button': '_onButtonKeyDown',
    'focus .js-button': function () {
      this.trigger('focus', this);
    },
    'blur': function () {
      this.trigger('blur', this);
    }
  },

  options: {
    selectedItemTemplate: selectedItemTemplate,
    itemListTemplate: itemListTemplate,
    customListItemView: CustomListItemView
  },

  initialize: function (opts) {
    Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
    EditorHelpers.setOptions(this, opts);

    this.template = opts.template || template;
    this.dialogMode = this.options.dialogMode || 'nested';

    if (this.options.mouseOverAction) {
      this._mouseOverAction = this.options.mouseOverAction;
    }

    if (this.options.mouseOutAction) {
      this._mouseOutAction = this.options.mouseOutAction;
    }

    if (this.options.editorAttrs && this.options.editorAttrs.help) {
      this._help = this.options.editorAttrs.help;
    }

    if (this.options.options != null) {
      this.collection = new CustomListCollection(this.options.options);
    } else {
      this.collection = this.options.collection;
    }

    if (this.collection.isAsync === undefined) {
      throw new Error('collection must implement isAsync method.');
    }

    this._initViews();

    this.setValue(this.model.get(this.options.keyAttr));

    this._initBinds();
  },

  render: function () {
    var isEmpty = !this.collection.length;
    var isNull = !this._hasValue();
    this._isDisabled = !isEmpty ? this.options.disabled : true;
    var placeholder = this._getPlaceholder(this._isDisabled);
    var label = isNull ? placeholder : this._getLabel();
    var isLoading = this._isLoading();

    this.$el.html(this.template({
      keyAttr: this.options.keyAttr,
      isEmpty: isEmpty,
      label: label,
      isDisabled: this._isDisabled,
      isNull: isNull,
      isLoading: isLoading,
      help: this._help || ''
    }));

    // we are replacing the html, so we need to re append if nested mode
    if (this.dialogMode === 'nested') {
      this._popupManager.append(this.dialogMode);
    }

    if (!isLoading) {
      this._renderSelected();
    }

    return this;
  },

  _initBinds: function () {
    var hide = function () {
      this._listView.hide();
      this._popupManager.untrack();
      this._onToggleSelected();
    }.bind(this);

    this.applyESCBind(hide);

    this.listenTo(this.collection, 'change:selected', this._onItemSelected);

    if (this.collection.isAsync()) {
      this.listenTo(this.collection.stateModel, 'change:state', this.render);
    }
  },

  _initViews: function () {
    this._listView = new SelectListView({
      collection: this.collection,
      showSearch: this.options.showSearch,
      allowFreeTextInput: this.options.allowFreeTextInput,
      typeLabel: this.options.keyAttr,
      itemTemplate: this.options.itemListTemplate,
      itemView: this.options.customListItemView,
      position: this.options.position,
      searchPlaceholder: this.options.searchPlaceholder,
      selectModel: this.options.defaultValue && this.model,
      mouseOverAction: this._mouseOverAction,
      mouseOutAction: this._mouseOutAction
    });

    this._popupManager = new PopupManager(this.cid, this.$el, this._listView.$el);
    this._popupManager.append(this.dialogMode);
  },

  _getPlaceholder: function (isDisabled) {
    var keyAttr = this.options.keyAttr;
    var placeholder;

    if (isDisabled) {
      placeholder = this.options.disabledPlaceholder || _t('components.backbone-forms.select.disabled-placeholder', { keyAttr: keyAttr });
    } else {
      placeholder = this.options.placeholder || _t('components.backbone-forms.select.placeholder', { keyAttr: keyAttr });
    }

    return placeholder;
  },

  _hasValue: function () {
    var name = this.model.get(this.options.keyAttr);
    return name != null && name !== '';
  },

  _getLabel: function () {
    var name = this.model.get(this.options.keyAttr);
    var mdl = this.collection.findWhere({val: name});
    return mdl && mdl.getName() || name || '';
  },

  _isLoading: function () {
    var isLoading = this.options.loading;

    if (this.collection.isAsync()) {
      isLoading = this.collection.stateModel.get('state') === 'fetching';
    }

    return isLoading;
  },

  _destroyBinds: function () {
    this.stopListening(this.collection);
    Backbone.Form.editors.Base.prototype._destroyBinds.call(this);
  },

  _onItemSelected: function (model) {
    this._listView.hide();
    this._popupManager.untrack();
    this._renderButton(model);
    this.trigger('change', this);
  },

  _onButtonClick: function () {
    if (this._isDisabled) {
      return;
    }

    this._listView.toggle();
    this._listView.isVisible() ? this._popupManager.track() : this._popupManager.untrack();
    this._onToggleSelected();
  },

  _onButtonKeyDown: function (event) {
    if (this._isDisabled) {
      return;
    }

    if (event.which === ENTER_KEY_CODE) {
      event.preventDefault();

      if (!this._listView.isVisible()) {
        event.stopPropagation();
        this._listView.toggle();
      } else {
        this._popupManager.track();
      }
    }
  },

  getValue: function () {
    var item = this.collection.getSelectedItem();
    if (item) {
      return item.getValue();
    } else {
      return this.value;
    }
  },

  setValue: function (value) {
    var selectedModel = this.collection.setSelected(value);
    if (selectedModel) {
      this._renderButton(selectedModel);
    } else {
      this.render();
    }
    this.value = value;
  },

  _renderSelected: function () {
    var selectedModel = this.collection.getSelectedItem();
    if (selectedModel) {
      this._renderButton(selectedModel);
    }
  },

  _getButton: function () {
    return this.$('.js-button');
  },

  _getButtonTemplateData: function (model) {
    return _.extend({ isSourceType: false }, model.attributes, { label: model.getName() });
  },

  _renderButton: function (model) {
    var button = this._getButton();
    var data = this._getButtonTemplateData(model);
    var $html = this.options.selectedItemTemplate(data);

    button
      .removeClass('is-empty')
      .html($html);

    this._initButtonBinds();

    if (this._help) {
      this._removeTooltip();

      this._helpTooltip = new TipsyTooltipView({
        el: this.$('.js-help'),
        gravity: 'w',
        title: function () {
          return $(this).data('tooltip');
        }
      });
    }

    return button;
  },

  _removeTooltip: function () {
    if (this._helpTooltip) {
      this._helpTooltip.clean();
    }
  },

  _initButtonBinds: function () {
    var button = this._getButton();

    button
      .on('mouseover', this._onMouseOver.bind(this))
      .on('mouseout', this._onMouseOut.bind(this));
  },

  _destroyButtonBinds: function () {
    var button = this._getButton();

    button
      .off('mouseover', this._onMouseOver.bind(this))
      .off('mouseout', this._onMouseOut.bind(this));
  },

  remove: function () {
    this._removeTooltip();
    this._popupManager && this._popupManager.destroy();
    this._listView && this._listView.clean();
    this._destroyButtonBinds();
    this._destroyBinds();
    Backbone.Form.editors.Base.prototype.remove.call(this);
  },

  _onToggleSelected: function () {
    var visible = this._listView.isVisible();

    this.$el.toggleClass('is-active', visible);

    if (!visible) {
      this._getButton().blur();
    }
  },

  _onMouseOver: function () {
    this._mouseOverAction && this._mouseOverAction();
  },

  _onMouseOut: function () {
    this._mouseOutAction && this._mouseOutAction();
  },

  clean: function () {
    this.$el.remove();
  }
});
<|endoftext|>


<commit_before>var cdb = require('cartodb.js');
var StartView = require('./start_view');
var PrivacyOptions = require('./options_collection');
var BaseDialog = require('../../views/base_dialog/view');
var randomQuote = require('../../view_helpers/random_quote');
var ViewFactory = require('../../view_factory');
var ShareView = require('./share/share_view');

/**
 * Change privacy datasets/maps dialog.
 */
var ChangePrivacyView = BaseDialog.extend({

  initialize: function() {
    this.elder('initialize');
    this._privacyOptions = PrivacyOptions.byVisAndUser(this.options.vis, this.options.user);
    this._initViews();
    this._initBinds();
  },

  /**
   * @implements cdb.ui.common.Dialog.prototype.render_content
   */
  render_content: function() {
    return this._panes.getActivePane().render().el;
  },

  ok: function() {
    var selectedOption = this._privacyOptions.selectedOption();
    if (!selectedOption.canSave()) {
      return;
    }

    var self = this;
    this._panes.active('saving');
    selectedOption.saveToVis(this.options.vis, {
      success: function() {
        self.close();
      },
      error: function() {
        self._panes.active('saveFail');
      }
    });
  },

  _initViews: function() {
    this._panes = new cdb.ui.common.TabPane({
      el: this.el
    });
    this.addView(this._panes);
    this._panes.addTab('start',
      new StartView({
        privacyOptions: this._privacyOptions,
        user: this.options.user,
        vis: this.options.vis
      })
    );
    this._panes.addTab('saving',
      ViewFactory.createByTemplate('new_common/templates/loading', {
        title: 'Saving privacy…',
        quote: randomQuote()
      }).render()
    );
    this._panes.addTab('saveFail',
      ViewFactory.createByTemplate('new_common/templates/fail', {
        msg: ''
      }).render()
    );
    this._panes.active('start');
  },

  _initBinds: function() {
    this._panes.bind('tabEnabled', this.render, this);
    this._panes.getPane('start').bind('clickedShare', this._openShareDialog, this);
  },

  _openShareDialog: function() {
    var view = new ShareView({
      clean_on_hide: true,
      enter_to_confirm: true,
      user: this.options.user,
      vis: this.options.vis,
      ChangePrivacyView: ChangePrivacyView
    });
    view.appendToBody();
    this.close();
  }
});

module.exports = ChangePrivacyView;
<commit_msg>Close privacy dialog before appending share<commit_after>var cdb = require('cartodb.js');
var StartView = require('./start_view');
var PrivacyOptions = require('./options_collection');
var BaseDialog = require('../../views/base_dialog/view');
var randomQuote = require('../../view_helpers/random_quote');
var ViewFactory = require('../../view_factory');
var ShareView = require('./share/share_view');

/**
 * Change privacy datasets/maps dialog.
 */
var ChangePrivacyView = BaseDialog.extend({

  initialize: function() {
    this.elder('initialize');
    this._privacyOptions = PrivacyOptions.byVisAndUser(this.options.vis, this.options.user);
    this._initViews();
    this._initBinds();
  },

  /**
   * @implements cdb.ui.common.Dialog.prototype.render_content
   */
  render_content: function() {
    return this._panes.getActivePane().render().el;
  },

  ok: function() {
    var selectedOption = this._privacyOptions.selectedOption();
    if (!selectedOption.canSave()) {
      return;
    }

    var self = this;
    this._panes.active('saving');
    selectedOption.saveToVis(this.options.vis, {
      success: function() {
        self.close();
      },
      error: function() {
        self._panes.active('saveFail');
      }
    });
  },

  _initViews: function() {
    this._panes = new cdb.ui.common.TabPane({
      el: this.el
    });
    this.addView(this._panes);
    this._panes.addTab('start',
      new StartView({
        privacyOptions: this._privacyOptions,
        user: this.options.user,
        vis: this.options.vis
      })
    );
    this._panes.addTab('saving',
      ViewFactory.createByTemplate('new_common/templates/loading', {
        title: 'Saving privacy…',
        quote: randomQuote()
      }).render()
    );
    this._panes.addTab('saveFail',
      ViewFactory.createByTemplate('new_common/templates/fail', {
        msg: ''
      }).render()
    );
    this._panes.active('start');
  },

  _initBinds: function() {
    this._panes.bind('tabEnabled', this.render, this);
    this._panes.getPane('start').bind('clickedShare', this._openShareDialog, this);
  },

  _openShareDialog: function() {
    var view = new ShareView({
      clean_on_hide: true,
      enter_to_confirm: true,
      user: this.options.user,
      vis: this.options.vis,
      ChangePrivacyView: ChangePrivacyView
    });

    // Order matters, close this dialog before appending the share one, for side-effects to work as expected (body.is-inDialog)
    this.close();
    view.appendToBody();
  }
});

module.exports = ChangePrivacyView;
<|endoftext|>


<commit_before>var _ = require('underscore'),
    Backbone = require('backbone'),
    chai = require('chai'),
    sinon = require('sinon'),
    sinonChai = require('sinon-chai'),
    should = chai.should(),
    syncer = require('../../shared/syncer'),
    BaseModel = require('../../shared/base/model'),
    App = require('../../shared/app');

chai.use(sinonChai);

describe('syncer', function() {

  describe('sync', function () {
    var model, options, app;

    beforeEach(function () {
      app = new App();

      model = new BaseModel({ id: 0 }, { app: app });
      model.urlRoot = '/listings';

      options = { url: model.url() };
    });

    describe('serverSync', function () {
      var request;

      beforeEach(function () {
        request = sinon.stub();
        app.req = { dataAdapter: { request: request } };

        model.api = 'foo'
      });

      it('should send a GET request with the configured dataAdapter', function () {
        var expectedRequestOptions = {
          method: 'GET',
          path: '/listings/0',
          query: {},
          api: 'foo',
          body: {}
        };

        syncer.serverSync.call(model, 'read', model, options);

        request.should.have.been.calledOnce;
        request.should.have.been.calledWith(model.app.req, expectedRequestOptions)
      });

      it('should send the correct payload on PUT or POST requests', function () {
        var expectedRequestOptions = {
          method: 'PUT',
          path: '/listings/0',
          query: {},
          api: 'foo',
          body: { id: 0, foo: 'bar', bar: 'foo' }
        };

        model.set('foo', 'bar');
        model.set('bar', 'foo');

        syncer.serverSync.call(model, 'update', model, options);

        request.should.have.been.calledOnce;
        request.should.have.been.calledWith(model.app.req, expectedRequestOptions)
      });
    });

    describe('clientSync', function () {
      var backboneSync, syncErrorHandler;

      beforeEach(function () {
        backboneSync = sinon.stub(Backbone, 'sync');
        syncErrorHandler = sinon.spy();
      });

      afterEach(function () {
        backboneSync.restore();
      });

      it('should call Backbone.sync', function () {
        syncer.clientSync.call(model, 'read', model, options);

        backboneSync.should.have.been.calledOnce;
        // Don't need to verify the options because they will be modified.
        // Test for the modification is lower.
        backboneSync.should.have.been.calledWith('read', model);
      });

      it('should get the prefixed API url', function () {
        syncer.clientSync.call(model, 'read', model, options);
        backboneSync.should.have.been.calledWithExactly('read', model, { url: '/api/-' + model.url() });
      });

      it('should wrap the error handler', function () {
        options.error = syncErrorHandler;

        syncer.clientSync.call(model, 'read', model, options);

        var error = backboneSync.lastCall.args[2].error;
        syncErrorHandler.should.be.not.equal(error);
        error.should.be.a('function');
        error.should.have.length(1);
      });

      describe('wrappedErrorHandler', function () {
        var fakeXhr;

        beforeEach(function () {
          fakeXhr = {
            responseText: '{"foo": "bar"}',
            status: 418,
            getResponseHeader: sinon.stub()
          };
          options.error = syncErrorHandler;
          backboneSync.yieldsTo('error', fakeXhr);
        });

        it('should call the original error handler with status and body', function () {
          var expectedResponse = {
            body: fakeXhr.responseText,
            status: fakeXhr.status
          };

          syncer.clientSync.call(model, 'read', model, options);

          syncErrorHandler.should.have.been.calledOnce;
          syncErrorHandler.should.have.been.calledWithExactly(expectedResponse);
        });

        it('should parse the payload if content-type is "application/json"', function () {
          var expectedResponse = {
            body: JSON.parse(fakeXhr.responseText),
            status: fakeXhr.status
          };

          fakeXhr.getResponseHeader.withArgs('content-type').returns('application/json');

          syncer.clientSync.call(model, 'read', model, options);

          syncErrorHandler.should.have.been.calledOnce;
          syncErrorHandler.should.have.been.calledWithExactly(expectedResponse);
        });
      });
    });
  });

  describe('interpolateParams', function() {
    beforeEach(function() {
      this.model = new Backbone.Model({
        id: 42
      });
    });

    it('should interpolate params', function() {
      var url;

      url = syncer.interpolateParams(this.model, '/listings/:id');
      url.should.equal('/listings/42');
    });
    it('should interpolate the same param multiple times', function() {
      var url;

      url = syncer.interpolateParams(this.model, '/special/:id/url/:id');
      url.should.equal('/special/42/url/42');
    });

    it('should delete any interpolated properties from an optional params hash', function() {
      var params, url;

      params = {
        id: 42,
        foo: 'bar'
      };
      url = syncer.interpolateParams(this.model, '/listings/:id', params);
      url.should.equal('/listings/42');
      params.should.eql({
        foo: 'bar'
      });
    });
  });

  describe('objectsDiffer', function() {

    describe('flat objects', function() {
      beforeEach(function() {
        this.obj1 = {
          foo: 'bar1',
          baz: 'bam1'
        };
        this.obj2 = _.clone(this.obj1);
      });

      it("should assert equality", function() {
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.false;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.false;
      });

      it("should work for string values", function() {
        this.obj2.foo = 'xxx';
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
      });

      it("should work for comparing obj to null", function() {
        this.obj2.foo = null;
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
      });

      it("should work for comparing obj to non-existant value", function() {
        this.obj2.other = 'something';
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
      });
    });

    describe('nested objects', function() {
      beforeEach(function() {
        this.obj1 = {
          foo: 'bar1',
          subobj: {
            nil: null,
            bool: true,
            obj: {
              harder: 'faster'
            }
          },
          arr: [1, 2, 3]
        };
        this.obj2 = _.clone(this.obj1);
      });

      it("should assert equality", function() {
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.false;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.false;
      });
    });

    describe('arrays', function() {
      it("should be equal if empty", function() {
        this.obj1 = [];
        this.obj2 = [];
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.false;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.false;
      });

      it("should be equal if non-empty", function() {
        this.obj1 = [1, 2, 3];
        this.obj2 = [1, 2, 3];
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.false;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.false;
      });

      it("should not be equal if different order", function() {
        this.obj1 = [1, 2, 3];
        this.obj2 = [1, 3, 2];
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
      });

      it("should not be equal if different values", function() {
        this.obj1 = [1, 2, 3];
        this.obj2 = [1, 2];
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
        this.obj1 = [];
        this.obj2 = [1, 3, 2];
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
      });
    });
  });

  describe('getUrl', function() {
    it("should support absolute URIs", function() {
      // HTTP
      syncer.getUrl('http://www.example.com/api/foo', true, {}).should.eql('http://www.example.com/api/foo');

      // HTTPS
      syncer.getUrl('https://www.example.com/api/foo', true, {}).should.eql('https://www.example.com/api/foo');
    });

    it("should support absolute URI with port", function() {
      syncer.getUrl('http://www.example.com:8080/api/foo').should.eql('http://www.example.com:8080/api/foo');
    });
  });

  describe('formatClientUrl', function() {
    beforeEach(function() {
      var app = new App;
      this.model = new BaseModel({}, {app: app});
    });

    it("should support default api", function() {
      this.model.formatClientUrl('/path/to/resource').should.eql('/api/-/path/to/resource');
    });

    it("should support specifying an api", function() {
      this.model.formatClientUrl('/path/to/resource', 'api-name').should.eql('/api/api-name/-/path/to/resource');
    });

    it("should support custom apiPath", function() {
      this.model.app.set('apiPath', '/foo/bar');
      this.model.formatClientUrl('/path/to/resource').should.eql('/foo/bar/-/path/to/resource');
    });
  });

});
<commit_msg>Added necessary headers object<commit_after>var _ = require('underscore'),
    Backbone = require('backbone'),
    chai = require('chai'),
    sinon = require('sinon'),
    sinonChai = require('sinon-chai'),
    should = chai.should(),
    syncer = require('../../shared/syncer'),
    BaseModel = require('../../shared/base/model'),
    App = require('../../shared/app');

chai.use(sinonChai);

describe('syncer', function() {

  describe('sync', function () {
    var model, options, app;

    beforeEach(function () {
      app = new App();

      model = new BaseModel({ id: 0 }, { app: app });
      model.urlRoot = '/listings';

      options = { url: model.url() };
    });

    describe('serverSync', function () {
      var request;

      beforeEach(function () {
        request = sinon.stub();
        app.req = { dataAdapter: { request: request } };

        model.api = 'foo'
      });

      it('should send a GET request with the configured dataAdapter', function () {
        var expectedRequestOptions = {
          method: 'GET',
          path: '/listings/0',
          query: {},
          headers: {},
          api: 'foo',
          body: {}
        };

        syncer.serverSync.call(model, 'read', model, options);

        request.should.have.been.calledOnce;
        request.should.have.been.calledWith(model.app.req, expectedRequestOptions)
      });

      it('should send the correct payload on PUT or POST requests', function () {
        var expectedRequestOptions = {
          method: 'PUT',
          path: '/listings/0',
          query: {},
          headers: {},
          api: 'foo',
          body: { id: 0, foo: 'bar', bar: 'foo' }
        };

        model.set('foo', 'bar');
        model.set('bar', 'foo');

        syncer.serverSync.call(model, 'update', model, options);

        request.should.have.been.calledOnce;
        request.should.have.been.calledWith(model.app.req, expectedRequestOptions)
      });
    });

    describe('clientSync', function () {
      var backboneSync, syncErrorHandler;

      beforeEach(function () {
        backboneSync = sinon.stub(Backbone, 'sync');
        syncErrorHandler = sinon.spy();
      });

      afterEach(function () {
        backboneSync.restore();
      });

      it('should call Backbone.sync', function () {
        syncer.clientSync.call(model, 'read', model, options);

        backboneSync.should.have.been.calledOnce;
        // Don't need to verify the options because they will be modified.
        // Test for the modification is lower.
        backboneSync.should.have.been.calledWith('read', model);
      });

      it('should get the prefixed API url', function () {
        syncer.clientSync.call(model, 'read', model, options);
        backboneSync.should.have.been.calledWithExactly('read', model, { url: '/api/-' + model.url() });
      });

      it('should wrap the error handler', function () {
        options.error = syncErrorHandler;

        syncer.clientSync.call(model, 'read', model, options);

        var error = backboneSync.lastCall.args[2].error;
        syncErrorHandler.should.be.not.equal(error);
        error.should.be.a('function');
        error.should.have.length(1);
      });

      describe('wrappedErrorHandler', function () {
        var fakeXhr;

        beforeEach(function () {
          fakeXhr = {
            responseText: '{"foo": "bar"}',
            status: 418,
            getResponseHeader: sinon.stub()
          };
          options.error = syncErrorHandler;
          backboneSync.yieldsTo('error', fakeXhr);
        });

        it('should call the original error handler with status and body', function () {
          var expectedResponse = {
            body: fakeXhr.responseText,
            status: fakeXhr.status
          };

          syncer.clientSync.call(model, 'read', model, options);

          syncErrorHandler.should.have.been.calledOnce;
          syncErrorHandler.should.have.been.calledWithExactly(expectedResponse);
        });

        it('should parse the payload if content-type is "application/json"', function () {
          var expectedResponse = {
            body: JSON.parse(fakeXhr.responseText),
            status: fakeXhr.status
          };

          fakeXhr.getResponseHeader.withArgs('content-type').returns('application/json');

          syncer.clientSync.call(model, 'read', model, options);

          syncErrorHandler.should.have.been.calledOnce;
          syncErrorHandler.should.have.been.calledWithExactly(expectedResponse);
        });
      });
    });
  });

  describe('interpolateParams', function() {
    beforeEach(function() {
      this.model = new Backbone.Model({
        id: 42
      });
    });

    it('should interpolate params', function() {
      var url;

      url = syncer.interpolateParams(this.model, '/listings/:id');
      url.should.equal('/listings/42');
    });
    it('should interpolate the same param multiple times', function() {
      var url;

      url = syncer.interpolateParams(this.model, '/special/:id/url/:id');
      url.should.equal('/special/42/url/42');
    });

    it('should delete any interpolated properties from an optional params hash', function() {
      var params, url;

      params = {
        id: 42,
        foo: 'bar'
      };
      url = syncer.interpolateParams(this.model, '/listings/:id', params);
      url.should.equal('/listings/42');
      params.should.eql({
        foo: 'bar'
      });
    });
  });

  describe('objectsDiffer', function() {

    describe('flat objects', function() {
      beforeEach(function() {
        this.obj1 = {
          foo: 'bar1',
          baz: 'bam1'
        };
        this.obj2 = _.clone(this.obj1);
      });

      it("should assert equality", function() {
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.false;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.false;
      });

      it("should work for string values", function() {
        this.obj2.foo = 'xxx';
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
      });

      it("should work for comparing obj to null", function() {
        this.obj2.foo = null;
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
      });

      it("should work for comparing obj to non-existant value", function() {
        this.obj2.other = 'something';
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
      });
    });

    describe('nested objects', function() {
      beforeEach(function() {
        this.obj1 = {
          foo: 'bar1',
          subobj: {
            nil: null,
            bool: true,
            obj: {
              harder: 'faster'
            }
          },
          arr: [1, 2, 3]
        };
        this.obj2 = _.clone(this.obj1);
      });

      it("should assert equality", function() {
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.false;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.false;
      });
    });

    describe('arrays', function() {
      it("should be equal if empty", function() {
        this.obj1 = [];
        this.obj2 = [];
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.false;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.false;
      });

      it("should be equal if non-empty", function() {
        this.obj1 = [1, 2, 3];
        this.obj2 = [1, 2, 3];
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.false;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.false;
      });

      it("should not be equal if different order", function() {
        this.obj1 = [1, 2, 3];
        this.obj2 = [1, 3, 2];
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
      });

      it("should not be equal if different values", function() {
        this.obj1 = [1, 2, 3];
        this.obj2 = [1, 2];
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
        this.obj1 = [];
        this.obj2 = [1, 3, 2];
        syncer.objectsDiffer(this.obj1, this.obj2).should.be.true;
        syncer.objectsDiffer(this.obj2, this.obj1).should.be.true;
      });
    });
  });

  describe('getUrl', function() {
    it("should support absolute URIs", function() {
      // HTTP
      syncer.getUrl('http://www.example.com/api/foo', true, {}).should.eql('http://www.example.com/api/foo');

      // HTTPS
      syncer.getUrl('https://www.example.com/api/foo', true, {}).should.eql('https://www.example.com/api/foo');
    });

    it("should support absolute URI with port", function() {
      syncer.getUrl('http://www.example.com:8080/api/foo').should.eql('http://www.example.com:8080/api/foo');
    });
  });

  describe('formatClientUrl', function() {
    beforeEach(function() {
      var app = new App;
      this.model = new BaseModel({}, {app: app});
    });

    it("should support default api", function() {
      this.model.formatClientUrl('/path/to/resource').should.eql('/api/-/path/to/resource');
    });

    it("should support specifying an api", function() {
      this.model.formatClientUrl('/path/to/resource', 'api-name').should.eql('/api/api-name/-/path/to/resource');
    });

    it("should support custom apiPath", function() {
      this.model.app.set('apiPath', '/foo/bar');
      this.model.formatClientUrl('/path/to/resource').should.eql('/foo/bar/-/path/to/resource');
    });
  });

});
<|endoftext|>


<commit_before>var caseToCassSchema = "http://schema.cassproject.org/0.3/case2cass";

var caseInterface = {
	CFDocuments: function () {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFDocuments");
	},
	CFDocument: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFDocuments/" + sourcedId);
	},
	CFPackages: function (sourcedId) {
		return httpGet(debug(this.targetUrl + "/ims/case/v1p0/CFPackages/" + sourcedId));
	},
	CFItems: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFItems/" + sourcedId);
	},
	CFAssociations: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFAssociations/" + sourcedId);
	},
	CFItemAssociations: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFItemAssociations/" + sourcedId);
	},
	CFAssociationGroupings: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFAssociationGroupings/" + sourcedId);
	},
	CFConcepts: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFConcepts/" + sourcedId);
	},
	CFItemTypes: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFItemTypes/" + sourcedId);
	},
	CFLicenses: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFLicenses/" + sourcedId);
	},
	CFSubjects: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFSubjects/" + sourcedId);
	},
	CFRubrics: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFRubrics/" + sourcedId);
	}
}

stripCassNamespace = function(o){
	if (o["@context"].indexOf("http://schema.cassproject.org") == -1)
		return o;
	var keys = EcObject.keys(o);
	for (var i = 0;i < keys.length;i++)
	{
		var k = keys[i];
		if (k.indexOf("cass:")==0){
			o[k.replace("cass:","")] = o[k];
			delete o[k];
		}
	}
	return o;
}

convertCFDocumentToFramework = function(document){
	var f = new EcFramework();
	f.assignId(document.identifier);
	if (document.subjectURI != null)
	for (var j = 0;j < document.subjectURI.length;j++){
		document.subjectURI[j]["@context"] = caseToCassSchema;
		document.subjectURI[j]["@type"] = "reference";
		document.subjectURI[j]["schema:alignmentType"]="educationalSubject";
	}
	document["@type"] = "CFDocument";
	document["@context"] = caseToCassSchema;
	document = jsonLdExpand(JSON.stringify(document));
	document = jsonLdCompact(JSON.stringify(document),"http://schema.cassproject.org/0.3");
	f.copyFrom(document);
	return f;
}

convertCFAssociationIntoRelation = function(a){
	a["@type"] = "CFAssociation";
	a["@context"] = caseToCassSchema;
	a.originNodeURI = a.originNodeURI.uri;
	a.destinationNodeURI = a.destinationNodeURI.uri;
	delete a.CFDocumentURI;
	a = jsonLdExpand(JSON.stringify(a));
	a = jsonLdCompact(JSON.stringify(a),"http://schema.cassproject.org/0.3");
	a.relationType = {"exactMatchOf":Relation.IS_EQUIVALENT_TO,"isChildOf":Relation.NARROWS}[a.relationType];
	a = stripCassNamespace(a);
	var r = new EcAlignment();
	r.copyFrom(a);
	return r;
}

convertCFItemIntoCompetency = function(a){
	a["@type"] = "CFItem";
	a["@context"] = caseToCassSchema;
	delete a.CFDocumentURI;
	if (a.educationLevel != null)
	for (var i = 0;i < a.educationLevel.length;i++)
	{
		if (!EcObject.isObject(a.educationLevel[i]))
			a.educationLevel[i]={
				"@type":"reference",
				"@context":caseToCassSchema,
				"schema:alignmentType":"educationalLevel",
				"schema:targetName":a.educationLevel[i]
			};
	}
	a = jsonLdExpand(JSON.stringify(a));
	a = jsonLdCompact(JSON.stringify(a),"http://schema.cassproject.org/0.3");
	var r = new EcCompetency();
	r.copyFrom(a);
	return r;
}

//Returns array of objects to save.
embedCFPackageIntoFramework = function(f,document){
	var p = httpGet(document.CFPackageURI.uri);
	var results = [f];
	var i = 0;
	for (i = 0;i < p.CFAssociations.length;i++)
	{
		var relation = convertCFAssociationIntoRelation(p.CFAssociations[i]);
		results.push(relation);
		var internalId = stringToHex(md5(relation.id));
		var version = date(relation["schema:dateModified"],null,true);
		var r = new EcAlignment();
		r.assignIdAndVersion(repoEndpoint(),internalId,version);
		p.CFAssociations[i] = r.id;
	}
	f.relation = p.CFAssociations;
	for (i = 0;i < p.CFItems.length;i++)
	{
		var competency = convertCFItemIntoCompetency(p.CFItems[i]);
		results.push(competency);
		var internalId = stringToHex(md5(competency.id));
		var version = date(competency["schema:dateModified"],null,true);
		var r = new EcCompetency();
		r.assignIdAndVersion(repoEndpoint(),internalId,version);
		p.CFItems[i] = r.id;
	}
	f.competency = p.CFItems;
	return results;
}

ingestCase = function () {
	var targetUrl = this.params.caseEndpoint;
	if (targetUrl == null)
		return "Please specify caseEndpoint = <target server> -- we'll add the /ims/case/v1p0.";
	this.targetUrl = targetUrl;
	var documents = caseInterface.CFDocuments.call(this);
	var dx;
	for (var i = 0;i < documents.CFDocuments.length;i++)
	{
	    var document = documents.CFDocuments[i];
	    var f = convertCFDocumentToFramework(document);
	    var listToSave = embedCFPackageIntoFramework(f,document);
	    for (var j = 0;j < listToSave.length;j++)
		{
			var internalId = stringToHex(md5(listToSave[j].id));
			var version = date(listToSave[j]["schema:dateModified"],null,true);
			skyrepoPut.call(this,{obj:listToSave[j].toJson(),type:listToSave[j].getFullType().replace("http://", "").replaceAll("/", "."),id:internalId,version:version});
		}
	}
	return JSON.stringify(dx,null,2);
}
bindWebService("/ims/case/harvest", ingestCase);
<commit_msg>Updates based on errors harvesting CASE frameworks.<commit_after>var caseToCassSchema = "http://schema.cassproject.org/0.3/case2cass";

var caseInterface = {
	CFDocuments: function () {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFDocuments");
	},
	CFDocument: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFDocuments/" + sourcedId);
	},
	CFPackages: function (sourcedId) {
		return httpGet(debug(this.targetUrl + "/ims/case/v1p0/CFPackages/" + sourcedId));
	},
	CFItems: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFItems/" + sourcedId);
	},
	CFAssociations: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFAssociations/" + sourcedId);
	},
	CFItemAssociations: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFItemAssociations/" + sourcedId);
	},
	CFAssociationGroupings: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFAssociationGroupings/" + sourcedId);
	},
	CFConcepts: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFConcepts/" + sourcedId);
	},
	CFItemTypes: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFItemTypes/" + sourcedId);
	},
	CFLicenses: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFLicenses/" + sourcedId);
	},
	CFSubjects: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFSubjects/" + sourcedId);
	},
	CFRubrics: function (sourcedId) {
		return httpGet(this.targetUrl + "/ims/case/v1p0/CFRubrics/" + sourcedId);
	}
}

stripCassNamespace = function(o){
	if (o["@context"].indexOf("http://schema.cassproject.org") == -1)
		return o;
	var keys = EcObject.keys(o);
	for (var i = 0;i < keys.length;i++)
	{
		var k = keys[i];
		if (k.indexOf("cass:")==0){
			o[k.replace("cass:","")] = o[k];
			delete o[k];
		}
	}
	return o;
}

convertCFDocumentToFramework = function(document){
	var f = new EcFramework();
	f.assignId(document.identifier);
	if (document.subjectURI != null)
	for (var j = 0;j < document.subjectURI.length;j++){
		document.subjectURI[j]["@context"] = caseToCassSchema;
		document.subjectURI[j]["@type"] = "reference";
		document.subjectURI[j]["schema:alignmentType"]="educationalSubject";
	}
	document["@type"] = "CFDocument";
	document["@context"] = caseToCassSchema;
	document = jsonLdExpand(JSON.stringify(document));
	document = jsonLdCompact(JSON.stringify(document),"http://schema.cassproject.org/0.3");
	f.copyFrom(document);
	return f;
}

convertCFAssociationIntoRelation = function(a){
	a["@type"] = "CFAssociation";
	a["@context"] = caseToCassSchema;
	a.originNodeURI = a.originNodeURI.uri;
	a.destinationNodeURI = a.destinationNodeURI.uri;
	delete a.CFDocumentURI;
	a = jsonLdExpand(JSON.stringify(a));
	a = jsonLdCompact(JSON.stringify(a),"http://schema.cassproject.org/0.3");
	a.relationType = {"exactMatchOf":Relation.IS_EQUIVALENT_TO,"isChildOf":Relation.NARROWS}[a.relationType];
	a = stripCassNamespace(a);
	var r = new EcAlignment();
	r.copyFrom(a);
	return r;
}

convertCFItemIntoCompetency = function(a){
	a["@type"] = "CFItem";
	a["@context"] = caseToCassSchema;
	delete a.CFDocumentURI;
	if (a.educationLevel != null)
	for (var i = 0;i < a.educationLevel.length;i++)
	{
		if (!EcObject.isObject(a.educationLevel[i]))
			a.educationLevel[i]={
				"@type":"reference",
				"@context":caseToCassSchema,
				"schema:alignmentType":"educationalLevel",
				"schema:targetName":a.educationLevel[i]
			};
	}
	a = jsonLdExpand(JSON.stringify(a));
	a = jsonLdCompact(JSON.stringify(a),"http://schema.cassproject.org/0.3");
	var r = new EcCompetency();
	r.copyFrom(a);
	return r;
}

//Returns array of objects to save.
embedCFPackageIntoFramework = function(f,document){
	var p = httpGet(document.CFPackageURI.uri);
	var results = [f];
	var i = 0;
	if (p.CFAssociations != null)
	for (i = 0;i < p.CFAssociations.length;i++)
	{
		var relation = convertCFAssociationIntoRelation(p.CFAssociations[i]);
		results.push(relation);
		var internalId = stringToHex(md5(relation.id));
		var version = date(relation["schema:dateModified"],null,true);
		var r = new EcAlignment();
		r.assignIdAndVersion(repoEndpoint(),internalId,version);
		p.CFAssociations[i] = r.id;
	}
	f.relation = p.CFAssociations;
    if (p.CFItems != null)
	for (i = 0;i < p.CFItems.length;i++)
	{
		var competency = convertCFItemIntoCompetency(p.CFItems[i]);
		results.push(competency);
		var internalId = stringToHex(md5(competency.id));
		var version = date(competency["schema:dateModified"],null,true);
		var r = new EcCompetency();
		r.assignIdAndVersion(repoEndpoint(),internalId,version);
		p.CFItems[i] = r.id;
	}
	f.competency = p.CFItems;
	return results;
}

ingestCase = function () {
	var targetUrl = this.params.caseEndpoint;
	if (targetUrl == null)
		return "Please specify caseEndpoint = <target server> -- we'll add the /ims/case/v1p0.";
	this.targetUrl = targetUrl;
	var documents = caseInterface.CFDocuments.call(this);
	var dx;
	for (var i = 0;i < documents.CFDocuments.length;i++)
	{
	    var document = documents.CFDocuments[i];
	    var f = convertCFDocumentToFramework(document);
	    var listToSave = embedCFPackageIntoFramework(f,document);
	    for (var j = 0;j < listToSave.length;j++)
		{
			var internalId = stringToHex(md5(listToSave[j].id));
			var version = date(listToSave[j]["schema:dateModified"],null,true);
			skyrepoPut.call(this,{obj:listToSave[j].toJson(),type:listToSave[j].getFullType().replace("http://", "").replaceAll("/", "."),id:internalId,version:version});
		}
	}
	return JSON.stringify(dx,null,2);
}
bindWebService("/ims/case/harvest", ingestCase);
<|endoftext|>


<commit_before>import path from 'path';
import assert from 'assert';
import Sassaby from 'sassaby';

describe('spacing/_generators.scss', () => {
  const file = path.resolve(__dirname, '../elegant-sass-things/spacing/_generators.scss');

  describe('generate-elegant-spacing-margin|padding-classes', () => {
    const sassaby = new Sassaby(file, {
      variables: {
        'elegant-spacing': `(
          unit: 1rem,
          multiples: (1 2)
        )`
      },
      dependencies: [
        path.resolve(__dirname, '../elegant-sass-things/spacing/_abstracts.scss')
      ]
    });

    ['margin', 'padding'].forEach((rule) => {
      [1, 2].forEach((multiple) => {
        it(`should generate ${rule} selector for multiple ${multiple}`, () => {
          sassaby.standaloneMixin(`generate-elegant-spacing-${rule}-classes`).calledWithArgs().createsSelector(`.u-${rule}--${multiple}`);
          sassaby.standaloneMixin(`generate-elegant-spacing-${rule}-classes`).calledWithArgs().calls(`${rule}(${multiple})`);
        });

        ['top', 'right', 'bottom', 'left', 'vertical', 'horizontal'].forEach((direction) => {
          it(`should generate ${rule} ${direction} selector for multiple $multiple`, () => {
            // Capitalize direction to build utilityName... e.g. marginTop
            let utilityName = rule + direction.charAt(0).toUpperCase() + direction.slice(1);
            sassaby.standaloneMixin(`generate-elegant-spacing-${rule}-classes`).calledWithArgs().createsSelector(`.u-${utilityName}--${multiple}`);
            sassaby.standaloneMixin(`generate-elegant-spacing-${rule}-classes`).calledWithArgs().calls(`${rule}-${direction}(${multiple})`);
          })
        });
      });
    });
  });

});
<commit_msg>Fix tests<commit_after>import path from 'path';
import assert from 'assert';
import Sassaby from 'sassaby';

describe('spacing/_generators.scss', () => {
  const file = path.resolve(__dirname, '../elegant-sass-things/spacing/_generators.scss');

  describe('generate-elegant-spacing-margin|padding-classes', () => {
    const sassaby = new Sassaby(file, {
      variables: {
        'elegant-spacing': `(
          unit: 1rem,
          multiples: (1 2)
        )`
      },
      dependencies: [
        path.resolve(__dirname, '../elegant-sass-things/spacing/_abstracts.scss')
      ]
    });

    ['margin', 'padding'].forEach((rule) => {
      let ruleAbbreviation = rule.charAt(0);

      [1, 2].forEach((multiple) => {
        it(`should generate ${rule} selector for multiple ${multiple}`, () => {
          sassaby.standaloneMixin(`generate-elegant-spacing-${rule}-classes`).calledWithArgs().createsSelector(`.${ruleAbbreviation}-${multiple}`);
          sassaby.standaloneMixin(`generate-elegant-spacing-${rule}-classes`).calledWithArgs().calls(`${rule}(${multiple})`);
        });

        ['top', 'right', 'bottom', 'left', 'vertical', 'horizontal'].forEach((direction) => {
          let directionAbbreviation = direction.charAt(0);

          it(`should generate ${rule} ${direction} selector for multiple ${multiple}`, () => {
            sassaby.standaloneMixin(`generate-elegant-spacing-${rule}-classes`).calledWithArgs().createsSelector(`.${ruleAbbreviation}${directionAbbreviation}-${multiple}`);
            sassaby.standaloneMixin(`generate-elegant-spacing-${rule}-classes`).calledWithArgs().calls(`${rule}-${direction}(${multiple})`);
          })
        });
      });
    });
  });

});
<|endoftext|>