<commit_before>/*

Assumes that it will run on dom:loaded

Example Markup structure:

      My Section Title

Stuff to be shown when expanded.

*/
var CanFireEvents = Class.create({
  initialize: function(name){
    this.name = name;
  },
  fireEvent: function (state, memo) {
    console.log(this.name + ':' + state, memo);
    document.fire(this.name + ':' + state, memo);
  }
});

var CanBeDisabled = Class.create(CanFireEvents, {
  initialize: function ($super, name, element, memo, disabled) {
    this.disabled = disabled || false;
    this.elementToBeDisabled = element;
    $super(name, memo);
    if (this.disabled) this.disable();
  },
  disable: function () {
    this.disabled = true;
    this.elementToBeDisabled.addClassName('disabled');
    this.fireEvent('disabled', { el: this.elementToBeDisabled });
  },
  enable: function() {
    this.disabled = false;
    this.elementToBeDisabled.removeClassName('disabled');
    this.fireEvent('enabled', { el: this.elementToBeDisabled });
  },
  toggleDisabled: function(){
    if (this.disabled) this.enable();
    else this.disable();
  }
});

var Accordion = Class.create(CanBeDisabled, {
  initialize: function($super, id, options){
    this.id = id;
    this.root = $(id);
    if (!this.root) return false;
    this.options = Object.extend({
      cancelEvent: true,
      classNames: { section: 'section', title: 'title', toggle: 'toggle', expanded: 'expanded' },
      mutuallyExclusive: true,
      effectDuration: .3,
      disabled: false
    }, options || {});

    console.log(this.options.disabled);
    $super(this.id, this.root, this.options.disabled);

    this.accordionEffectOptions = $H({
      duration: this.options.effectDuration,
      queue: { position: 'end', limit: 1, scope: id }
    });
    this.elements = {
      sections: $$('#' + id + ' .' + this.options.classNames.section),
      titles:   $$('#' + id + ' .' + this.options.classNames.title),
      toggles:  $$('#' + id + ' .' + this.options.classNames.toggle)
    };
    this.activeSection = false;
    this.setupAccordionSections();
    this.fireEvent('initialized');
  },
  setupAccordionSections: function(){
    this.sections = [];
    for (var i=0; i < this.elements.sections.length; i++) {
      this.sections.push(new AccordionSection(i, this));
    };

    this.root.observe('click', this.rootObserver.bind(this));
  },
  rootObserver: function(e){
    var el = e.element();
    if (el.up('.' + this.options.classNames.title)) el = el.up('.' + this.options.classNames.title);

    if (el.match('.' + this.options.classNames.title)) {

      if (!this.disabled) this.fireEvent('clicked');

      if (this.options.cancelEvent) e.stop();

      el.blur();

      if (this.disabled) return;

      var comingSection = this.sections.find(function(section){
        return !section.visible && section.elements.title == el;
      });
      var goingSection = this.sections.find(function(section){
        return section.visible;
      });

      if (comingSection && goingSection && this.options.mutuallyExclusive){
        this.showAnotherSection(comingSection, goingSection);
      } else {
        if (comingSection) this.showSection(comingSection);
        else {
          for (var i = this.sections.length - 1; i >= 0; i--){
            if (this.sections[i].elements.title == el) goingSection = this.sections[i];
          };
          this.hideSection(goingSection);
        }
      }
    }
  },
  showAnotherSection: function(comingSection, goingSection){
    if (comingSection.disabled || goingSection.disabled) return;
    new Effect.Parallel([
      new Effect.BlindDown(comingSection.elements.toggle, { sync: true }),
      new Effect.BlindUp(goingSection.elements.toggle, { sync: true })
    ],
    this.accordionEffectOptions.merge({
      afterFinish: function(){
        goingSection.setHidden();
        comingSection.setVisible();
      }
    }).toObject());
  },
  hideSection: function(goingSection){
    if (goingSection.disabled) return;
    goingSection.elements.toggle.blindUp(
      this.accordionEffectOptions.merge({
        afterFinish: goingSection.setHidden.bind(goingSection)
      }).toObject()
    );
  },
  showSection: function(comingSection){
    if (comingSection.disabled) return;
    comingSection.elements.toggle.blindDown(
      this.accordionEffectOptions.merge({
        afterFinish: comingSection.setVisible.bind(comingSection)
      }).toObject()
    );
  },
  fireEvent: function($super, state){
    $super(state, { accordion: this });
  }
});

var AccordionSection = Class.create(CanBeDisabled, {
  initialize: function($super, i, accordion){
    this.index = i;
    this.accordion = accordion;
    this.classNames = accordion.options.classNames;
    this.elements = {
      section: accordion.elements.sections[i],
      title:   accordion.elements.titles[i],
      toggle:  accordion.elements.toggles[i]
    };
    this.elements.toggle.setStyle({ height: this.elements.toggle.getHeight() + 'px' }).hide();
    this.visible = false;
  },

  setHidden: function(){
    if (this.elements.section.hasClassName(this.classNames.expanded))
      this.elements.section.removeClassName(this.classNames.expanded);
    this.visible = false;
    this.fireEvent('hidden');
  },

  setVisible: function(){
    this.elements.section.addClassName(this.classNames.expanded);
    this.visible = true;
    this.accordion.activeSection = this;
    this.fireEvent('shown');
  },
  fireEvent: function($super, state){
    $super('Section:' + state, { accordion: this.accordion, section: this });
  }
});
<commit_msg>Using multiple classes now.<commit_after>/*

Assumes that it will run on dom:loaded

Example Markup structure:

      My Section Title

Stuff to be shown when expanded.

*/
var CanFireEvents = Class.create({
  initialize: function(name){
    this.name = name;
  },
  fireEvent: function (state, memo) {
    //console.log(this.name + ':' + state, memo);
    document.fire(this.name + ':' + state, memo);
  }
});

var CanBeDisabled = Class.create(CanFireEvents, {
  initialize: function ($super, name, element, memo, disabled) {
    this.disabled = disabled || false;
    this.elementToBeDisabled = element;
    $super(name, memo);
    if (this.disabled) this.disable();
  },
  disable: function () {
    this.disabled = true;
    this.elementToBeDisabled.addClassName('disabled');
    this.fireEvent('disabled', { el: this.elementToBeDisabled });
  },
  enable: function() {
    this.disabled = false;
    this.elementToBeDisabled.removeClassName('disabled');
    this.fireEvent('enabled', { el: this.elementToBeDisabled });
  },
  toggleDisabled: function(){
    if (this.disabled) this.enable();
    else this.disable();
  }
});

var Accordion = Class.create(CanBeDisabled, {
  initialize: function($super, id, options){
    this.id = id;
    this.root = $(id);
    if (!this.root) return false;
    this.options = Object.extend({
      cancelEvent: true,
      classNames: { section: 'section', title: 'title', toggle: 'toggle', expanded: 'expanded' },
      mutuallyExclusive: true,
      effectDuration: .3,
      disabled: false
    }, options || {});

    $super(this.id, this.root, this.options.disabled);

    this.accordionEffectOptions = $H({
      duration: this.options.effectDuration,
      queue: { position: 'end', limit: 1, scope: id }
    });
    this.elements = {
      sections: $$('#' + id + ' .' + this.options.classNames.section),
      titles:   $$('#' + id + ' .' + this.options.classNames.title),
      toggles:  $$('#' + id + ' .' + this.options.classNames.toggle)
    };
    this.activeSection = false;
    this.setupAccordionSections();
    this.fireEvent('initialized');
  },
  setupAccordionSections: function(){
    this.sections = [];
    for (var i=0; i < this.elements.sections.length; i++) {
      this.sections.push(new AccordionSection(i, this));
    };

    this.root.observe('click', this.rootObserver.bind(this));
  },
  rootObserver: function(e){
    var el = e.element();
    if (el.up('.' + this.options.classNames.title)) el = el.up('.' + this.options.classNames.title);

    if (el.match('.' + this.options.classNames.title)) {

      if (!this.disabled) this.fireEvent('clicked');

      if (this.options.cancelEvent) e.stop();

      el.blur();

      if (this.disabled) return;

      var comingSection = this.sections.find(function(section){
        return !section.visible && section.elements.title == el;
      });
      var goingSection = this.sections.find(function(section){
        return section.visible;
      });

      if (comingSection && goingSection && this.options.mutuallyExclusive){
        this.showAnotherSection(comingSection, goingSection);
      } else {
        if (comingSection) this.showSection(comingSection);
        else {
          for (var i = this.sections.length - 1; i >= 0; i--){
            if (this.sections[i].elements.title == el) goingSection = this.sections[i];
          };
          this.hideSection(goingSection);
        }
      }
    }
  },
  showAnotherSection: function(comingSection, goingSection){
    if (comingSection.disabled || goingSection.disabled) return;
    new Effect.Parallel([
      new Effect.BlindDown(comingSection.elements.toggle, { sync: true }),
      new Effect.BlindUp(goingSection.elements.toggle, { sync: true })
    ],
    this.accordionEffectOptions.merge({
      afterFinish: function(){
        goingSection.setHidden();
        comingSection.setVisible();
      }
    }).toObject());
  },
  hideSection: function(goingSection){
    if (goingSection.disabled) return;
    goingSection.elements.toggle.blindUp(
      this.accordionEffectOptions.merge({
        afterFinish: goingSection.setHidden.bind(goingSection)
      }).toObject()
    );
  },
  showSection: function(comingSection){
    if (comingSection.disabled) return;
    comingSection.elements.toggle.blindDown(
      this.accordionEffectOptions.merge({
        afterFinish: comingSection.setVisible.bind(comingSection)
      }).toObject()
    );
  },
  fireEvent: function($super, state){
    $super(state, { accordion: this });
  }
});

var AccordionSection = Class.create(CanBeDisabled, {
  initialize: function($super, i, accordion){
    this.index = i;
    this.accordion = accordion;
    this.classNames = accordion.options.classNames;
    this.elements = {
      section: accordion.elements.sections[i],
      title:   accordion.elements.titles[i],
      toggle:  accordion.elements.toggles[i]
    };
    $super(this.accordion.id + 'Section', this.elements.section);
    this.elements.toggle.setStyle({ height: this.elements.toggle.getHeight() + 'px' }).hide();
    this.visible = false;
  },

  setHidden: function(){
    if (this.elements.section.hasClassName(this.classNames.expanded))
      this.elements.section.removeClassName(this.classNames.expanded);
    this.visible = false;
    this.fireEvent('hidden');
  },

  setVisible: function(){
    this.elements.section.addClassName(this.classNames.expanded);
    this.visible = true;
    this.accordion.activeSection = this;
    this.fireEvent('shown');
  },
  fireEvent: function($super, state){
    $super(state, { accordion: this.accordion, section: this });
  }
});
<|endoftext|>


<commit_before>var _ = require("lodash");
var fs = require("fs");
var glob = require("glob");
var handlebars = require("handlebars");

var fileWatcher = require("../file_watcher");
var env = require("../enviroments");
var templateLoader = require("./template_loader");

var FileNotFoundError = require("../errors").FileNotFoundError;

require("./helpers");

env.extraHelpers.forEach(function(helperPath) {
  try {
    return require(helperPath)(handlebars);
  } catch (error) {
    return console.error("error when load extra helper file: " + helperPath, error);
  }
});

var componentsPartials = [];
function registerComponentLayout(filePath) {
  if (!/\.hbs$/.test(filePath)) {
    return;
  }
  var hbs = fs.readFileSync(filePath);
  var name = filePath.slice(env.componentsHome.length + 1).split(".")[0];
  name = "component:" + name;
  componentsPartials.push(name);
  return handlebars.registerPartial(name, handlebars.compile(hbs.toString()));
};

function findComponentLayout() {
  var filePaths = glob.sync(env.componentsHome + '/**/{all_templates,other_templates,templates}/*.hbs');
  return _.map(filePaths, registerComponentLayout);
};
findComponentLayout();

fileWatcher.watchFiles(env.componentsHome, {
  filter: function(fullPath) {
    return !/\S*\/(all_templates|other_templates|templates)\/\w+.hbs$/.test(fullPath);
  }
}, function(filePath) {
  if (!fs.existsSync(filePath)) {
    return;
  }
  if (fs.statSync(filePath).isDirectory()) {
    return;
  }
  try {
    registerComponentLayout(filePath);
    return console.log("[component Reload] " + filePath);
  } catch (error) {
    return console.log("component Reload Error] " + filePath + " - " + error);
  }
});

var blocks = {};
handlebars.registerHelper("partial", function(name, options) {
  var block = blocks[name];
  if (!block) {
    block = blocks[name] = [];
  }
  block.push(options.fn(this));
  return;
});

handlebars.registerHelper("block", function(name, options) {
  var block = blocks[name] || [];
  if (block.length === 0) {
    if (options.fn) {
      return options.fn(this);
    } else {
      return "";
    }
  } else {
    var content = block.join("\n");
    blocks[name] = [];
    return content;
  }
});

function registerLayout(filePath) {
  if (!/\.hbs$/.test(filePath)) {
    return;
  }
  var hbs = fs.readFileSync(filePath);
  var name = filePath.slice(env.viewsHome.length + 1).split(".")[0];
  if (env.oldMode) {
    name = "views/" + name;
  }
  return handlebars.registerPartial(name, handlebars.compile(hbs.toString()));
};

var layouts = [];
function findLayouts(dir) {
  if (!fs.existsSync(dir)) {
    console.error('viewsHome is not exist', dir);
    return;
  }
  var files = fs.readdirSync(dir);
  return files.forEach(function(file) {
    var filePath = dir + "/" + file;
    if (fs.statSync(filePath).isDirectory()) {
      return findLayouts(filePath);
    } else {
      return layouts.push(filePath);
    }
  });
};
findLayouts(env.viewsHome);

layouts.forEach(function(file) {
  return registerLayout(file);
});

fileWatcher.watchFiles(env.viewsHome, function(filePath) {
  if (!fs.existsSync(filePath)) {
    return;
  }
  if (fs.statSync(filePath).isDirectory()) {
    return;
  }
  try {
    registerLayout(filePath);
    return console.log("[Layout Reload] " + filePath);
  } catch (error) {
    return console.log("Layout Reload Error] " + filePath + " - " + error);
  }
});

function normalizePath(path) {
  if (path[0] === "/") {
    return path.slice(1);
  } else {
    return path;
  }
};

function getRealPath(path) {
  if (env.pageMode) {
    return env.viewsHome + "/" + (normalizePath(path)) + "/view.hbs";
  } else {
    return env.viewsHome + "/" + (normalizePath(path)) + ".hbs";
  }
};

function getComponentViewPath(path) {
  return env.componentsHome + "/" + (normalizePath(path)) + "/view.hbs";
};

function renderFromRealPath(path, context) {
  var template = templateLoader.fromPathSync(path);
  return template(context);
};

module.exports = {

  renderFile: function(path, context) {
    return renderFromRealPath(getRealPath(path), context);
  },

  renderComponent: function(path, context) {
    context = context || {};
    context[this.CONST.COMP_PATH] = path;
    try {
      return renderFromRealPath(getComponentViewPath(path), context);
    } catch (error) {
      if (err instanceof FileNotFoundError) {
        console.log("[Component Not Found] " + error.path);
        return "component view not found: " + error.path;
      } else {
        throw error;
      }
    }
  },

  CONST: {
    COMP_PATH: "COMP_PATH"
  }
};

require("./render_helpers");
<commit_msg>fix template判断<commit_after>var _ = require("lodash");
var fs = require("fs");
var glob = require("glob");
var handlebars = require("handlebars");

var fileWatcher = require("../file_watcher");
var env = require("../enviroments");
var templateLoader = require("./template_loader");

var FileNotFoundError = require("../errors").FileNotFoundError;

require("./helpers");

env.extraHelpers.forEach(function(helperPath) {
  try {
    return require(helperPath)(handlebars);
  } catch (error) {
    return console.error("error when load extra helper file: " + helperPath, error);
  }
});

var componentsPartials = [];
function registerComponentLayout(filePath) {
  if (!/\.hbs$/.test(filePath)) {
    return;
  }
  var hbs = fs.readFileSync(filePath);
  var name = filePath.slice(env.componentsHome.length + 1).split(".")[0];
  name = "component:" + name;
  componentsPartials.push(name);
  return handlebars.registerPartial(name, handlebars.compile(hbs.toString()));
};

function findComponentLayout() {
  var filePaths = glob.sync(env.componentsHome + '/**/{all_templates,other_templates,templates}/*.hbs');
  return _.map(filePaths, registerComponentLayout);
};
findComponentLayout();

fileWatcher.watchFiles(env.componentsHome, {
  filter: function(fullPath) {
    return /\S*\/(all_templates|other_templates|templates)\/\w+.hbs$/.test(fullPath);
  }
}, function(filePath) {
  if (!fs.existsSync(filePath)) {
    return;
  }
  if (fs.statSync(filePath).isDirectory()) {
    return;
  }
  try {
    registerComponentLayout(filePath);
    return console.log("[component Reload] " + filePath);
  } catch (error) {
    return console.log("component Reload Error] " + filePath + " - " + error);
  }
});

var blocks = {};
handlebars.registerHelper("partial", function(name, options) {
  var block = blocks[name];
  if (!block) {
    block = blocks[name] = [];
  }
  block.push(options.fn(this));
  return;
});

handlebars.registerHelper("block", function(name, options) {
  var block = blocks[name] || [];
  if (block.length === 0) {
    if (options.fn) {
      return options.fn(this);
    } else {
      return "";
    }
  } else {
    var content = block.join("\n");
    blocks[name] = [];
    return content;
  }
});

function registerLayout(filePath) {
  if (!/\.hbs$/.test(filePath)) {
    return;
  }
  var hbs = fs.readFileSync(filePath);
  var name = filePath.slice(env.viewsHome.length + 1).split(".")[0];
  if (env.oldMode) {
    name = "views/" + name;
  }
  return handlebars.registerPartial(name, handlebars.compile(hbs.toString()));
};

var layouts = [];
function findLayouts(dir) {
  if (!fs.existsSync(dir)) {
    console.error('viewsHome is not exist', dir);
    return;
  }
  var files = fs.readdirSync(dir);
  return files.forEach(function(file) {
    var filePath = dir + "/" + file;
    if (fs.statSync(filePath).isDirectory()) {
      return findLayouts(filePath);
    } else {
      return layouts.push(filePath);
    }
  });
};
findLayouts(env.viewsHome);

layouts.forEach(function(file) {
  return registerLayout(file);
});

fileWatcher.watchFiles(env.viewsHome, function(filePath) {
  if (!fs.existsSync(filePath)) {
    return;
  }
  if (fs.statSync(filePath).isDirectory()) {
    return;
  }
  try {
    registerLayout(filePath);
    return console.log("[Layout Reload] " + filePath);
  } catch (error) {
    return console.log("Layout Reload Error] " + filePath + " - " + error);
  }
});

function normalizePath(path) {
  if (path[0] === "/") {
    return path.slice(1);
  } else {
    return path;
  }
};

function getRealPath(path) {
  if (env.pageMode) {
    return env.viewsHome + "/" + (normalizePath(path)) + "/view.hbs";
  } else {
    return env.viewsHome + "/" + (normalizePath(path)) + ".hbs";
  }
};

function getComponentViewPath(path) {
  return env.componentsHome + "/" + (normalizePath(path)) + "/view.hbs";
};

function renderFromRealPath(path, context) {
  var template = templateLoader.fromPathSync(path);
  return template(context);
};

module.exports = {

  renderFile: function(path, context) {
    return renderFromRealPath(getRealPath(path), context);
  },

  renderComponent: function(path, context) {
    context = context || {};
    context[this.CONST.COMP_PATH] = path;
    try {
      return renderFromRealPath(getComponentViewPath(path), context);
    } catch (error) {
      if (err instanceof FileNotFoundError) {
        console.log("[Component Not Found] " + error.path);
        return "component view not found: " + error.path;
      } else {
        throw error;
      }
    }
  },

  CONST: {
    COMP_PATH: "COMP_PATH"
  }
};

require("./render_helpers");
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var page = require('page')
  , request = require('superagent')
  , domify = require('domify')
  , Citizen = require('citizen')
  , Article = require('proposal-article')
  , List = require('proposal-list')
  , Options = require('proposal-options');

// Routing.
page('/home', identify, load, function(ctx) {
  // Build page's content
  var list = new List(ctx.proposals, ctx.proposal);
  var article = new Article(ctx.proposal); // !!MUST be aware of citizen's data too
  var options = new Options(ctx.proposal, ctx.citizen);

  // Render page's content
  replaceWith('article.proposal', article.render());
  replaceWith('nav.sidebar-nav', list.render());
  replaceWith('.proposal-options', options.render());
});

/**
 * Load citizen's data
 *
 * @param {Object} ctx page's context
 * @param {Function} next callback after load
 * @api private
 */

function identify (ctx, next) {
  var citizen = new Citizen();
  ctx.citizen = citizen;
  debugger;
  citizen.load('me'); // What would happen on error?
  citizen.ready(next);
}

/**
 * Load homepage data
 *
 * @param {Object} ctx page's context
 * @param {Function} next callback after load
 * @api private
 */

function load (ctx, next) {
  debugger;
  request
  .get('/api/proposal/all')
  .set('Accept', 'application/json')
  .on('error', _handleRequestError)
  .end(function(res) {
    if (!res.ok) return;

    ctx.proposals = res.body || [proposalExample];
    ctx.proposal = res.body[0] || proposalExample;

    next();
  });
}

/**
 * Handle error from requests
 *
 * @param {Object} err from request
 * @api private
 */

function _handleRequestError (err) {
  console.log(err);
}

/**
 * replaceWith
 *
 * @param {String} selector query string selector
 * @param {NodeElement} el `NodeElement` to replace with match
 * @api private
 */

function replaceWith (selector, el) {
  var match = document.querySelector(selector);
  if (match) {
    match.parentNode.replaceChild(el, match);
  };
}

/**
 * Mocked Proposal Object
 */
var proposalExample = {
  title: "Title Example",
  author: { fullName: "Ricardo Rauch", avatar: "https://si0.twimg.com/profile_images/2583335118/yts2np89ifncbi0j3vgm.jpeg" },
  essay: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse a sodales mi. Nullam non nisi sit amet elit interdum pellentesque. Curabitur lobortis neque eu turpis dictum laoreet.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse a sodales mi. Nullam non nisi sit amet elit interdum pellentesque. Curabitur lobortis neque eu turpis dictum laoreet.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse a sodales mi. Nullam non nisi sit amet elit interdum pellentesque. Curabitur lobortis neque eu turpis dictum laoreet.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse a sodales mi. Nullam non nisi sit amet elit interdum pellentesque. Curabitur lobortis neque eu turpis dictum laoreet.",
  tag: { hash: "Tag" },
  participants: [{ fullName: "Ricardo Rauch", avatar: "https://si0.twimg.com/profile_images/2583335118/yts2np89ifncbi0j3vgm.jpeg" },],
  createdAt: new Date(),
  vote: {
    census: [],
    positive: [],
    negative: [],
    toJSON: function() {
      return {census:[1,2,3],positive:[1,2],negative:[3]}
    }
  }
};

<commit_msg>[homepage] - Remove forgotten debuggers<commit_after>/**
 * Module dependencies.
 */

var page = require('page')
  , request = require('superagent')
  , domify = require('domify')
  , Citizen = require('citizen')
  , Article = require('proposal-article')
  , List = require('proposal-list')
  , Options = require('proposal-options');

// Routing.
page('/home', identify, load, function(ctx) {
  // Build page's content
  var list = new List(ctx.proposals, ctx.proposal);
  var article = new Article(ctx.proposal); // !!MUST be aware of citizen's data too
  var options = new Options(ctx.proposal, ctx.citizen);

  // Render page's content
  replaceWith('article.proposal', article.render());
  replaceWith('nav.sidebar-nav', list.render());
  replaceWith('.proposal-options', options.render());
});

/**
 * Load citizen's data
 *
 * @param {Object} ctx page's context
 * @param {Function} next callback after load
 * @api private
 */

function identify (ctx, next) {
  var citizen = new Citizen();
  ctx.citizen = citizen;
  citizen.load('me'); // What would happen on error?
  citizen.ready(next);
}

/**
 * Load homepage data
 *
 * @param {Object} ctx page's context
 * @param {Function} next callback after load
 * @api private
 */

function load (ctx, next) {
  request
  .get('/api/proposal/all')
  .set('Accept', 'application/json')
  .on('error', _handleRequestError)
  .end(function(res) {
    if (!res.ok) return;

    ctx.proposals = res.body || [proposalExample];
    ctx.proposal = res.body[0] || proposalExample;

    next();
  });
}

/**
 * Handle error from requests
 *
 * @param {Object} err from request
 * @api private
 */

function _handleRequestError (err) {
  console.log(err);
}

/**
 * replaceWith
 *
 * @param {String} selector query string selector
 * @param {NodeElement} el `NodeElement` to replace with match
 * @api private
 */

function replaceWith (selector, el) {
  var match = document.querySelector(selector);
  if (match) {
    match.parentNode.replaceChild(el, match);
  };
}

/**
 * Mocked Proposal Object
 */
var proposalExample = {
  title: "Title Example",
  author: { fullName: "Ricardo Rauch", avatar: "https://si0.twimg.com/profile_images/2583335118/yts2np89ifncbi0j3vgm.jpeg" },
  essay: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse a sodales mi. Nullam non nisi sit amet elit interdum pellentesque. Curabitur lobortis neque eu turpis dictum laoreet.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse a sodales mi. Nullam non nisi sit amet elit interdum pellentesque. Curabitur lobortis neque eu turpis dictum laoreet.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse a sodales mi. Nullam non nisi sit amet elit interdum pellentesque. Curabitur lobortis neque eu turpis dictum laoreet.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse a sodales mi. Nullam non nisi sit amet elit interdum pellentesque. Curabitur lobortis neque eu turpis dictum laoreet.",
  tag: { hash: "Tag" },
  participants: [{ fullName: "Ricardo Rauch", avatar: "https://si0.twimg.com/profile_images/2583335118/yts2np89ifncbi0j3vgm.jpeg" },],
  createdAt: new Date(),
  vote: {
    census: [],
    positive: [],
    negative: [],
    toJSON: function() {
      return {census:[1,2,3],positive:[1,2],negative:[3]}
    }
  }
};

<|endoftext|>


<commit_before>module.exports = function(sails) {

  /**
   * Module dependencies
   */

  var _ = require('lodash');
  var Err = require('../../../errors');
  var path = require('path');
  var fs = require('fs');
  var ChildProcess = require('child_process');

  /**
   * Grunt hook
   *
   * A core hook for interacting with the Grunt-powered pipeline which
   * is the conventional default in all new Sails apps.
   *
   * =============================================================================================
   *
   * @event 'hook:grunt:loaded'
   *        Emitted when the Grunt hook has been automatically loaded by Sails core, and
   *        triggered the callback in its `initialize` function.
   *
   * @event 'hook:grunt:done'
   *        Emitted when the Grunt child process exits with a normal status code.
   *        (in development, this will not fire until the app is lowered, since grunt-contrib-watch
   *         keeps the child process active)
   *
   * @event 'hook:grunt:error'
   *        Emitted when the Grunt child process exits with a non-zero status code.
   *
   *
   * > In development, note that *neither* the `grunt:hook:done`, *nor* the `grunt:hook:done` event will
   * > fire until the app is lowered if you're using the default pipeline.
   * > (This is because `grunt-contrib-watch` keeps the child process active.)
   */
  return {

    /**
     * When this hook is initialized, load this project's Grunt tasks.
     */
    initialize: function(cb) {

      sails.log.verbose('Loading app Gruntfile...');

      // Run the Grunt task for this environment
      // (spinning up a child process)
      if (sails.config.environment === 'production') {
        this.runTask('prod', cb);
      } else {
        this.runTask('default', cb);
      }
    },

    /**
     * `runTask()`
     *
     * Fork a Grunt child process that runs the specified task.
     *
     * @param {String} taskName
     *        The name of the Grunt task to run.
     *
     * @param {Function} cb
     *        Optional.  Fires when the Grunt task has been started (non-production) or finished (production).
     *
     * @api private
     *      (however note that this is called directly by `sails www` in the CLI)
     */
    runTask: function(taskName, cb_afterTaskStarted) {
      cb_afterTaskStarted = cb_afterTaskStarted || function() {};

      // Determine the path to the root directory of the current running instance
      // of Sails core.
      var pathToSails = path.resolve(__dirname, '../../../');

      // If provided task is not a string, fail silently.
      // TODO: handle this more elegantly.
      if (!taskName) {
        taskName = '';
      }

      // Fork Grunt child process
      var child = ChildProcess.fork(

        // Set our Grunt wrapper file in Sails core as the working directory for
        // the Grunt child process.
        path.join(__dirname, 'grunt-wrapper.js'),

        // Command-line arguments (e.g. `foo bar`) and command-line options (e.g. `--foo="bar"`)
        // to pass to the child process.
        [
          taskName,
          '--pathToSails=' + pathToSails,

          // Backwards compatibility for v0.9.x
          '--gdsrc=' + pathToSails + '/node_modules'
        ],

        // Command-line options (e.g. `--foo="bar"`) to pass to the child process.
        {
          silent: true,
          stdio: 'pipe',
          // Pass all current node process arguments to the child process,
          // except the debug-related arguments, see issue #2670
          execArgv: process.execArgv.slice(0).filter(function(param) {
            return !(new RegExp('--debug(-brk=[0-9]+)?').test(param));
          })
        }
      );

      // Initialize local variables which will be used to buffer the
      // incoming output from our Grunt child process.
      //
      // `errorMsg` will end up holding the human-readable error message,
      // while `stackTrace` will end up with our best guess at a reasonable
      // stack trace parsed from the incoming child proc output.
      var errorMsg = '';
      var stackTrace = '';

      // Receive output as it comes in from the child proc's stdout
      child.stdout.on('data', function(consoleMsg) {

        // store all the output
        consoleMsg = consoleMsg.toString();
        errorMsg += consoleMsg;

        // Clean out all the whitespace
        var trimmedStackTrace = (typeof stackTrace === 'string') ? stackTrace : '';
        trimmedStackTrace = trimmedStackTrace.replace(/[\n\s]*$/, '');
        trimmedStackTrace = trimmedStackTrace.replace(/^[\n\s]*/, '');
        var trimmedConsoleMsg = (typeof consoleMsg === 'string') ? consoleMsg : '';
        trimmedConsoleMsg = trimmedConsoleMsg.replace(/[\n\s]*$/, '');
        trimmedConsoleMsg = trimmedConsoleMsg.replace(/^[\n\s]*/, '');

        // Remove '--force to continue' message since it makes no sense
        // in this context:
        trimmedConsoleMsg = trimmedConsoleMsg.replace(/Use --force to continue\./i, '');
        trimmedStackTrace = trimmedStackTrace.replace(/Use --force to continue\./i, '');

        // Find the stack trace related to this warning
        stackTrace = errorMsg.substring(errorMsg.lastIndexOf('Running "'));

        // Handle fatal errors, like missing grunt dependency, etc.
        if (consoleMsg.match(/Fatal error/g)) {

          // If no Gruntfile exists, don't crash- just display a warning.
          if (consoleMsg.match(/Unable to find Gruntfile/i)) {
            sails.log.info('Gruntfile could not be found.');
            sails.log.info('(no grunt tasks will be run.)');
            if (sails.config.environment === 'production') {
              cb_afterTaskStarted();
            }
            return;
          }

          Err.fatal.__GruntAborted__(trimmedConsoleMsg, trimmedStackTrace);
          return;
        }

        // Handle fatal Grunt errors by killing Sails process as well
        if (consoleMsg.match(/Aborted due to warnings/)) {
          sails.log.error('** Grunt :: An error occurred. **');
          // sails.log.warn(trimmedStackTrace);
          // sails.emit('hook:grunt:error', trimmedStackTrace);
          Err.fatal.__GruntAborted__(trimmedConsoleMsg, trimmedStackTrace);
          return;
        }

        if (consoleMsg.match(/ParseError/)) {
          sails.log.warn('** Grunt :: Parse Warning **');
          sails.log.warn(trimmedStackTrace);
        }

        // Only display console message if it has content besides whitespace
        else if (!consoleMsg.match(/^\s*$/)) {
          sails.log.verbose('Grunt :: ' + trimmedConsoleMsg);
        }
      }); // </ stdout.on('data') >

      // Handle errors on the stdout stream
      // (rare- this is mainly to prevent throwing and crashing the process)
      child.stdout.on('error', function(gruntOutput) {
        sails.log.error('Grunt ::', _sanitize(gruntOutput));
      });

      // Receive output from the proc's stderr stream.
      child.stderr.on('data', function(gruntOutput) {
        gruntOutput = _sanitize(gruntOutput);
        // Ignore the "debugger listening" message from node --debug
        if (gruntOutput.match(/debugger listening on port/)) {
          return;
        }
        sails.log.error('Grunt ::', gruntOutput);
      });

      // Handle errors on the stderr stream.
      // (rare- this is mainly to prevent throwing and crashing the process)
      child.stderr.on('error', function(gruntOutput) {
        sails.log.error('Grunt ::', _sanitize(gruntOutput));
      });

      // When Grunt child process exits, fire event on `sails` app object.
      child.on('exit', function(code) {

        // If this is a non-zero status code, emit the 'hook:grunt:error' event.
        if (code !== 0) {
          return sails.emit('hook:grunt:error');
        }

        // Otherwise emit 'hook:grunt:done'
        sails.emit('hook:grunt:done');

        // Now, if we're in a production environment, wait until Grunt actually finishes running
        // before firing the callback passed in to `runTask`.
        if (sails.config.environment === 'production') {
          return cb_afterTaskStarted();
        }
      });

      // Since there's likely a watch task involved, and we'll want the ability to
      // flush our child process later, we save a reference to it on `sails.childProcesses`.
      sails.log.verbose('Tracking new grunt child process...');
      if (!_.isArray(sails.childProcesses)) {
        return cb_afterTaskStarted(
          new Error(
            'Consistency violation in Sails core: `sails.childProcesses` should exist and be an array.  ' +
            'Instead it\'s type: `' + typeof sails.childProcesses + '`'
          )
        );
      }
      sails.childProcesses.push(child);

      // Now that the child process is chugging along, if we are NOT in a production
      // environment, we'll go ahead and fire our callback (since Grunt might just be sitting
      // here backgrounded, assuming the conventional default pipeline is being used with
      // grunt-contrib-watch.)
      //
      // Note that, if we were in a production environment, we'd wait until the Grunt
      // child proc actually finished running before firing our callback.
      if (sails.config.environment !== 'production') {
        return cb_afterTaskStarted();
      }
    }
  };
};

/**
 * After ensuring a chunk is a string, trim any leading or
 * trailing whitespace.  If chunk cannot be nicely casted to a string,
 * pass it straight through.
 *
 * TODO: use `util.inspect()` and/or `rttc.compile()`to squeeze better string output out of non-strings.
 * TODO: when Lodash is upgraded, use `_.trim()`
 *
 * @param  {*} chunk
 * @return {*}
 */
function _sanitize(chunk) {

  if (chunk && typeof chunk === 'object' && chunk.toString) {
    chunk = chunk.toString();
  }
  if (typeof chunk === 'string') {
    chunk = chunk.replace(/^[\s\n]*/, '');
    chunk = chunk.replace(/[\s\n]*$/, '');
  }
  return chunk;
}
<commit_msg>Bring back env-specific tasklist from 3e1dd3723543b68d483d6cbaf4baf553bbcc8391 to try w/ freshened CI.<commit_after>module.exports = function(sails) {

  /**
   * Module dependencies
   */

  var _ = require('lodash');
  var Err = require('../../../errors');
  var path = require('path');
  var fs = require('fs');
  var ChildProcess = require('child_process');

  /**
   * Grunt hook
   *
   * A core hook for interacting with the Grunt-powered pipeline which
   * is the conventional default in all new Sails apps.
   *
   * =============================================================================================
   *
   * @event 'hook:grunt:loaded'
   *        Emitted when the Grunt hook has been automatically loaded by Sails core, and
   *        triggered the callback in its `initialize` function.
   *
   * @event 'hook:grunt:done'
   *        Emitted when the Grunt child process exits with a normal status code.
   *        (in development, this will not fire until the app is lowered, since grunt-contrib-watch
   *         keeps the child process active)
   *
   * @event 'hook:grunt:error'
   *        Emitted when the Grunt child process exits with a non-zero status code.
   *
   *
   * > In development, note that *neither* the `grunt:hook:done`, *nor* the `grunt:hook:done` event will
   * > fire until the app is lowered if you're using the default pipeline.
   * > (This is because `grunt-contrib-watch` keeps the child process active.)
   */
  return {

    /**
     * When this hook is initialized, load this project's Grunt tasks.
     */
    initialize: function(cb) {

      sails.log.verbose('Loading app Gruntfile...');

      // Determine the proper Grunt task to run.
      var gruntTaskListName;

      // Check to see if the user has specified a grunt task list for this NODE_ENV.
      //
      // Note: Your asset pipeline can also be made environment-specific by checking
      // `process.env.NODE_ENV` or `sails.config.environment` from within the Gruntfile
      // itself, or any task.  This convenience feature exists to make it easier to drop
      // in an environment-specific task list without doing any manual config or imperative
      // coding.
      //
      // Docs on how to work with the default Grunt asset pipeline here:
      // http://sailsjs.org/documentation/concepts/assets/default-tasks
      var pathForEnvSpecificTaskList = path.resolve( sails.config.appPath, path.join('tasks/register/',sails.config.environment + '.js') );
      if ( fs.existsSync(pathForEnvSpecificTaskList) ) {
        gruntTaskListName = sails.config.environment;
      }
      // If the environment is "production", use the "prod" tasklist (unless a "production" tasklist exists)
      else if (sails.config.environment === 'production') {
        gruntTaskListName = 'prod';
      }
      else {
        gruntTaskListName = 'default';
      }

      // Now run the appropriate Grunt task for this environment
      // (spinning up a child process)
      return this.runTask(gruntTaskListName, cb);

    },

    /**
     * `runTask()`
     *
     * Fork a Grunt child process that runs the specified task.
     *
     * @param {String} taskName
     *        The name of the Grunt task to run.
     *
     * @param {Function} cb
     *        Optional.  Fires when the Grunt task has been started (non-production) or finished (production).
     *
     * @api private
     *      (however note that this is called directly by `sails www` in the CLI)
     */
    runTask: function(taskName, cb_afterTaskStarted) {
      cb_afterTaskStarted = cb_afterTaskStarted || function() {};

      // Determine the path to the root directory of the current running instance
      // of Sails core.
      var pathToSails = path.resolve(__dirname, '../../../');

      // If provided task is not a string, fail silently.
      // TODO: handle this more elegantly.
      if (!taskName) {
        taskName = '';
      }

      // Fork Grunt child process
      var child = ChildProcess.fork(

        // Set our Grunt wrapper file in Sails core as the working directory for
        // the Grunt child process.
        path.join(__dirname, 'grunt-wrapper.js'),

        // Command-line arguments (e.g. `foo bar`) and command-line options (e.g. `--foo="bar"`)
        // to pass to the child process.
        [
          taskName,
          '--pathToSails=' + pathToSails,

          // Backwards compatibility for v0.9.x
          '--gdsrc=' + pathToSails + '/node_modules'
        ],

        // Command-line options (e.g. `--foo="bar"`) to pass to the child process.
        {
          silent: true,
          stdio: 'pipe',
          // Pass all current node process arguments to the child process,
          // except the debug-related arguments, see issue #2670
          execArgv: process.execArgv.slice(0).filter(function(param) {
            return !(new RegExp('--debug(-brk=[0-9]+)?').test(param));
          })
        }
      );

      // Initialize local variables which will be used to buffer the
      // incoming output from our Grunt child process.
      //
      // `errorMsg` will end up holding the human-readable error message,
      // while `stackTrace` will end up with our best guess at a reasonable
      // stack trace parsed from the incoming child proc output.
      var errorMsg = '';
      var stackTrace = '';

      // Receive output as it comes in from the child proc's stdout
      child.stdout.on('data', function(consoleMsg) {

        // store all the output
        consoleMsg = consoleMsg.toString();
        errorMsg += consoleMsg;

        // Clean out all the whitespace
        var trimmedStackTrace = (typeof stackTrace === 'string') ? stackTrace : '';
        trimmedStackTrace = trimmedStackTrace.replace(/[\n\s]*$/, '');
        trimmedStackTrace = trimmedStackTrace.replace(/^[\n\s]*/, '');
        var trimmedConsoleMsg = (typeof consoleMsg === 'string') ? consoleMsg : '';
        trimmedConsoleMsg = trimmedConsoleMsg.replace(/[\n\s]*$/, '');
        trimmedConsoleMsg = trimmedConsoleMsg.replace(/^[\n\s]*/, '');

        // Remove '--force to continue' message since it makes no sense
        // in this context:
        trimmedConsoleMsg = trimmedConsoleMsg.replace(/Use --force to continue\./i, '');
        trimmedStackTrace = trimmedStackTrace.replace(/Use --force to continue\./i, '');

        // Find the stack trace related to this warning
        stackTrace = errorMsg.substring(errorMsg.lastIndexOf('Running "'));

        // Handle fatal errors, like missing grunt dependency, etc.
        if (consoleMsg.match(/Fatal error/g)) {

          // If no Gruntfile exists, don't crash- just display a warning.
          if (consoleMsg.match(/Unable to find Gruntfile/i)) {
            sails.log.info('Gruntfile could not be found.');
            sails.log.info('(no grunt tasks will be run.)');
            if (sails.config.environment === 'production') {
              cb_afterTaskStarted();
            }
            return;
          }

          Err.fatal.__GruntAborted__(trimmedConsoleMsg, trimmedStackTrace);
          return;
        }

        // Handle fatal Grunt errors by killing Sails process as well
        if (consoleMsg.match(/Aborted due to warnings/)) {
          sails.log.error('** Grunt :: An error occurred. **');
          // sails.log.warn(trimmedStackTrace);
          // sails.emit('hook:grunt:error', trimmedStackTrace);
          Err.fatal.__GruntAborted__(trimmedConsoleMsg, trimmedStackTrace);
          return;
        }

        if (consoleMsg.match(/ParseError/)) {
          sails.log.warn('** Grunt :: Parse Warning **');
          sails.log.warn(trimmedStackTrace);
        }

        // Only display console message if it has content besides whitespace
        else if (!consoleMsg.match(/^\s*$/)) {
          sails.log.verbose('Grunt :: ' + trimmedConsoleMsg);
        }
      }); // </ stdout.on('data') >

      // Handle errors on the stdout stream
      // (rare- this is mainly to prevent throwing and crashing the process)
      child.stdout.on('error', function(gruntOutput) {
        sails.log.error('Grunt ::', _sanitize(gruntOutput));
      });

      // Receive output from the proc's stderr stream.
      child.stderr.on('data', function(gruntOutput) {
        gruntOutput = _sanitize(gruntOutput);
        // Ignore the "debugger listening" message from node --debug
        if (gruntOutput.match(/debugger listening on port/)) {
          return;
        }
        sails.log.error('Grunt ::', gruntOutput);
      });

      // Handle errors on the stderr stream.
      // (rare- this is mainly to prevent throwing and crashing the process)
      child.stderr.on('error', function(gruntOutput) {
        sails.log.error('Grunt ::', _sanitize(gruntOutput));
      });

      // When Grunt child process exits, fire event on `sails` app object.
      child.on('exit', function(code) {

        // If this is a non-zero status code, emit the 'hook:grunt:error' event.
        if (code !== 0) {
          return sails.emit('hook:grunt:error');
        }

        // Otherwise emit 'hook:grunt:done'
        sails.emit('hook:grunt:done');

        // Now, if we're in a production environment, wait until Grunt actually finishes running
        // before firing the callback passed in to `runTask`.
        if (sails.config.environment === 'production') {
          return cb_afterTaskStarted();
        }
      });

      // Since there's likely a watch task involved, and we'll want the ability to
      // flush our child process later, we save a reference to it on `sails.childProcesses`.
      sails.log.verbose('Tracking new grunt child process...');
      if (!_.isArray(sails.childProcesses)) {
        return cb_afterTaskStarted(
          new Error(
            'Consistency violation in Sails core: `sails.childProcesses` should exist and be an array.  ' +
            'Instead it\'s type: `' + typeof sails.childProcesses + '`'
          )
        );
      }
      sails.childProcesses.push(child);

      // Now that the child process is chugging along, if we are NOT in a production
      // environment, we'll go ahead and fire our callback (since Grunt might just be sitting
      // here backgrounded, assuming the conventional default pipeline is being used with
      // grunt-contrib-watch.)
      //
      // Note that, if we were in a production environment, we'd wait until the Grunt
      // child proc actually finished running before firing our callback.
      if (sails.config.environment !== 'production') {
        return cb_afterTaskStarted();
      }
    }
  };
};

/**
 * After ensuring a chunk is a string, trim any leading or
 * trailing whitespace.  If chunk cannot be nicely casted to a string,
 * pass it straight through.
 *
 * TODO: use `util.inspect()` and/or `rttc.compile()`to squeeze better string output out of non-strings.
 * TODO: when Lodash is upgraded, use `_.trim()`
 *
 * @param  {*} chunk
 * @return {*}
 */
function _sanitize(chunk) {

  if (chunk && typeof chunk === 'object' && chunk.toString) {
    chunk = chunk.toString();
  }
  if (typeof chunk === 'string') {
    chunk = chunk.replace(/^[\s\n]*/, '');
    chunk = chunk.replace(/[\s\n]*$/, '');
  }
  return chunk;
}
<|endoftext|>


<commit_before>require('../core-upgrade');
var HTML5 = require('../html5');
var sys = require('sys');
var assert = require('assert');

HTML5.TreeBuilder = b = function TreeBuilder(document) {
	this.open_elements = [];
	this.document = document;
	this.activeFormattingElements = [];
}

b.prototype.reset = function() {

}

b.prototype.copyAttributeToElement = function(element, attribute) {
	if(attribute.nodeType && attribute.nodeType == attribute.ATTRIBUTE_NODE) {
		element.setAttributeNode(attribute);
	} else {
		element.setAttribute(attribute.nodeName, attribute.nodeValue)
		if(attribute.namespace) {
			var at = element.getAttributeNode(attribute.nodeName);
			at.namespace = attribute.namespace;
		}
	}
}

b.prototype.createElement = function (name, attributes, namespace) {
	var el = this.document.createElement(name);
	el.namespace = namespace;
	if(attributes) {
		if(attributes.item) {
			for(var i = 0; i < attributes.length; i++) {
HTML5.debug('treebuilder.copyAttributes', attributes.item(i));
				this.copyAttributeToElement(el, attributes.item(i));
			}
		} else {
			for(var i = 0; i < attributes.length; i++) {
HTML5.debug('treebuilder.copyAttributes', attributes[i]);
				this.copyAttributeToElement(el, attributes[i]);
			}
		}
	}
	return el;
}

b.prototype.insert_element = function(name, attributes, namespace) {
	HTML5.debug('treebuilder.insert_element', name)
	if(this.insert_from_table) {
		return this.insert_element_from_table(name, attributes, namespace)
	} else {
		return this.insert_element_normal(name, attributes, namespace)
	}
}

b.prototype.insert_foreign_element = function(name, attributes, namespace) {
	return this.insert_element(name, attributes, namespace);
}

b.prototype.insert_element_normal = function(name, attributes, namespace) {
	var element = this.createElement(name, attributes, namespace);
	this.open_elements.last().appendChild(element);
	this.open_elements.push(element);
	return element;
}

b.prototype.insert_element_from_table = function(name, attributes, namespace) {
	var element = this.createElement(name, attributes, namespace)
	if(HTML5.TABLE_INSERT_MODE_ELEMENTS.indexOf(this.open_elements.last().tagName.toLowerCase()) != -1) {
		// We should be in the InTable mode. This means we want to do
		// special magic element rearranging
		var t = this.getTableMisnestedNodePosition()
		if(!t.insertBefore) {
			t.parent.appendChild(element)
		} else {
			t.parent.insertBefore(element, t.insertBefore)
		}
		this.open_elements.push(element)
	} else {
		return this.insert_element_normal(name, attributes, namespace);
	}
	return element;
}

b.prototype.insert_comment = function(data, parent) {
	var c = this.document.createComment(data);
	if(!parent) parent = this.open_elements.last();
	parent.appendChild(c);
}

b.prototype.insert_doctype = function (name, publicId, systemId) {
	var doctype = this.document.implementation.createDocumentType(name, publicId, systemId);
	this.document.appendChild(doctype);
}

b.prototype.insert_text = function(data, parent) {
	if(!parent) parent = this.open_elements.last();
	if(!this.insert_from_table || HTML5.TABLE_INSERT_MODE_ELEMENTS.indexOf(this.open_elements.last().tagName.toLowerCase()) == -1) {
		if(parent.lastChild && parent.lastChild.nodeType == parent.TEXT_NODE) {
			parent.lastChild.appendData(data);
		} else {
			var tn = this.document.createTextNode(data);
			parent.appendChild(tn);
		}
	} else {
		// We should be in the inTable phase. This means we want to do special
		// magic element rearranging.
		var t = this.getTableMisnestedNodePosition();
		insertText(t.parent, data, t.insertBefore)
	}
}

b.prototype.remove_open_elements_until = function(nameOrCb) {
	HTML5.debug('treebuilder.remove_open_elements_until', nameOrCb)
	var finished = false;
	while(!finished) {
		var element = this.pop_element();
		finished = (typeof nameOrCb == 'function' ? nameOrCb(element) : element.tagName.toLowerCase() == nameOrCb);
	}
	return element;
}

b.prototype.pop_element = function() {
	var el = this.open_elements.pop()
	HTML5.debug('treebuilder.pop_element', el.name)
	return el
}

function insertText(node, data, before) {
	var t = node.ownerDocument.createTextNode(data)
	if(before) {
		if(before.previousSibling && before.previousSibling.nodeType == before.previousSibling.TEXT_NODE) {
			before.previousSibling.nodeValue += data;
		} else {
			node.insertBefore(t, before)
		}
	} else {
		node.appendChild(t)
	}
}

b.prototype.getTableMisnestedNodePosition = function() {
	// The foster parent element is the one which comes before the most
	// recently opened table element
	// XXX - this is really inelegant
	var lastTable, fosterParent, insertBefore

	for(var i = this.open_elements.length - 1; i >= 0; i--) {
		var element = this.open_elements[i]
		if(element.tagName.toLowerCase() == 'table') {
			lastTable = element
			break
		}
	}

	if(lastTable) {
		// XXX - we should check that the parent really is a node here
		if(lastTable.parentNode) {
			fosterParent = lastTable.parentNode
			insertBefore = lastTable
		} else {
			fosterParent = this.open_elements[this.open_elements.indexOf(lastTable) - 1]
		}
	} else {
		fosterParent = this.open_elements[0]
	}

	return {parent: fosterParent, insertBefore: insertBefore}
}

b.prototype.elementInScope = function(name, tableVariant) {
	if(this.open_elements.length == 0) return false
	if(this.open_elements.last().tagName.toLowerCase() == name) return true
	for(var i = this.open_elements.length - 1; i >= 0; i--) {
		if (this.open_elements[i].tagName == undefined) return false
		else if(this.open_elements[i].tagName.toLowerCase() == name) return true
		else if(this.open_elements[i].tagName.toLowerCase() == 'table') return false
		else if(!tableVariant && HTML5.SCOPING_ELEMENTS.indexOf(this.open_elements[i].tagName.toLowerCase()) != -1) return false
		else if(this.open_elements[i].tagName.toLowerCase() == 'html') return false;
	}
	return false;
}

b.prototype.generateImpliedEndTags = function(exclude) {
	if(exclude) exclude = exclude.toLowerCase()
	if(this.open_elements.length == 0) {
		HTML5.debug('treebuilder.generateImpliedEndTags', 'no open elements')
		return
	}
	var name = this.open_elements.last().tagName.toLowerCase();
	if(['dd', 'dt', 'li', 'p', 'td', 'th', 'tr'].indexOf(name) != -1 && name != exclude) {
		var p  = this.pop_element();
		this.generateImpliedEndTags(exclude);
	}
}

b.prototype.reconstructActiveFormattingElements = function() {
	// Within this algorithm the order of steps decribed in the specification
	// is not quite the same as the order of steps in the code. It should still
	// do the same though.

	// Step 1: stop if there's nothing to do
	if(this.activeFormattingElements.length == 0) return;

	// Step 2 and 3: start with the last element
	var i = this.activeFormattingElements.length - 1;
	var entry = this.activeFormattingElements[i];
	if(entry == HTML5.Marker || this.open_elements.indexOf(entry) != -1) return;

	while(entry != HTML5.Marker && this.open_elements.indexOf(entry) == -1) {
		i -= 1;
		entry = this.activeFormattingElements[i];
		if(!entry) break;
	}

	while(true) {
		i += 1;
		var clone = this.activeFormattingElements[i].cloneNode();

		var element = this.insert_element(clone.tagName, clone.attributes);

		this.activeFormattingElements[i] = element;

		if(element == this.activeFormattingElements.last()) break;
	}

}

b.prototype.elementInActiveFormattingElements = function(name) {
	var els = this.activeFormattingElements;
	for(var i = els.length - 1; i >= 0; i--) {
		if(els[i] == HTML5.Marker) break;
		if(els[i].tagName.toLowerCase() == name) return els[i];
	}
	return false;
}

b.prototype.reparentChildren = function(o, n) {
	while(o.childNodes.length > 0) {
		var el = o.removeChild(o.childNodes[0]);
		n.appendChild(el);
	}
}

b.prototype.clearActiveFormattingElements = function() {
	while(!(this.activeFormattingElements.length == 0 || this.activeFormattingElements.pop() == HTML5.Marker));
}

b.prototype.getFragment = function() {
	// assert.ok(this.parser.inner_html)
	var fragment = this.document.createDocumentFragment()
	this.reparentChildren(this.root_pointer, fragment)
	return fragment
}

b.prototype.create_structure_elements = function(container) {
	this.html_pointer = this.document.getElementsByTagName('html')[0]
	if(!this.html_pointer) {
		this.html_pointer = this.createElement('html');
		this.document.appendChild(this.html_pointer);
	}
	if(container == 'html') return;
	if(!this.head_pointer) {
		this.head_pointer = this.document.getElementsByTagName('head')[0]
		if(!this.head_pointer) {
			this.head_pointer = this.createElement('head');
			this.html_pointer.appendChild(this.head_pointer);
		}
	}
	if(container == 'head') return;
	this.body_pointer = this.document.getElementsByTagName('body')[0]
	if(!this.body_pointer) {
		this.body_pointer = this.createElement('body');
		this.html_pointer.appendChild(this.body_pointer);
	}
}
<commit_msg>simplify the inScope definition<commit_after>require('../core-upgrade');
var HTML5 = require('../html5');
var sys = require('sys');
var assert = require('assert');

HTML5.TreeBuilder = b = function TreeBuilder(document) {
	this.open_elements = [];
	this.document = document;
	this.activeFormattingElements = [];
}

b.prototype.reset = function() {

}

b.prototype.copyAttributeToElement = function(element, attribute) {
	if(attribute.nodeType && attribute.nodeType == attribute.ATTRIBUTE_NODE) {
		element.setAttributeNode(attribute);
	} else {
		element.setAttribute(attribute.nodeName, attribute.nodeValue)
		if(attribute.namespace) {
			var at = element.getAttributeNode(attribute.nodeName);
			at.namespace = attribute.namespace;
		}
	}
}

b.prototype.createElement = function (name, attributes, namespace) {
	var el = this.document.createElement(name);
	el.namespace = namespace;
	if(attributes) {
		if(attributes.item) {
			for(var i = 0; i < attributes.length; i++) {
HTML5.debug('treebuilder.copyAttributes', attributes.item(i));
				this.copyAttributeToElement(el, attributes.item(i));
			}
		} else {
			for(var i = 0; i < attributes.length; i++) {
HTML5.debug('treebuilder.copyAttributes', attributes[i]);
				this.copyAttributeToElement(el, attributes[i]);
			}
		}
	}
	return el;
}

b.prototype.insert_element = function(name, attributes, namespace) {
	HTML5.debug('treebuilder.insert_element', name)
	if(this.insert_from_table) {
		return this.insert_element_from_table(name, attributes, namespace)
	} else {
		return this.insert_element_normal(name, attributes, namespace)
	}
}

b.prototype.insert_foreign_element = function(name, attributes, namespace) {
	return this.insert_element(name, attributes, namespace);
}

b.prototype.insert_element_normal = function(name, attributes, namespace) {
	var element = this.createElement(name, attributes, namespace);
	this.open_elements.last().appendChild(element);
	this.open_elements.push(element);
	return element;
}

b.prototype.insert_element_from_table = function(name, attributes, namespace) {
	var element = this.createElement(name, attributes, namespace)
	if(HTML5.TABLE_INSERT_MODE_ELEMENTS.indexOf(this.open_elements.last().tagName.toLowerCase()) != -1) {
		// We should be in the InTable mode. This means we want to do
		// special magic element rearranging
		var t = this.getTableMisnestedNodePosition()
		if(!t.insertBefore) {
			t.parent.appendChild(element)
		} else {
			t.parent.insertBefore(element, t.insertBefore)
		}
		this.open_elements.push(element)
	} else {
		return this.insert_element_normal(name, attributes, namespace);
	}
	return element;
}

b.prototype.insert_comment = function(data, parent) {
	var c = this.document.createComment(data);
	if(!parent) parent = this.open_elements.last();
	parent.appendChild(c);
}

b.prototype.insert_doctype = function (name, publicId, systemId) {
	var doctype = this.document.implementation.createDocumentType(name, publicId, systemId);
	this.document.appendChild(doctype);
}

b.prototype.insert_text = function(data, parent) {
	if(!parent) parent = this.open_elements.last();
	if(!this.insert_from_table || HTML5.TABLE_INSERT_MODE_ELEMENTS.indexOf(this.open_elements.last().tagName.toLowerCase()) == -1) {
		if(parent.lastChild && parent.lastChild.nodeType == parent.TEXT_NODE) {
			parent.lastChild.appendData(data);
		} else {
			var tn = this.document.createTextNode(data);
			parent.appendChild(tn);
		}
	} else {
		// We should be in the inTable phase. This means we want to do special
		// magic element rearranging.
		var t = this.getTableMisnestedNodePosition();
		insertText(t.parent, data, t.insertBefore)
	}
}

b.prototype.remove_open_elements_until = function(nameOrCb) {
	HTML5.debug('treebuilder.remove_open_elements_until', nameOrCb)
	var finished = false;
	while(!finished) {
		var element = this.pop_element();
		finished = (typeof nameOrCb == 'function' ? nameOrCb(element) : element.tagName.toLowerCase() == nameOrCb);
	}
	return element;
}

b.prototype.pop_element = function() {
	var el = this.open_elements.pop()
	HTML5.debug('treebuilder.pop_element', el.name)
	return el
}

function insertText(node, data, before) {
	var t = node.ownerDocument.createTextNode(data)
	if(before) {
		if(before.previousSibling && before.previousSibling.nodeType == before.previousSibling.TEXT_NODE) {
			before.previousSibling.nodeValue += data;
		} else {
			node.insertBefore(t, before)
		}
	} else {
		node.appendChild(t)
	}
}

b.prototype.getTableMisnestedNodePosition = function() {
	// The foster parent element is the one which comes before the most
	// recently opened table element
	// XXX - this is really inelegant
	var lastTable, fosterParent, insertBefore

	for(var i = this.open_elements.length - 1; i >= 0; i--) {
		var element = this.open_elements[i]
		if(element.tagName.toLowerCase() == 'table') {
			lastTable = element
			break
		}
	}

	if(lastTable) {
		// XXX - we should check that the parent really is a node here
		if(lastTable.parentNode) {
			fosterParent = lastTable.parentNode
			insertBefore = lastTable
		} else {
			fosterParent = this.open_elements[this.open_elements.indexOf(lastTable) - 1]
		}
	} else {
		fosterParent = this.open_elements[0]
	}

	return {parent: fosterParent, insertBefore: insertBefore}
}

b.prototype.elementInScope = function(name, tableVariant) {
	if(this.open_elements.length == 0) return false
	for(var i = this.open_elements.length - 1; i >= 0; i--) {
		if (this.open_elements[i].tagName == undefined) return false
		else if(this.open_elements[i].tagName.toLowerCase() == name) return true
		else if(this.open_elements[i].tagName.toLowerCase() == 'table') return false
		else if(!tableVariant && HTML5.SCOPING_ELEMENTS.indexOf(this.open_elements[i].tagName.toLowerCase()) != -1) return false
		else if(this.open_elements[i].tagName.toLowerCase() == 'html') return false;
	}
	return false;
}

b.prototype.generateImpliedEndTags = function(exclude) {
	if(exclude) exclude = exclude.toLowerCase()
	if(this.open_elements.length == 0) {
		HTML5.debug('treebuilder.generateImpliedEndTags', 'no open elements')
		return
	}
	var name = this.open_elements.last().tagName.toLowerCase();
	if(['dd', 'dt', 'li', 'p', 'td', 'th', 'tr'].indexOf(name) != -1 && name != exclude) {
		var p  = this.pop_element();
		this.generateImpliedEndTags(exclude);
	}
}

b.prototype.reconstructActiveFormattingElements = function() {
	// Within this algorithm the order of steps decribed in the specification
	// is not quite the same as the order of steps in the code. It should still
	// do the same though.

	// Step 1: stop if there's nothing to do
	if(this.activeFormattingElements.length == 0) return;

	// Step 2 and 3: start with the last element
	var i = this.activeFormattingElements.length - 1;
	var entry = this.activeFormattingElements[i];
	if(entry == HTML5.Marker || this.open_elements.indexOf(entry) != -1) return;

	while(entry != HTML5.Marker && this.open_elements.indexOf(entry) == -1) {
		i -= 1;
		entry = this.activeFormattingElements[i];
		if(!entry) break;
	}

	while(true) {
		i += 1;
		var clone = this.activeFormattingElements[i].cloneNode();

		var element = this.insert_element(clone.tagName, clone.attributes);

		this.activeFormattingElements[i] = element;

		if(element == this.activeFormattingElements.last()) break;
	}

}

b.prototype.elementInActiveFormattingElements = function(name) {
	var els = this.activeFormattingElements;
	for(var i = els.length - 1; i >= 0; i--) {
		if(els[i] == HTML5.Marker) break;
		if(els[i].tagName.toLowerCase() == name) return els[i];
	}
	return false;
}

b.prototype.reparentChildren = function(o, n) {
	while(o.childNodes.length > 0) {
		var el = o.removeChild(o.childNodes[0]);
		n.appendChild(el);
	}
}

b.prototype.clearActiveFormattingElements = function() {
	while(!(this.activeFormattingElements.length == 0 || this.activeFormattingElements.pop() == HTML5.Marker));
}

b.prototype.getFragment = function() {
	// assert.ok(this.parser.inner_html)
	var fragment = this.document.createDocumentFragment()
	this.reparentChildren(this.root_pointer, fragment)
	return fragment
}

b.prototype.create_structure_elements = function(container) {
	this.html_pointer = this.document.getElementsByTagName('html')[0]
	if(!this.html_pointer) {
		this.html_pointer = this.createElement('html');
		this.document.appendChild(this.html_pointer);
	}
	if(container == 'html') return;
	if(!this.head_pointer) {
		this.head_pointer = this.document.getElementsByTagName('head')[0]
		if(!this.head_pointer) {
			this.head_pointer = this.createElement('head');
			this.html_pointer.appendChild(this.head_pointer);
		}
	}
	if(container == 'head') return;
	this.body_pointer = this.document.getElementsByTagName('body')[0]
	if(!this.body_pointer) {
		this.body_pointer = this.createElement('body');
		this.html_pointer.appendChild(this.body_pointer);
	}
}
<|endoftext|>


<commit_before>/* eslint quotes: 0 */

import fs from 'fs';
import path from 'path';
import postcss from 'postcss';
import test from 'ava';
import plugin from '..';

function process(css, options, postcssOptions) {
  return postcss().use(plugin(options)).process(css, postcssOptions);
}

test('resolves urls', t =>
  process("a { b: resolve('picture.png') }", {
    basePath: 'test/fixtures',
    baseUrl: 'http://example.com/wp-content/themes',
    loadPaths: ['fonts', 'images'],
  })
    .then((result) => {
      t.is(result.css, "a { b: url('http://example.com/wp-content/themes/images/picture.png') }");
    }));

test('resolves urls from the current path', t =>
  process("a { b: resolve('picture.png') }", {
    basePath: 'test/fixtures',
    baseUrl: 'http://example.com/wp-content/themes',
  }, {
    from: path.resolve('test/fixtures/images/style.css'),
  })
    .then((result) => {
      t.is(result.css, "a { b: url('http://example.com/wp-content/themes/images/picture.png') }");
    }));

test('resolves relative urls from the current path', t =>
  process("a { b: resolve('fonts/empty-sans.woff') }", {
    basePath: 'test/fixtures',
    relative: true,
  }, {
    from: path.resolve('test/fixtures/images/style.css'),
  })
    .then((result) => {
      t.is(result.css, "a { b: url('../fonts/empty-sans.woff') }");
    }));

test('resolves relative urls from the provided path', t =>
  process("a { b: resolve('fonts/empty-sans.woff') }", {
    basePath: 'test/fixtures',
    relative: 'fonts',
  })
    .then((result) => {
      t.is(result.css, "a { b: url('empty-sans.woff') }");
    }));

test('busts cache when resolving urls', t =>
  process("a { b: resolve('picture.png') }", {
    basePath: 'test/fixtures',
    baseUrl: 'http://example.com/wp-content/themes',
    cachebuster(resolvedPath) {
      return fs.statSync(resolvedPath).size;
    },
    loadPaths: ['fonts', 'images'],
  })
    .then((result) => {
      t.is(result.css, "a { b: url('http://example.com/wp-content/themes/images/picture.png?3061') }");
    }));

test('throws when trying to resolve a non-existing file', t =>
  process("a { b: resolve('non-existing.gif') }")
    .then(t.fail, (err) => {
      t.true(err instanceof Error);
      t.is(err.message, 'Asset not found or unreadable: non-existing.gif');
    }));

test('inlines data', t =>
  process("a { b: inline('picture.png') }", {
    basePath: 'test/fixtures',
    loadPaths: ['fonts', 'images'],
  })
    .then((result) => {
      t.is(result.css.slice(0, 32), "a { b: url('data:image/png;base6");
      t.is(result.css.slice(-32), "ufaJraBKlQAAAABJRU5ErkJggg==') }");
    }));

test('inlines svg unencoded', t =>
  process("a { b: inline('vector.svg') }", {
    basePath: 'test/fixtures',
    loadPaths: ['fonts', 'images'],
  })
    .then((result) => {
      t.is(result.css.slice(0, 32), "a { b: url('data:image/svg+xml;c");
      t.is(result.css.slice(-32), "z%22%2F%3E%0D%0A%3C%2Fsvg%3E') }");
    }));

test('throws when trying to inline a non-existing file', t =>
  process("a { b: inline('non-existing.gif') }")
    .then(t.fail, (err) => {
      t.true(err instanceof Error);
      t.is(err.message, 'Asset not found or unreadable: non-existing.gif');
    }));

test('measures images', t =>
  process("a { " +
    "b: size('vector.svg'); " +
    "c: width('picture.png'); " +
    "d: height('picture.png'); " +
    "}", {
      basePath: 'test/fixtures',
      loadPaths: ['fonts', 'images'],
    })
      .then((result) => {
        t.is(result.css, "a { b: 160px 120px; c: 200px; d: 57px; }");
      }));

test('measures images with density provided', t =>
  process("a { " +
    "b: size('vector.svg', 2); " +
    "c: width('picture.png', 2); " +
    "d: height('picture.png', 2); " +
    "}", {
      basePath: 'test/fixtures',
      loadPaths: ['fonts', 'images'],
    })
      .then((result) => {
        t.is(result.css, "a { b: 80px 60px; c: 100px; d: 28.5px; }");
      }));

test('throws when trying to measure a non-existing image', t =>
  process("a { b: size('non-existing.gif') }")
    .then(t.fail, (err) => {
      t.true(err instanceof Error);
      t.is(err.message, 'Asset not found or unreadable: non-existing.gif');
    }));

test('throws when trying to measure an unsupported file', t =>
  process("a { b: size('test/fixtures/fonts/empty-sans.woff') }")
    .then(t.fail, (err) => {
      const absolutePath = path.resolve('test/fixtures/fonts/empty-sans.woff');
      t.true(err instanceof Error);
      t.is(err.message, `File type not supported: ${absolutePath}`);
    }));

test('throws when trying to measure an invalid file', t =>
  process("a { b: size('test/fixtures/images/invalid.jpg') }")
    .then(t.fail, (err) => {
      const absolutePath = path.resolve('test/fixtures/images/invalid.jpg');
      t.true(err instanceof Error);
      t.is(err.message, `Invalid JPEG file: ${absolutePath}`);
    }));

test('handles quotes and escaped characters', t =>
  process("a {" +
    "b: resolve(picture.png);" +
    "c: resolve('picture.png');" +
    'd: resolve("picture.png");' +
    'e: resolve("\\70 icture.png");' +
  "}", {
    basePath: 'test/fixtures/images',
  })
    .then((result) => {
      t.is(result.css, "a {" +
        "b: url('/picture.png');" +
        "c: url('/picture.png');" +
        "d: url('/picture.png');" +
        "e: url('/picture.png');" +
      "}");
    }));

test('allows usage inside media queries', t =>
  process("@media a and (b: height('test/fixtures/images/picture.png')) { c { d: e }}")
    .then((result) => {
      t.is(result.css, "@media a and (b: 57px) { c { d: e }}");
    }));
<commit_msg>Make ESLint happy<commit_after>/* eslint quotes: 0 */

import fs from 'fs';
import path from 'path';
import postcss from 'postcss';
import test from 'ava';
import plugin from '..';

function process(css, options, postcssOptions) {
  return postcss().use(plugin(options)).process(css, postcssOptions);
}

test('resolves urls', t =>
  process("a { b: resolve('picture.png') }", {
    basePath: 'test/fixtures',
    baseUrl: 'http://example.com/wp-content/themes',
    loadPaths: ['fonts', 'images'],
  })
    .then((result) => {
      t.is(result.css, "a { b: url('http://example.com/wp-content/themes/images/picture.png') }");
    }));

test('resolves urls from the current path', t =>
  process("a { b: resolve('picture.png') }", {
    basePath: 'test/fixtures',
    baseUrl: 'http://example.com/wp-content/themes',
  }, {
    from: path.resolve('test/fixtures/images/style.css'),
  })
    .then((result) => {
      t.is(result.css, "a { b: url('http://example.com/wp-content/themes/images/picture.png') }");
    }));

test('resolves relative urls from the current path', t =>
  process("a { b: resolve('fonts/empty-sans.woff') }", {
    basePath: 'test/fixtures',
    relative: true,
  }, {
    from: path.resolve('test/fixtures/images/style.css'),
  })
    .then((result) => {
      t.is(result.css, "a { b: url('../fonts/empty-sans.woff') }");
    }));

test('resolves relative urls from the provided path', t =>
  process("a { b: resolve('fonts/empty-sans.woff') }", {
    basePath: 'test/fixtures',
    relative: 'fonts',
  })
    .then((result) => {
      t.is(result.css, "a { b: url('empty-sans.woff') }");
    }));

test('busts cache when resolving urls', t =>
  process("a { b: resolve('picture.png') }", {
    basePath: 'test/fixtures',
    baseUrl: 'http://example.com/wp-content/themes',
    cachebuster(resolvedPath) {
      return fs.statSync(resolvedPath).size;
    },
    loadPaths: ['fonts', 'images'],
  })
    .then((result) => {
      t.is(result.css, "a { b: url('http://example.com/wp-content/themes/images/picture.png?3061') }");
    }));

test('throws when trying to resolve a non-existing file', t =>
  process("a { b: resolve('non-existing.gif') }")
    .then(t.fail, (err) => {
      t.true(err instanceof Error);
      t.is(err.message, 'Asset not found or unreadable: non-existing.gif');
    }));

test('inlines data', t =>
  process("a { b: inline('picture.png') }", {
    basePath: 'test/fixtures',
    loadPaths: ['fonts', 'images'],
  })
    .then((result) => {
      t.is(result.css.slice(0, 32), "a { b: url('data:image/png;base6");
      t.is(result.css.slice(-32), "ufaJraBKlQAAAABJRU5ErkJggg==') }");
    }));

test('inlines svg unencoded', t =>
  process("a { b: inline('vector.svg') }", {
    basePath: 'test/fixtures',
    loadPaths: ['fonts', 'images'],
  })
    .then((result) => {
      t.is(result.css.slice(0, 32), "a { b: url('data:image/svg+xml;c");
      t.is(result.css.slice(-32), "z%22%2F%3E%0D%0A%3C%2Fsvg%3E') }");
    }));

test('throws when trying to inline a non-existing file', t =>
  process("a { b: inline('non-existing.gif') }")
    .then(t.fail, (err) => {
      t.true(err instanceof Error);
      t.is(err.message, 'Asset not found or unreadable: non-existing.gif');
    }));

test('measures images', t =>
  process("a { " +
    "b: size('vector.svg'); " +
    "c: width('picture.png'); " +
    "d: height('picture.png'); " +
    "}", {
    basePath: 'test/fixtures',
    loadPaths: ['fonts', 'images'],
  })
    .then((result) => {
      t.is(result.css, "a { b: 160px 120px; c: 200px; d: 57px; }");
    }));

test('measures images with density provided', t =>
  process("a { " +
    "b: size('vector.svg', 2); " +
    "c: width('picture.png', 2); " +
    "d: height('picture.png', 2); " +
    "}", {
    basePath: 'test/fixtures',
    loadPaths: ['fonts', 'images'],
  })
    .then((result) => {
      t.is(result.css, "a { b: 80px 60px; c: 100px; d: 28.5px; }");
    }));

test('throws when trying to measure a non-existing image', t =>
  process("a { b: size('non-existing.gif') }")
    .then(t.fail, (err) => {
      t.true(err instanceof Error);
      t.is(err.message, 'Asset not found or unreadable: non-existing.gif');
    }));

test('throws when trying to measure an unsupported file', t =>
  process("a { b: size('test/fixtures/fonts/empty-sans.woff') }")
    .then(t.fail, (err) => {
      const absolutePath = path.resolve('test/fixtures/fonts/empty-sans.woff');
      t.true(err instanceof Error);
      t.is(err.message, `File type not supported: ${absolutePath}`);
    }));

test('throws when trying to measure an invalid file', t =>
  process("a { b: size('test/fixtures/images/invalid.jpg') }")
    .then(t.fail, (err) => {
      const absolutePath = path.resolve('test/fixtures/images/invalid.jpg');
      t.true(err instanceof Error);
      t.is(err.message, `Invalid JPEG file: ${absolutePath}`);
    }));

test('handles quotes and escaped characters', t =>
  process("a {" +
    "b: resolve(picture.png);" +
    "c: resolve('picture.png');" +
    'd: resolve("picture.png");' +
    'e: resolve("\\70 icture.png");' +
  "}", {
    basePath: 'test/fixtures/images',
  })
    .then((result) => {
      t.is(result.css, "a {" +
        "b: url('/picture.png');" +
        "c: url('/picture.png');" +
        "d: url('/picture.png');" +
        "e: url('/picture.png');" +
      "}");
    }));

test('allows usage inside media queries', t =>
  process("@media a and (b: height('test/fixtures/images/picture.png')) { c { d: e }}")
    .then((result) => {
      t.is(result.css, "@media a and (b: 57px) { c { d: e }}");
    }));
<|endoftext|>