<commit_before>var d3 = require('d3'),
    vg = require('vega'),
    parameter = require('./parameter'),
    post = require('./post');

var config = {
  // URL for loading specs into editor
  editor_url: 'http://vega.github.io/vega-editor/',

  // HTML to inject within view source head element
  source_header: '',

  // HTML to inject before view source closing body tag
  source_footer: ''
};

function load(url, arg, el, callback) {
  vg.util.load({url: url}, function(err, data) {
    if (err || !data) {
      console.error(err || ('No data found at ' + url));
    } else {
      // marshal embedding spec and restart
      var opt = !arg ? JSON.parse(data) : vg.util.extend({source: data}, arg);
      embed(el, opt, callback);
    }
  });
}

// Embed a Vega visualization component in a web page.
// el: DOM element in which to place component (DOM node or CSS selector)
// opt: Embedding specification (parsed JSON or URL string)
// callback: invoked with the generated Vega View instance
function embed(el, opt, callback) {
  var params = [], source, spec;

  if (vg.util.isString(opt)) {
    return load(opt, null, el, callback);
  } else if (opt.source) {
    source = opt.source;
    spec = JSON.parse(source);
  } else if (opt.spec) {
    spec = opt.spec;
    source = JSON.stringify(spec, null, 2);
  } else if (opt.url) {
    return load(opt.url, opt, el, callback);
  } else {
    spec = opt;
    source = JSON.stringify(spec, null, 2);
    opt = {spec: spec, actions: false};
  }

  // ensure container div has class 'vega-embed'
  var div = d3.select(el)
    .classed('vega-embed', true)
    .html(''); // clear container

  // handle parameters
  if (opt.parameters) {
    var elp = opt.parameter_el ? d3.select(opt.parameter_el) : div;
    var pdiv = elp.append('div')
      .attr('class', 'vega-params');
    params = opt.parameters.map(function(p) {
      return parameter.init(pdiv, p, spec);
    });
  }

  vg.parse.spec(spec, function(chart) {
    var renderer = opt.renderer || 'canvas',
        actions  = opt.actions || {};

    var view = chart({
      el: el,
      data: opt.data || undefined,
      renderer: renderer
    });

    if (opt.actions !== false) {
      // add child div to house action links
      var ctrl = div.append('div')
        .attr('class', 'vega-actions');

      // add 'Export' action
      if (actions.export !== false) {
        ctrl.append('a')
          .text('Export as ' + (renderer==='canvas' ? 'PNG' : 'SVG'))
          .attr('href', '#')
          .attr('download', spec.name || 'vega')
          .on('mousedown', function() {
            this.href = imageURL(view);
            d3.event.preventDefault();
          });
      }

      // add 'View Source' action
      if (actions.source !== false) {
        ctrl.append('a')
          .text('View Source')
          .attr('href', '#')
          .on('click', function() {
            viewSource(source);
            d3.event.preventDefault();
          });
      }

      // add 'Open in Vega Editor' action
      if (actions.editor !== false) {
        ctrl.append('a')
          .text('Open in Vega Editor')
          .attr('href', '#')
          .on('click', function() {
            post(window, embed.config.editor_url, {spec: source});
            d3.event.preventDefault();
          });
      }
    }

    // bind all parameter elements
    params.forEach(function(p) { parameter.bind(p, view); });

    // initialize and return visualization
    view.update();
    if (callback) callback(view, spec);
  });
}

function imageURL(view) {
  var ren = view.renderer(),
      scene = ren.scene();

  if (ren.svg) {
    var blob = new Blob([ren.svg()], {type: 'image/svg+xml'});
    return window.URL.createObjectURL(blob);
  } else if (scene.toDataURL) {
    return scene.toDataURL('image/png');
  } else {
    throw Error('Renderer does not support image export.');
  }
}

function viewSource(source) {
  var header = '' + config.source_header + '' + '
';
  var footer = '
' + config.source_footer + '';
  var win = window.open('');
  win.document.write(header + source + footer);
  win.document.title = 'Vega JSON Source';
}

// make config and imageURL externally visible
embed.config = config;
embed.imageURL = imageURL;

module.exports = embed;
<commit_msg>Use View.toImageURL for export.<commit_after>var d3 = require('d3'),
    vg = require('vega'),
    parameter = require('./parameter'),
    post = require('./post');

var config = {
  // URL for loading specs into editor
  editor_url: 'http://vega.github.io/vega-editor/',

  // HTML to inject within view source head element
  source_header: '',

  // HTML to inject before view source closing body tag
  source_footer: ''
};

function load(url, arg, el, callback) {
  vg.util.load({url: url}, function(err, data) {
    if (err || !data) {
      console.error(err || ('No data found at ' + url));
    } else {
      // marshal embedding spec and restart
      var opt = !arg ? JSON.parse(data) : vg.util.extend({source: data}, arg);
      embed(el, opt, callback);
    }
  });
}

// Embed a Vega visualization component in a web page.
// el: DOM element in which to place component (DOM node or CSS selector)
// opt: Embedding specification (parsed JSON or URL string)
// callback: invoked with the generated Vega View instance
function embed(el, opt, callback) {
  var params = [], source, spec;

  if (vg.util.isString(opt)) {
    return load(opt, null, el, callback);
  } else if (opt.source) {
    source = opt.source;
    spec = JSON.parse(source);
  } else if (opt.spec) {
    spec = opt.spec;
    source = JSON.stringify(spec, null, 2);
  } else if (opt.url) {
    return load(opt.url, opt, el, callback);
  } else {
    spec = opt;
    source = JSON.stringify(spec, null, 2);
    opt = {spec: spec, actions: false};
  }

  // ensure container div has class 'vega-embed'
  var div = d3.select(el)
    .classed('vega-embed', true)
    .html(''); // clear container

  // handle parameters
  if (opt.parameters) {
    var elp = opt.parameter_el ? d3.select(opt.parameter_el) : div;
    var pdiv = elp.append('div')
      .attr('class', 'vega-params');
    params = opt.parameters.map(function(p) {
      return parameter.init(pdiv, p, spec);
    });
  }

  vg.parse.spec(spec, function(chart) {
    var renderer = opt.renderer || 'canvas',
        actions  = opt.actions || {};

    var view = chart({
      el: el,
      data: opt.data || undefined,
      renderer: renderer
    });

    if (opt.actions !== false) {
      // add child div to house action links
      var ctrl = div.append('div')
        .attr('class', 'vega-actions');

      // add 'Export' action
      if (actions.export !== false) {
        var ext = (renderer==='canvas' ? 'png' : 'svg');
        ctrl.append('a')
          .text('Export as ' + ext.toUpperCase())
          .attr('href', '#')
          .attr('target', '_blank')
          .attr('download', (spec.name || 'vega') + '.' + ext)
          .on('mousedown', function() {
            this.href = view.toImageURL(ext);
            d3.event.preventDefault();
          });
      }

      // add 'View Source' action
      if (actions.source !== false) {
        ctrl.append('a')
          .text('View Source')
          .attr('href', '#')
          .on('click', function() {
            viewSource(source);
            d3.event.preventDefault();
          });
      }

      // add 'Open in Vega Editor' action
      if (actions.editor !== false) {
        ctrl.append('a')
          .text('Open in Vega Editor')
          .attr('href', '#')
          .on('click', function() {
            post(window, embed.config.editor_url, {spec: source});
            d3.event.preventDefault();
          });
      }
    }

    // bind all parameter elements
    params.forEach(function(p) { parameter.bind(p, view); });

    // initialize and return visualization
    view.update();
    if (callback) callback(view, spec);
  });
}

function viewSource(source) {
  var header = '' + config.source_header + '' + '
';
  var footer = '
' + config.source_footer + '';
  var win = window.open('');
  win.document.write(header + source + footer);
  win.document.title = 'Vega JSON Source';
}

// make config externally visible
embed.config = config;

module.exports = embed;
<|endoftext|>


<commit_before>const child_process = require('child_process');
const path = require('path');
const os = require('os');

function binPath() {
  if ((process.platform === 'linux' || process.platform === 'darwin') && os.arch() === 'x64') {
    return path.join(__dirname, '..', 'bin', 'esbuild');
  }

  if (process.platform === 'win32' && os.arch() === 'x64') {
    return path.join(__dirname, '..', '.install', 'node_modules', 'esbuild-windows-64', 'esbuild.exe');
  }

  throw new Error(`Unsupported platform: ${process.platform} ${os.arch()}`);
}

function pushCommonFlags(flags, options) {
  flags.push(`--error-limit=${options.errorLimit || 0}`);

  if (options.sourcemap) flags.push('--sourcemap');
  if (options.target) flags.push(`--target=${options.target}`);

  if (options.minify) flags.push('--minify');
  if (options.minifySyntax) flags.push('--minify-syntax');
  if (options.minifyWhitespace) flags.push('--minify-whitespace');
  if (options.minifyIdentifiers) flags.push('--minify-identifiers');

  if (options.jsxFactory) flags.push(`--jsx-factory=${options.jsxFactory}`);
  if (options.jsxFragment) flags.push(`--jsx-fragment=${options.jsxFragment}`);
  if (options.define) for (const key in options.define) flags.push(`--define:${key}=${options.define[key]}`);
}

function failureErrorWithLog(text, errors, warnings) {
  const summary = errors.length < 1 ? '' : ` with ${errors.length} error${errors.length < 2 ? '' : 's'}`;
  const error = new Error(`${text}${summary}`);
  error.errors = errors;
  error.warnings = warnings;
  return error;
}

exports.build = options => {
  return new Promise((resolve, reject) => {
    const stdio = options.stdio;
    const flags = [];
    pushCommonFlags(flags, options);

    if (options.name) flags.push(`--name=${options.name}`);
    if (options.bundle) flags.push('--bundle');
    if (options.outfile) flags.push(`--outfile=${options.outfile}`);
    if (options.outdir) flags.push(`--outdir=${options.outdir}`);
    if (options.platform) flags.push(`--platform=${options.platform}`);
    if (options.format) flags.push(`--format=${options.format}`);
    if (options.color) flags.push(`--color=${options.color}`);
    if (options.external) for (const name of options.external) flags.push(`--external:${name}`);
    if (options.loader) for (const ext in options.loader) flags.push(`--loader:${ext}=${options.loader[ext]}`);

    for (const entryPoint of options.entryPoints) {
      if (entryPoint.startsWith('-')) throw new Error(`Invalid entry point: ${entryPoint}`);
      flags.push(entryPoint);
    }

    const child = child_process.spawn(binPath(), flags, { cwd: process.cwd(), windowsHide: true, stdio });
    child.on('error', error => reject(error));

    // The stderr pipe won't be available if "stdio" is set to "inherit"
    const stderrChunks = [];
    if (child.stderr) child.stderr.on('data', chunk => stderrChunks.push(chunk));

    child.on('close', code => {
      const fullRegex = /^(.+):(\d+):(\d+): (warning|error): (.+)$/;
      const smallRegex = /^(warning|error): (.+)$/;
      const errors = [];
      const warnings = [];
      const stderr = Buffer.concat(stderrChunks).toString();

      for (const line of stderr.split('\n')) {
        let match = fullRegex.exec(line);
        if (match) {
          const [, file, line, column, kind, text] = match;
          (kind === 'error' ? errors : warnings).push({ text, location: { file, line: +line, column: +column } });
        }

        else {
          match = smallRegex.exec(line);
          if (match) {
            const [, kind, text] = match;
            (kind === 'error' ? errors : warnings).push({ text, location: null });
          }
        }
      }

      if (errors.length === 0 && code === 0) {
        resolve({ stderr, warnings });
      }

      else {
        const error = failureErrorWithLog('Build failed', errors, warnings);
        error.stderr = stderr;
        reject(error);
      }
    });
  });
}

exports.startService = () => {
  return new Promise((resolve, reject) => {
    const child = child_process.spawn(binPath(), ['--service'], {
      windowsHide: true,
      stdio: ['pipe', 'pipe', 'inherit'],
    });
    child.on('error', error => reject(error));
    const requests = new Map();
    let isClosed = false;
    let nextID = 0;

    // Use a long-lived buffer to store stdout data
    let stdout = Buffer.alloc(4096);
    let stdoutUsed = 0;
    child.stdout.on('data', chunk => {
      // Append the chunk to the stdout buffer, growing it as necessary
      const limit = stdoutUsed + chunk.length;
      if (limit > stdout.length) {
        let swap = Buffer.alloc(limit * 2);
        swap.set(stdout);
        stdout = swap;
      }
      stdout.set(chunk, stdoutUsed);
      stdoutUsed += chunk.length;

      // Process all complete (i.e. not partial) responses
      let offset = 0;
      while (offset + 4 <= stdoutUsed) {
        const length = stdout.readUInt32LE(offset);
        if (offset + 4 + length > stdoutUsed) {
          break;
        }
        offset += 4;
        handleResponse(stdout.slice(offset, offset + length));
        offset += length;
      }
      if (offset > 0) {
        stdout.set(stdout.slice(offset));
        stdoutUsed -= offset;
      }
    });

    child.on('close', () => {
      // When the process is closed, fail all pending requests
      isClosed = true;
      for (const { reject } of requests.values()) {
        reject(new Error('The service was stopped'));
      }
      requests.clear();
    });

    function sendRequest(request) {
      return new Promise((resolve, reject) => {
        if (isClosed) throw new Error('The service is no longer running');

        // Allocate an id for this request
        const id = (nextID++).toString();
        requests.set(id, { resolve, reject });

        // Figure out how long the request will be
        const argBuffers = [Buffer.from(id)];
        let length = 4 + 4 + 4 + argBuffers[0].length;
        for (const arg of request) {
          const argBuffer = Buffer.from(arg);
          argBuffers.push(argBuffer);
          length += 4 + argBuffer.length;
        }

        // Write out the request message
        const bytes = Buffer.alloc(length);
        let offset = 0;
        const writeUint32 = value => {
          bytes.writeUInt32LE(value, offset);
          offset += 4;
        };
        writeUint32(length - 4);
        writeUint32(argBuffers.length);
        for (const argBuffer of argBuffers) {
          writeUint32(argBuffer.length);
          bytes.set(argBuffer, offset);
          offset += argBuffer.length;
        }
        child.stdin.write(bytes);
      });
    }

    function handleResponse(bytes) {
      let offset = 0;
      const eat = n => {
        offset += n;
        if (offset > bytes.length) throw new Error('Invalid message');
        return offset - n;
      };
      const count = bytes.readUInt32LE(eat(4));
      const response = {};
      let id;

      // Parse the response into a map
      for (let i = 0; i < count; i++) {
        const keyLength = bytes.readUInt32LE(eat(4));
        const key = bytes.slice(offset, eat(keyLength) + keyLength).toString();
        const valueLength = bytes.readUInt32LE(eat(4));
        const value = bytes.slice(offset, eat(valueLength) + valueLength);
        if (key === 'id') id = value.toString();
        else response[key] = value.toString();
      }

      // Dispatch the response
      if (!id) throw new Error('Invalid message');
      const { resolve, reject } = requests.get(id);
      requests.delete(id);
      if (response.error) reject(new Error(response.error));
      else resolve(response);
    }

    function jsonToMessages(json) {
      const parts = JSON.parse(json);
      const messages = [];
      for (let i = 0; i < parts.length; i += 4) {
        messages.push({
          text: parts[i],
          location: parts[i + 1] === '' ? null : {
            file: parts[i + 1],
            line: parts[i + 2],
            column: parts[i + 3],
          },
        });
      }
      return messages;
    }

    // Send an initial ping before resolving with the service object to make
    // sure the service is up and running
    sendRequest(['ping']).then(() => resolve({
      async transform(file, options) {
        const loader = options.loader || 'js';
        const name = `input.${loader}`;
        const flags = ['build', `/${name}`, file, '--', name, '--outfile=/output.js'];
        pushCommonFlags(flags, options);
        if (options.loader) flags.push(`--loader:.${loader}=${options.loader}`);
        const response = await sendRequest(flags);

        // Check for failure
        const errors = response.errors ? jsonToMessages(response.errors) : [];
        const warnings = response.warnings ? jsonToMessages(response.warnings) : [];
        if (errors.length > 0) throw failureErrorWithLog('Transform failed', errors, warnings);

        // Return the result
        const result = { warnings };
        if ('/output.js' in response) result.js = response['/output.js'];
        if ('/output.js.map' in response) result.jsSourceMap = response['/output.js.map'];
        return result;
      },

      stop() {
        child.kill();
      },
    }));
  });
};
<commit_msg>service ping error should reject promise<commit_after>const child_process = require('child_process');
const path = require('path');
const os = require('os');

function binPath() {
  if ((process.platform === 'linux' || process.platform === 'darwin') && os.arch() === 'x64') {
    return path.join(__dirname, '..', 'bin', 'esbuild');
  }

  if (process.platform === 'win32' && os.arch() === 'x64') {
    return path.join(__dirname, '..', '.install', 'node_modules', 'esbuild-windows-64', 'esbuild.exe');
  }

  throw new Error(`Unsupported platform: ${process.platform} ${os.arch()}`);
}

function pushCommonFlags(flags, options) {
  flags.push(`--error-limit=${options.errorLimit || 0}`);

  if (options.sourcemap) flags.push('--sourcemap');
  if (options.target) flags.push(`--target=${options.target}`);

  if (options.minify) flags.push('--minify');
  if (options.minifySyntax) flags.push('--minify-syntax');
  if (options.minifyWhitespace) flags.push('--minify-whitespace');
  if (options.minifyIdentifiers) flags.push('--minify-identifiers');

  if (options.jsxFactory) flags.push(`--jsx-factory=${options.jsxFactory}`);
  if (options.jsxFragment) flags.push(`--jsx-fragment=${options.jsxFragment}`);
  if (options.define) for (const key in options.define) flags.push(`--define:${key}=${options.define[key]}`);
}

function failureErrorWithLog(text, errors, warnings) {
  const summary = errors.length < 1 ? '' : ` with ${errors.length} error${errors.length < 2 ? '' : 's'}`;
  const error = new Error(`${text}${summary}`);
  error.errors = errors;
  error.warnings = warnings;
  return error;
}

exports.build = options => {
  return new Promise((resolve, reject) => {
    const stdio = options.stdio;
    const flags = [];
    pushCommonFlags(flags, options);

    if (options.name) flags.push(`--name=${options.name}`);
    if (options.bundle) flags.push('--bundle');
    if (options.outfile) flags.push(`--outfile=${options.outfile}`);
    if (options.outdir) flags.push(`--outdir=${options.outdir}`);
    if (options.platform) flags.push(`--platform=${options.platform}`);
    if (options.format) flags.push(`--format=${options.format}`);
    if (options.color) flags.push(`--color=${options.color}`);
    if (options.external) for (const name of options.external) flags.push(`--external:${name}`);
    if (options.loader) for (const ext in options.loader) flags.push(`--loader:${ext}=${options.loader[ext]}`);

    for (const entryPoint of options.entryPoints) {
      if (entryPoint.startsWith('-')) throw new Error(`Invalid entry point: ${entryPoint}`);
      flags.push(entryPoint);
    }

    const child = child_process.spawn(binPath(), flags, { cwd: process.cwd(), windowsHide: true, stdio });
    child.on('error', error => reject(error));

    // The stderr pipe won't be available if "stdio" is set to "inherit"
    const stderrChunks = [];
    if (child.stderr) child.stderr.on('data', chunk => stderrChunks.push(chunk));

    child.on('close', code => {
      const fullRegex = /^(.+):(\d+):(\d+): (warning|error): (.+)$/;
      const smallRegex = /^(warning|error): (.+)$/;
      const errors = [];
      const warnings = [];
      const stderr = Buffer.concat(stderrChunks).toString();

      for (const line of stderr.split('\n')) {
        let match = fullRegex.exec(line);
        if (match) {
          const [, file, line, column, kind, text] = match;
          (kind === 'error' ? errors : warnings).push({ text, location: { file, line: +line, column: +column } });
        }

        else {
          match = smallRegex.exec(line);
          if (match) {
            const [, kind, text] = match;
            (kind === 'error' ? errors : warnings).push({ text, location: null });
          }
        }
      }

      if (errors.length === 0 && code === 0) {
        resolve({ stderr, warnings });
      }

      else {
        const error = failureErrorWithLog('Build failed', errors, warnings);
        error.stderr = stderr;
        reject(error);
      }
    });
  });
}

exports.startService = () => {
  return new Promise((resolve, reject) => {
    const child = child_process.spawn(binPath(), ['--service'], {
      windowsHide: true,
      stdio: ['pipe', 'pipe', 'inherit'],
    });
    child.on('error', error => reject(error));
    const requests = new Map();
    let isClosed = false;
    let nextID = 0;

    // Use a long-lived buffer to store stdout data
    let stdout = Buffer.alloc(4096);
    let stdoutUsed = 0;
    child.stdout.on('data', chunk => {
      // Append the chunk to the stdout buffer, growing it as necessary
      const limit = stdoutUsed + chunk.length;
      if (limit > stdout.length) {
        let swap = Buffer.alloc(limit * 2);
        swap.set(stdout);
        stdout = swap;
      }
      stdout.set(chunk, stdoutUsed);
      stdoutUsed += chunk.length;

      // Process all complete (i.e. not partial) responses
      let offset = 0;
      while (offset + 4 <= stdoutUsed) {
        const length = stdout.readUInt32LE(offset);
        if (offset + 4 + length > stdoutUsed) {
          break;
        }
        offset += 4;
        handleResponse(stdout.slice(offset, offset + length));
        offset += length;
      }
      if (offset > 0) {
        stdout.set(stdout.slice(offset));
        stdoutUsed -= offset;
      }
    });

    child.on('close', () => {
      // When the process is closed, fail all pending requests
      isClosed = true;
      for (const { reject } of requests.values()) {
        reject(new Error('The service was stopped'));
      }
      requests.clear();
    });

    function sendRequest(request) {
      return new Promise((resolve, reject) => {
        if (isClosed) throw new Error('The service is no longer running');

        // Allocate an id for this request
        const id = (nextID++).toString();
        requests.set(id, { resolve, reject });

        // Figure out how long the request will be
        const argBuffers = [Buffer.from(id)];
        let length = 4 + 4 + 4 + argBuffers[0].length;
        for (const arg of request) {
          const argBuffer = Buffer.from(arg);
          argBuffers.push(argBuffer);
          length += 4 + argBuffer.length;
        }

        // Write out the request message
        const bytes = Buffer.alloc(length);
        let offset = 0;
        const writeUint32 = value => {
          bytes.writeUInt32LE(value, offset);
          offset += 4;
        };
        writeUint32(length - 4);
        writeUint32(argBuffers.length);
        for (const argBuffer of argBuffers) {
          writeUint32(argBuffer.length);
          bytes.set(argBuffer, offset);
          offset += argBuffer.length;
        }
        child.stdin.write(bytes);
      });
    }

    function handleResponse(bytes) {
      let offset = 0;
      const eat = n => {
        offset += n;
        if (offset > bytes.length) throw new Error('Invalid message');
        return offset - n;
      };
      const count = bytes.readUInt32LE(eat(4));
      const response = {};
      let id;

      // Parse the response into a map
      for (let i = 0; i < count; i++) {
        const keyLength = bytes.readUInt32LE(eat(4));
        const key = bytes.slice(offset, eat(keyLength) + keyLength).toString();
        const valueLength = bytes.readUInt32LE(eat(4));
        const value = bytes.slice(offset, eat(valueLength) + valueLength);
        if (key === 'id') id = value.toString();
        else response[key] = value.toString();
      }

      // Dispatch the response
      if (!id) throw new Error('Invalid message');
      const { resolve, reject } = requests.get(id);
      requests.delete(id);
      if (response.error) reject(new Error(response.error));
      else resolve(response);
    }

    function jsonToMessages(json) {
      const parts = JSON.parse(json);
      const messages = [];
      for (let i = 0; i < parts.length; i += 4) {
        messages.push({
          text: parts[i],
          location: parts[i + 1] === '' ? null : {
            file: parts[i + 1],
            line: parts[i + 2],
            column: parts[i + 3],
          },
        });
      }
      return messages;
    }

    // Send an initial ping before resolving with the service object to make
    // sure the service is up and running
    sendRequest(['ping']).then(() => resolve({
      async transform(file, options) {
        const loader = options.loader || 'js';
        const name = `input.${loader}`;
        const flags = ['build', `/${name}`, file, '--', name, '--outfile=/output.js'];
        pushCommonFlags(flags, options);
        if (options.loader) flags.push(`--loader:.${loader}=${options.loader}`);
        const response = await sendRequest(flags);

        // Check for failure
        const errors = response.errors ? jsonToMessages(response.errors) : [];
        const warnings = response.warnings ? jsonToMessages(response.warnings) : [];
        if (errors.length > 0) throw failureErrorWithLog('Transform failed', errors, warnings);

        // Return the result
        const result = { warnings };
        if ('/output.js' in response) result.js = response['/output.js'];
        if ('/output.js.map' in response) result.jsSourceMap = response['/output.js.map'];
        return result;
      },

      stop() {
        child.kill();
      },
    }), error => reject(error));
  });
};
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import {
   Grid,
   GridCell,
   Spacer,
   Text,
   Title,
   TitleBar,
   View
} from '../../src'

import style from '../style.less'

export default class DemoContentTypography extends Component {
   static propTypes = {
      contents: React.PropTypes.array
   }

   constructor(props) {
      super(props)

      this.handleEditCancel = this.handleEditCancel.bind(this)
      this.handleEditDone = this.handleEditDone.bind(this)
   }

   handleEditCancel(body) {
      console.log(body) // eslint-disable-line no-console
   }

   handleEditDone(body) {
      console.log(body) // eslint-disable-line no-console
   }

   render() {
      return (
         <View width="100%" className={style.docs}>
            <TitleBar title="Text" flush />
            <Spacer size="9" />
            <Grid columns="1[a] 2[b] 4[c-e]" gutter="60px">
               <GridCell>
                  <Title>Text</Title>
                  <Text size="2" color="gray50">

A standard block of text content.

                  </Text>
               </GridCell>
               <GridCell colspan="3">
                  <Text>

Flexitarian biodiesel kale chips, hoodie lumbersexual
                     food truck keffiyeh umami single-origin coffee franzen.
                     Celiac viral put a bird on it, farm-to-table
                     heirloom everyday carry before they sold out locavore
                     listicle stumptown.

                  </Text>
               </GridCell>
            </Grid>
            <Grid columns="1[a] 2[b] 4[c-e]" gutter="60px">
               <GridCell>
                  <Title>Text (editable)</Title>
                  <Text size="2" color="gray50">

With the editable prop,Text
                     can be modified in place.

                  </Text>
               </GridCell>
               <GridCell colspan="3">
                  <Text
                     editable
                     onEditCancel={this.handleEditCancel}
                     onEditDone={this.handleEditDone}
                     editableBody="Flexitarian biodiesel kale chips, hoodie lumbersexual
                     food truck keffiyeh umami single-origin coffee franzen.
                     Celiac viral put a bird on it, farm-to-table
                     heirloom everyday carry before they sold out locavore
                     listicle stumptown. Cold-pressed single-origin coffee seitan,
                     next level biodiesel vinyl synth chia pop-up sartorial ugh
                     post-ironic. Hella bitters cardigan affogato selfies
                     thundercats gentrify, man braid schlitz normcore banjo
                     umami messenger bag sartorial. Humblebrag freegan offal,
                     mumblecore tote bag mustache venmo meditation lumbersexual.
                     Put a bird on it intelligentsia lomo gluten-free bitters marfa.
                     Meh literally try-hard ugh everyday carry."
                  />
               </GridCell>
            </Grid>
            <Grid columns="1[a] 2[b] 4[c-e]" gutter="60px">
               <GridCell>
                  <Title>Text Configurations</Title>
                  <Text size="2" color="gray50">
                     Props for using the Modal component
                  </Text>
               </GridCell>
               <GridCell colspan="3">

                           Prop
                           Prop Type
                           Default Value
                           Description/Options

                           children
                           Node
                           -
                           Children if editable === false

                           color
                           String
                           -
                           CSS color (e.g. #CCC)

                           editable
                           Boolean
                           false
                           Makes Text component editable

                           editableBody
                           String
                           -
                           Initial body content (replaces children)

                           onEditCancel
                           Function
                           -
                           Returns body text to use.
                              editableBody is not changed.

                           onEditDone
                           Function
                           -
                           Returns body text to use.
                              editableBody IS changed.

                           size
                           String
                           -
                           Set content size 1 - 10

                           uppercase
                           Boolean
                           -
                           Make content uppercase

                           weight
                           String
                           normal
                           Set content font-weight

               </GridCell>
            </Grid>
         </View>
      )
   }
}
<commit_msg>Tiny fix.<commit_after>import React, { Component } from 'react'
import {
   Grid,
   GridCell,
   Spacer,
   Text,
   Title,
   TitleBar,
   View
} from '../../src'

import style from '../style.less'

export default class DemoContentText extends Component {
   static propTypes = {
      contents: React.PropTypes.array
   }

   constructor(props) {
      super(props)

      this.handleEditCancel = this.handleEditCancel.bind(this)
      this.handleEditDone = this.handleEditDone.bind(this)
   }

   handleEditCancel(body) {
      console.log(body) // eslint-disable-line no-console
   }

   handleEditDone(body) {
      console.log(body) // eslint-disable-line no-console
   }

   render() {
      return (
         <View width="100%" className={style.docs}>
            <TitleBar title="Text" flush />
            <Spacer size="9" />
            <Grid columns="1[a] 2[b] 4[c-e]" gutter="60px">
               <GridCell>
                  <Title>Text</Title>
                  <Text size="2" color="gray50">

A standard block of text content.

                  </Text>
               </GridCell>
               <GridCell colspan="3">
                  <Text>

Flexitarian biodiesel kale chips, hoodie lumbersexual
                     food truck keffiyeh umami single-origin coffee franzen.
                     Celiac viral put a bird on it, farm-to-table
                     heirloom everyday carry before they sold out locavore
                     listicle stumptown.

                  </Text>
               </GridCell>
            </Grid>
            <Grid columns="1[a] 2[b] 4[c-e]" gutter="60px">
               <GridCell>
                  <Title>Text (editable)</Title>
                  <Text size="2" color="gray50">

With the editable prop,Text
                     can be modified in place.

                  </Text>
               </GridCell>
               <GridCell colspan="3">
                  <Text
                     editable
                     onEditCancel={this.handleEditCancel}
                     onEditDone={this.handleEditDone}
                     editableBody="Flexitarian biodiesel kale chips, hoodie lumbersexual
                     food truck keffiyeh umami single-origin coffee franzen.
                     Celiac viral put a bird on it, farm-to-table
                     heirloom everyday carry before they sold out locavore
                     listicle stumptown. Cold-pressed single-origin coffee seitan,
                     next level biodiesel vinyl synth chia pop-up sartorial ugh
                     post-ironic. Hella bitters cardigan affogato selfies
                     thundercats gentrify, man braid schlitz normcore banjo
                     umami messenger bag sartorial. Humblebrag freegan offal,
                     mumblecore tote bag mustache venmo meditation lumbersexual.
                     Put a bird on it intelligentsia lomo gluten-free bitters marfa.
                     Meh literally try-hard ugh everyday carry."
                  />
               </GridCell>
            </Grid>
            <Grid columns="1[a] 2[b] 4[c-e]" gutter="60px">
               <GridCell>
                  <Title>Text Configurations</Title>
                  <Text size="2" color="gray50">
                     Props for using the Modal component
                  </Text>
               </GridCell>
               <GridCell colspan="3">

                           Prop
                           Prop Type
                           Default Value
                           Description/Options

                           children
                           Node
                           -
                           Children if editable === false

                           color
                           String
                           -
                           CSS color (e.g. #CCC)

                           editable
                           Boolean
                           false
                           Makes Text component editable

                           editableBody
                           String
                           -
                           Initial body content (replaces children)

                           onEditCancel
                           Function
                           -
                           Returns body text to use.
                              editableBody is not changed.

                           onEditDone
                           Function
                           -
                           Returns body text to use.
                              editableBody IS changed.

                           size
                           String
                           -
                           Set content size 1 - 10

                           uppercase
                           Boolean
                           -
                           Make content uppercase

                           weight
                           String
                           normal
                           Set content font-weight

               </GridCell>
            </Grid>
         </View>
      )
   }
}
<|endoftext|>


<commit_before>/** @define {boolean} */
var DEBUG = true;

var

/** @const */ LOG_ALL = -1,
/** @const */ LOG_NONE = 0,

/** @const */ LOG_OTHER =  0x000001,
/** @const */ LOG_CPU =    0x000002,
/** @const */ LOG_FPU =    0x000004,
/** @const */ LOG_MEM =    0x000008,
/** @const */ LOG_DMA =    0x000010,
/** @const */ LOG_IO =     0x000020,
/** @const */ LOG_PS2 =    0x000040,
/** @const */ LOG_PIC =    0x000080,
/** @const */ LOG_VGA =    0x000100,
/** @const */ LOG_PIT =    0x000200,
/** @const */ LOG_MOUSE =  0x000400,
/** @const */ LOG_PCI =    0x000800,
/** @const */ LOG_BIOS =   0x001000,
/** @const */ LOG_CD =     0x002000,
/** @const */ LOG_SERIAL = 0x004000,
/** @const */ LOG_DISK =   0x008000,
/** @const */ LOG_RTC =    0x010000,
/** @const */ LOG_HPET =   0x020000,
/** @const */ LOG_ACPI =   0x040000,
/** @const */ LOG_APIC =   0x080000,
/** @const */ LOG_NET =    0x100000,
/** @const */ LOG_VIRTIO = 0x200000,
/** @const */ LOG_9P =     0x400000,

    //LOG_LEVEL = LOG_ALL & ~LOG_DMA & ~LOG_DISK & ~LOG_PIT;
    LOG_LEVEL = LOG_CPU | LOG_OTHER | LOG_IO;
    //LOG_LEVEL = LOG_CPU | LOG_OTHER | LOG_DISK | LOG_IO | LOG_CD;
    //LOG_LEVEL = 0;

/** @const */
var LOG_NAMES = [
    [1, ""],
    [LOG_CPU, "CPU"],
    [LOG_DISK, "DISK"],
    [LOG_FPU, "FPU"],
    [LOG_MEM, "MEM"],
    [LOG_DMA, "DMA"],
    [LOG_IO, "IO"],
    [LOG_PS2, "PS2"],
    [LOG_PIC, "PIC"],
    [LOG_VGA, "VGA"],
    [LOG_PIT, "PIT"],
    [LOG_MOUSE, "MOUS"],
    [LOG_PCI, "PCI"],
    [LOG_BIOS, "BIOS"],
    [LOG_CD, "CD"],
    [LOG_SERIAL, "SERI"],
    [LOG_RTC, "RTC"],
    [LOG_HPET, "HPET"],
    [LOG_ACPI, "ACPI"],
    [LOG_APIC, "APIC"],
    [LOG_NET, "NET"],
    [LOG_VIRTIO, "VIO"],
    [LOG_9P, "9P"],
];

var
/** @const */ TLB_SYSTEM_READ = 1,
/** @const */ TLB_SYSTEM_WRITE = 2,
/** @const */ TLB_USER_READ = 4,
/** @const */ TLB_USER_WRITE = 8;

var
    /** @const */
    ENABLE_HPET = false,

    /** @const */
    ENABLE_ACPI = false;

var

// flags register bitflags
/** @const */ flag_carry = 1,
/** @const */ flag_parity = 4,
/** @const */ flag_adjust = 16,
/** @const */ flag_zero = 64,
/** @const */ flag_sign = 128,
/** @const */ flag_trap = 256,
/** @const */ flag_interrupt = 512,
/** @const */ flag_direction = 1024,
/** @const */ flag_overflow = 2048,
/** @const */ flag_iopl = 1 << 12 | 1 << 13,
/** @const */ flag_nt = 1 << 14,
/** @const */ flag_rf = 1 << 16,
/** @const */ flag_vm = 1 << 17,
/** @const */ flag_ac = 1 << 18,
/** @const */ flag_vif = 1 << 19,
/** @const */ flag_vip = 1 << 20,
/** @const */ flag_id = 1 << 21,

/**
 * default values of reserved flags bits
 * @const
 */
flags_default = 1 << 1,

/**
 * bitmask to select non-reserved flags bits
 * @const
 */
flags_mask =
    flag_carry | flag_parity | flag_adjust | flag_zero | flag_sign | flag_trap | flag_interrupt |
    flag_direction | flag_overflow | flag_iopl | flag_nt | flag_rf | flag_vm | flag_ac |
    flag_vif | flag_vip | flag_id,

/**
 * all arithmetic flags
 * @const
 */
flags_all = flag_carry | flag_parity | flag_adjust | flag_zero | flag_sign | flag_overflow,

/**
 * opsizes used by get flag functions
 *
 * @const
 */
OPSIZE_8 = 7,
/** @const */
OPSIZE_16 = 15,
/** @const */
OPSIZE_32 = 31,

/** @const */
PSE_ENABLED = 128,

/** @const */ reg_eax = 0,
/** @const */ reg_ecx = 1,
/** @const */ reg_edx = 2,
/** @const */ reg_ebx = 3,
/** @const */ reg_esp = 4,
/** @const */ reg_ebp = 5,
/** @const */ reg_esi = 6,
/** @const */ reg_edi = 7,

/** @const */ reg_ax = 0,
/** @const */ reg_cx = 2,
/** @const */ reg_dx = 4,
/** @const */ reg_bx = 6,
/** @const */ reg_sp = 8,
/** @const */ reg_bp = 10,
/** @const */ reg_si = 12,
/** @const */ reg_di = 14,

/** @const */ reg_al = 0,
/** @const */ reg_cl = 4,
/** @const */ reg_dl = 8,
/** @const */ reg_bl = 12,
/** @const */ reg_ah = 1,
/** @const */ reg_ch = 5,
/** @const */ reg_dh = 9,
/** @const */ reg_bh = 13,

/** @const */ reg_es = 0,
/** @const */ reg_cs = 1,
/** @const */ reg_ss = 2,
/** @const */ reg_ds = 3,
/** @const */ reg_fs = 4,
/** @const */ reg_gs = 5,

/** @const */ reg_tr = 6, // task register
/** @const */ reg_ldtr = 7, // local descriptor table register

/** @const */ LOOP_COUNTER = 11001,
/** @const */ TIME_PER_FRAME = 33;

/** @const */
var OP_TRANSLATION = false;

var
    /**
     * The minimum number of bytes that can be memory-mapped
     * by one device.
     *
     * @const
     */
    MMAP_BLOCK_BITS = 14,
    /** @const */
    MMAP_BLOCK_SIZE = 1 << MMAP_BLOCK_BITS;

/** @const */
var MEM_PAGE_WRITTEN = 1;

/** @const */
var MAGIC_CPU_EXCEPTION = 0xDEADBEE;

var
    /** @const */
    REPEAT_STRING_PREFIX_NONE = 0,
    /** @const */
    REPEAT_STRING_PREFIX_NZ = 1,
    /** @const */
    REPEAT_STRING_PREFIX_Z = 2;

var
    /** @const */
    CR0_PE = 1,
    /** @const */
    CR0_MP = 1 << 1,
    /** @const */
    CR0_EM = 1 << 2,
    /** @const */
    CR0_TS = 1 << 3,
    /** @const */
    CR0_ET = 1 << 4,
    /** @const */
    CR0_WP = 1 << 16,
    /** @const */
    CR0_NW = 1 << 29,
    /** @const */
    CR0_CD = 1 << 30,
    /** @const */
    CR0_PG = 1 << 31;

var
    /** @const */
    CR4_VME = 1,
    /** @const */
    CR4_PVI = 1 << 1,
    /** @const */
    CR4_TSD = 1 << 2,
    /** @const */
    CR4_PSE = 1 << 4,
    /** @const */
    CR4_PAE = 1 << 5,
    /** @const */
    CR4_PGE = 1 << 7;

// Segment prefixes must not collide with reg_*s variables
// _ZERO is a special zero offset segment
var
    /** @const */
    SEG_PREFIX_NONE = -1,

    /** @const */
    SEG_PREFIX_ZERO = 9;

var
    /** @const */
    IA32_SYSENTER_CS = 0x174,

    // Note: These are wrong in Intel's manuals. Fuck Intel
    /** @const */
    IA32_SYSENTER_ESP = 0x175,

    /** @const */
    IA32_SYSENTER_EIP = 0x176;

var
    /** @const */
    TSC_RATE = 1024;
<commit_msg>Increase TSC speed to 8 MHz<commit_after>/** @define {boolean} */
var DEBUG = true;

var

/** @const */ LOG_ALL = -1,
/** @const */ LOG_NONE = 0,

/** @const */ LOG_OTHER =  0x000001,
/** @const */ LOG_CPU =    0x000002,
/** @const */ LOG_FPU =    0x000004,
/** @const */ LOG_MEM =    0x000008,
/** @const */ LOG_DMA =    0x000010,
/** @const */ LOG_IO =     0x000020,
/** @const */ LOG_PS2 =    0x000040,
/** @const */ LOG_PIC =    0x000080,
/** @const */ LOG_VGA =    0x000100,
/** @const */ LOG_PIT =    0x000200,
/** @const */ LOG_MOUSE =  0x000400,
/** @const */ LOG_PCI =    0x000800,
/** @const */ LOG_BIOS =   0x001000,
/** @const */ LOG_CD =     0x002000,
/** @const */ LOG_SERIAL = 0x004000,
/** @const */ LOG_DISK =   0x008000,
/** @const */ LOG_RTC =    0x010000,
/** @const */ LOG_HPET =   0x020000,
/** @const */ LOG_ACPI =   0x040000,
/** @const */ LOG_APIC =   0x080000,
/** @const */ LOG_NET =    0x100000,
/** @const */ LOG_VIRTIO = 0x200000,
/** @const */ LOG_9P =     0x400000,

    //LOG_LEVEL = LOG_ALL & ~LOG_DMA & ~LOG_DISK & ~LOG_PIT;
    LOG_LEVEL = LOG_CPU | LOG_OTHER | LOG_IO;
    //LOG_LEVEL = LOG_CPU | LOG_OTHER | LOG_DISK | LOG_IO | LOG_CD;
    //LOG_LEVEL = 0;

/** @const */
var LOG_NAMES = [
    [1, ""],
    [LOG_CPU, "CPU"],
    [LOG_DISK, "DISK"],
    [LOG_FPU, "FPU"],
    [LOG_MEM, "MEM"],
    [LOG_DMA, "DMA"],
    [LOG_IO, "IO"],
    [LOG_PS2, "PS2"],
    [LOG_PIC, "PIC"],
    [LOG_VGA, "VGA"],
    [LOG_PIT, "PIT"],
    [LOG_MOUSE, "MOUS"],
    [LOG_PCI, "PCI"],
    [LOG_BIOS, "BIOS"],
    [LOG_CD, "CD"],
    [LOG_SERIAL, "SERI"],
    [LOG_RTC, "RTC"],
    [LOG_HPET, "HPET"],
    [LOG_ACPI, "ACPI"],
    [LOG_APIC, "APIC"],
    [LOG_NET, "NET"],
    [LOG_VIRTIO, "VIO"],
    [LOG_9P, "9P"],
];

var
/** @const */ TLB_SYSTEM_READ = 1,
/** @const */ TLB_SYSTEM_WRITE = 2,
/** @const */ TLB_USER_READ = 4,
/** @const */ TLB_USER_WRITE = 8;

var
    /** @const */
    ENABLE_HPET = false,

    /** @const */
    ENABLE_ACPI = false;

var

// flags register bitflags
/** @const */ flag_carry = 1,
/** @const */ flag_parity = 4,
/** @const */ flag_adjust = 16,
/** @const */ flag_zero = 64,
/** @const */ flag_sign = 128,
/** @const */ flag_trap = 256,
/** @const */ flag_interrupt = 512,
/** @const */ flag_direction = 1024,
/** @const */ flag_overflow = 2048,
/** @const */ flag_iopl = 1 << 12 | 1 << 13,
/** @const */ flag_nt = 1 << 14,
/** @const */ flag_rf = 1 << 16,
/** @const */ flag_vm = 1 << 17,
/** @const */ flag_ac = 1 << 18,
/** @const */ flag_vif = 1 << 19,
/** @const */ flag_vip = 1 << 20,
/** @const */ flag_id = 1 << 21,

/**
 * default values of reserved flags bits
 * @const
 */
flags_default = 1 << 1,

/**
 * bitmask to select non-reserved flags bits
 * @const
 */
flags_mask =
    flag_carry | flag_parity | flag_adjust | flag_zero | flag_sign | flag_trap | flag_interrupt |
    flag_direction | flag_overflow | flag_iopl | flag_nt | flag_rf | flag_vm | flag_ac |
    flag_vif | flag_vip | flag_id,

/**
 * all arithmetic flags
 * @const
 */
flags_all = flag_carry | flag_parity | flag_adjust | flag_zero | flag_sign | flag_overflow,

/**
 * opsizes used by get flag functions
 *
 * @const
 */
OPSIZE_8 = 7,
/** @const */
OPSIZE_16 = 15,
/** @const */
OPSIZE_32 = 31,

/** @const */
PSE_ENABLED = 128,

/** @const */ reg_eax = 0,
/** @const */ reg_ecx = 1,
/** @const */ reg_edx = 2,
/** @const */ reg_ebx = 3,
/** @const */ reg_esp = 4,
/** @const */ reg_ebp = 5,
/** @const */ reg_esi = 6,
/** @const */ reg_edi = 7,

/** @const */ reg_ax = 0,
/** @const */ reg_cx = 2,
/** @const */ reg_dx = 4,
/** @const */ reg_bx = 6,
/** @const */ reg_sp = 8,
/** @const */ reg_bp = 10,
/** @const */ reg_si = 12,
/** @const */ reg_di = 14,

/** @const */ reg_al = 0,
/** @const */ reg_cl = 4,
/** @const */ reg_dl = 8,
/** @const */ reg_bl = 12,
/** @const */ reg_ah = 1,
/** @const */ reg_ch = 5,
/** @const */ reg_dh = 9,
/** @const */ reg_bh = 13,

/** @const */ reg_es = 0,
/** @const */ reg_cs = 1,
/** @const */ reg_ss = 2,
/** @const */ reg_ds = 3,
/** @const */ reg_fs = 4,
/** @const */ reg_gs = 5,

/** @const */ reg_tr = 6, // task register
/** @const */ reg_ldtr = 7, // local descriptor table register

/** @const */ LOOP_COUNTER = 11001,
/** @const */ TIME_PER_FRAME = 33;

/** @const */
var OP_TRANSLATION = false;

var
    /**
     * The minimum number of bytes that can be memory-mapped
     * by one device.
     *
     * @const
     */
    MMAP_BLOCK_BITS = 14,
    /** @const */
    MMAP_BLOCK_SIZE = 1 << MMAP_BLOCK_BITS;

/** @const */
var MEM_PAGE_WRITTEN = 1;

/** @const */
var MAGIC_CPU_EXCEPTION = 0xDEADBEE;

var
    /** @const */
    REPEAT_STRING_PREFIX_NONE = 0,
    /** @const */
    REPEAT_STRING_PREFIX_NZ = 1,
    /** @const */
    REPEAT_STRING_PREFIX_Z = 2;

var
    /** @const */
    CR0_PE = 1,
    /** @const */
    CR0_MP = 1 << 1,
    /** @const */
    CR0_EM = 1 << 2,
    /** @const */
    CR0_TS = 1 << 3,
    /** @const */
    CR0_ET = 1 << 4,
    /** @const */
    CR0_WP = 1 << 16,
    /** @const */
    CR0_NW = 1 << 29,
    /** @const */
    CR0_CD = 1 << 30,
    /** @const */
    CR0_PG = 1 << 31;

var
    /** @const */
    CR4_VME = 1,
    /** @const */
    CR4_PVI = 1 << 1,
    /** @const */
    CR4_TSD = 1 << 2,
    /** @const */
    CR4_PSE = 1 << 4,
    /** @const */
    CR4_PAE = 1 << 5,
    /** @const */
    CR4_PGE = 1 << 7;

// Segment prefixes must not collide with reg_*s variables
// _ZERO is a special zero offset segment
var
    /** @const */
    SEG_PREFIX_NONE = -1,

    /** @const */
    SEG_PREFIX_ZERO = 9;

var
    /** @const */
    IA32_SYSENTER_CS = 0x174,

    // Note: These are wrong in Intel's manuals. Fuck Intel
    /** @const */
    IA32_SYSENTER_ESP = 0x175,

    /** @const */
    IA32_SYSENTER_EIP = 0x176;

var
    /** @const */
    TSC_RATE = 8 * 1024;
<|endoftext|>


<commit_before>
var

/**
 * Get localization string function name. Plugins might use this configuration
 * to alter the behavior of updating keys and compiling.
 *
 * @value {String}
 */

GET_LOCALIZATION_STRING_FUNCTION_NAME = project.function.name || 'l',

/**
 * Get localization string using variables function name. Plugins might use this
 * configuration to alter the behavior of updating keys and compiling.
 *
 * @value {String}
 */

GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME = project.function.nameUsingData || 'l';

module.exports = {

  /**
   * Translation inner function call regex should match
   * any inner function of gt(). Innner functions can be
   * used in setting some translation vars. We need to
   * remove these function calls, because they will cause
   * issues with parsing the translation function calls.
   *
   * Example match (double hard brackets mean match):
   *
   * gt('SOME_TRANSLATION_KEY', {
   *   'test': [[test()]]
   * });
   *
   * @value {RegExp}
   */

  GET_LOCALIZATION_INNER_FUNCTION_CALL_SYNTAX: new RegExp('\\s+(?!(' + GET_LOCALIZATION_STRING_FUNCTION_NAME + '|' + GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME + '))\\w+?\\((.*?\\s*?)*?\\)', 'g'),

  /** Translation vars regex should capture a match
   * of the whole translation var object literal in
   * the first index of the whole translation function
   * call string.
   *
   * Example match (double hard brackets mean match):
   *
   * [[l('SOME_TRANSLATION_KEY', {
   *   'test': 'test'
   * });]]
   *
   * @value {RegExp}
   */

  GET_LOCALIZATION_FUNCTION_CALL_SYNTAX: new RegExp('\\s+(' + GET_LOCALIZATION_STRING_FUNCTION_NAME + '|' + GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME + ')\\((\'|")(.*?)\\2\\s*(\\\,\\s*\\{\\s*((.*?)|(\\s*?))+?\\s*\\})??\\s*\\)', 'g'),

  /**
   * Translation key regex should capture a match
   * of the translation key in the second index
   * of the whole translation function call string.
   *
   * Example match (double hard brackets mean match):
   *
   * l([['SOME_TRANSLATION_KEY']], {
   *   'test': 'test'
   * });
   *
   * @value {RegExp}
   */

  LOCALIZATION_KEY_SYNTAX: new RegExp('\\s+(' + GET_LOCALIZATION_STRING_FUNCTION_NAME + '|' + GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME + ')\\((\'|")(.+?)\\2[\,\)]'),

  /**
   * Translation variables regex should capture a match
   * of the whole translation var object literal in
   * the first index of the whole translation function
   * call string. It should also match multiline
   * variable map.
   *
   * Example match (double hard brackets mean match):
   *
   * l('SOME_TRANSLATION_KEY', [[{
   *   'test': 'test'
   * }]]);
   *
   * @value {RegExp}
   */

  LOCALIZATION_VARIABLES_SYNTAX: /(\{(.|\s)*?\})/g,

  /**
   * Translation variable name syntax. Captures the name
   * of a variable. Given that the map key string is only
   * provided.
   *
   * Example match (double hard brackets mean match):
   *
   * l('SOME_TRANSLATION_KEY', {
   *   'plural [[test]]': 'test'
   * });
   *
   * @value {RegExp}
   */

  LOCALIZATION_VARIABLE_NAME_SYNTAX: /(\w+)("|')?\:$/g,

  /**
   * Translation var regex should capture a match
   * of one line of the translation var key including
   * colon sign of the whole translation vars string.
   *
   * Example match (double hard brackets mean match):
   *
   * {
   *   [['test1':]] 'test',
   *   [['test2':]] 'test',
   * };
   *
   * @value {RegExp}
   */

  LOCALIZATION_VARIABLE_SYNTAX: /\s*('|")?\w+\1?\s*\:/g,

  /**
   * Imports of local variables
   */

  GET_LOCALIZATION_STRING_FUNCTION_NAME: GET_LOCALIZATION_STRING_FUNCTION_NAME,
  GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME: GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME
};
<commit_msg>Fixes inner function call bug<commit_after>
var

/**
 * Get localization string function name. Plugins might use this configuration
 * to alter the behavior of updating keys and compiling.
 *
 * @value {String}
 */

GET_LOCALIZATION_STRING_FUNCTION_NAME = project.function.name || 'l',

/**
 * Get localization string using variables function name. Plugins might use this
 * configuration to alter the behavior of updating keys and compiling.
 *
 * @value {String}
 */

GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME = project.function.nameUsingData || 'l';

module.exports = {

  /**
   * Translation inner function call regex should match
   * any inner function of gt(). Innner functions can be
   * used in setting some translation vars. We need to
   * remove these function calls, because they will cause
   * issues with parsing the translation function calls.
   *
   * Example match (double hard brackets mean match):
   *
   * gt('SOME_TRANSLATION_KEY', {
   *   'test': [[test()]]
   * });
   *
   * @value {RegExp}
   */

  GET_LOCALIZATION_INNER_FUNCTION_CALL_SYNTAX: new RegExp('\\s*(?!(' + GET_LOCALIZATION_STRING_FUNCTION_NAME + '|' + GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME + '))\\w+?\\((?!' + GET_LOCALIZATION_STRING_FUNCTION_NAME + '|' + GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME + ')(.*?\\s*?)*?\\)', 'g'),

  /** Translation vars regex should capture a match
   * of the whole translation var object literal in
   * the first index of the whole translation function
   * call string.
   *
   * Example match (double hard brackets mean match):
   *
   * [[l('SOME_TRANSLATION_KEY', {
   *   'test': 'test'
   * });]]
   *
   * @value {RegExp}
   */

  GET_LOCALIZATION_FUNCTION_CALL_SYNTAX: new RegExp('\\s*(' + GET_LOCALIZATION_STRING_FUNCTION_NAME + '|' + GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME + ')\\((\'|")(.*?)\\2\\s*(\\\,\\s*\\{\\s*((.*?)|(\\s*?))+?\\s*\\})??\\s*\\)', 'g'),

  /**
   * Translation key regex should capture a match
   * of the translation key in the second index
   * of the whole translation function call string.
   *
   * Example match (double hard brackets mean match):
   *
   * l([['SOME_TRANSLATION_KEY']], {
   *   'test': 'test'
   * });
   *
   * @value {RegExp}
   */

  LOCALIZATION_KEY_SYNTAX: new RegExp('\\s*(' + GET_LOCALIZATION_STRING_FUNCTION_NAME + '|' + GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME + ')\\((\'|")(.+?)\\2[\,\)]'),

  /**
   * Translation variables regex should capture a match
   * of the whole translation var object literal in
   * the first index of the whole translation function
   * call string. It should also match multiline
   * variable map.
   *
   * Example match (double hard brackets mean match):
   *
   * l('SOME_TRANSLATION_KEY', [[{
   *   'test': 'test'
   * }]]);
   *
   * @value {RegExp}
   */

  LOCALIZATION_VARIABLES_SYNTAX: /(\{(.|\s)*?\})/g,

  /**
   * Translation variable name syntax. Captures the name
   * of a variable. Given that the map key string is only
   * provided.
   *
   * Example match (double hard brackets mean match):
   *
   * l('SOME_TRANSLATION_KEY', {
   *   'plural [[test]]': 'test'
   * });
   *
   * @value {RegExp}
   */

  LOCALIZATION_VARIABLE_NAME_SYNTAX: /(\w+)("|')?\:$/g,

  /**
   * Translation var regex should capture a match
   * of one line of the translation var key including
   * colon sign of the whole translation vars string.
   *
   * Example match (double hard brackets mean match):
   *
   * {
   *   [['test1':]] 'test',
   *   [['test2':]] 'test',
   * };
   *
   * @value {RegExp}
   */

  LOCALIZATION_VARIABLE_SYNTAX: /\s*('|")?\w+\1?\s*\:/g,

  /**
   * Imports of local variables
   */

  GET_LOCALIZATION_STRING_FUNCTION_NAME: GET_LOCALIZATION_STRING_FUNCTION_NAME,
  GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME: GET_LOCALIZATION_STRING_USING_VARIABLES_FUNCTION_NAME
};
<|endoftext|>


<commit_before>var
  dummy
, e = require('allhttperrors')
, declare = require('simpledeclare')
, Schema = require('simpleschema')
, url = require('url')
, async = require('async')
, querystring = require('querystring')
;

/*
KEEP IN MIND:
- If collectionName is not passed, it's assumed to be the same as storeName
- If two stores have the same collectionName, the second one will reuse the existing one.
- This means that you can create the stores beforehand, and then get JsonRestStores to use them (nice!)
- In case of reusing, schema, nested, idProperty and hardLimitOnQueries will be ignored (the dbLayer's are used)
- SimpleDbLayerMixin must be passed schema, nested, hardLimitOnQueries, idproperty to create the layer with
*/

exports = module.exports = declare( Object,  {

  // Must be defined for this module to be functional
  DbLayer: null,

  // The object representing the table in the DB layer
  dbLayer: null,

  nested: [],
  hardLimitOnQueries: 50,

  collectionName: null,

  _foreignSearchFields: [],

  constructor: function(){

    var self = this;

    // The db driver must be defined
    if( self.DbLayer == null ){
      throw( new Error("You must define a db driver in constructor class (creating " + self.storeName + ')' ));
    }

    // If collectionName is not specified, it will deduct it from storeName
    self.collectionName = this.collectionName ? this.collectionName : this.storeName;

    // The db layer already has a table called 'collectionName' in the registry!
    // This store will reuse it. In this case, the class' self.schema, self.nested and
    // self.hardLimitOnQueries will be ignored and the dbLayer's will be used instead
    var existingDbLayer = self.DbLayer.getLayer( self.collectionName );
    if( existingDbLayer ){

      // idProperty must match
      if( self.idProperty != existingDbLayer.idProperty ){
        throw( new Error("When reusing a db layer, idProperties must match.", self.storeName, 'is reusing', existingDbLayer.table ) );
      }

      self.schema = existingDbLayer.schema;
      self.nested = existingDbLayer.nested;
      self.hardLimitOnQueries = existingDbLayer.hardLimitOnQueries;
      self.strictSchemaOnFetch = existingDbLayer.strictSchemaOnFetch;

      self.dbLayer = existingDbLayer;

    } else {

      var layerOptions = {
        idProperty: self.idProperty,
        schema: self.schema,
        nested: self.nested,
        hardLimitOnQueries: self.hardLimitOnQueries,
        strictSchemaOnFetch: self.strictSchemaOnFetch,

        schemaError: self.UnprocessableEntityError,
        fetchChildrenByDefault: true,
      };
      if( self.position ){
        layerOptions.positionField = '__position';
        layerOptions.positionBase = self.paramIds.slice( 0, -1 );
      }

      layerOptions.table = self.collectionName;
      // Actually create the layer with the given parameters
      self.dbLayer = new self.DbLayer( layerOptions );
    }

    var schema = self.schema;

    // By default, added paramIds will be set as `searchable` in DB `schema`
    for( var i = 0, l = self.paramIds.length; i < l; i ++ ){
      self.dbLayer._makeFieldSearchable( self.paramIds[ i ] );
    }

    // Make all LOCAL fields in a search side of any query condition "searchable"
    // Remember: the _remote_ fields will be made searchable in path.field format by
    // layer._makeTablesHashes()
    function visitQueryConditions( o ){

      //console.log( require('util').inspect(o, { depth: 10 } ) );

      if( ! o.type ) throw new Error("Filter's 'type' attribute missing");
      if( ! Array.isArray( o.args) ) throw new Error("Filter's 'args' attribute must be an array");

      //console.log("Visiting: ", require('util').inspect( o, { depth: 10 } ) );
      if( o.type === 'and' || o.type === 'or'){
        //console.log("WILL ENTER: ", require('util').inspect( o.args ));
        o.args.forEach( function( condition ){
          //console.log("Entering:", condition );
          visitQueryConditions( condition );
        });
      } else {

        //console.log( require('util').inspect( o, { depth: 10 } ) );
        var field = o.args[ 0 ];
        // It's a local field: mark it as searchable
        if( field.indexOf( '.' ) === -1 ){

          // It's a local field: it MUST be in the schema
          if( typeof( self.schema.structure[ field ] ) === 'undefined' )
            throw new Error("Field " + field + " cannot be in query if it's not in the schema")

          self.dbLayer._makeFieldSearchable( field );
        }
      }
    }
    visitQueryConditions( self.queryConditions );
  },

  // *********************************************************************
  // *** FUNCTIONS THAT ACTUALLY ACCESS DATA THROUGH THE DB DRIVER
  // *********************************************************************

  _enrichConditionsWithParams: function( conditions, params ){

    var self = this;

    // This will have the list of items that will actually get filtered
    // (they are in self.paramIds and are also defined in `params`
    var list = [];
    var returnedConditions = conditions;
    var whereToPush;

    // Get the list of items that _actually_ need to be added
    self.paramIds.forEach( function( paramId ){
      if( params.hasOwnProperty( paramId ) ) list.push( paramId );
    });

    // If nothing needs to be added, leave filter as it is
    if( ! list.length ) return returnedConditions;

    if( conditions.type === 'and' ){
      whereToPush = conditions.args;
    } else {

      // Turn first condition into an 'and' condition
      returnedConditions = { type: 'and', args: [] };
      whereToPush = returnedConditions.args;
      if( conditions.type ) whereToPush.push( conditions );
    }

    // Add a condition for each paramId, so that it will get satisfied
    list.forEach( function( paramId ){
      whereToPush.push( { type: 'eq', args: [ paramId, params[ paramId ] ] } );
    });

    // If there is only one "and" condition, normalise it to the condition itself
    if( returnedConditions.type === 'and' && returnedConditions.args.length === 1 ){
      returnedConditions = returnedConditions.args[ 0 ];
    }

    return returnedConditions;
  },

  implementReposition: function( doc, where, beforeId, cb ){
    if( typeof( cb ) === 'undefined' ) cb = function(){};

    // No position field: nothing to do
    if( ! this.dbLayer.positionField ){
       return cb( null );
    }

    // Doesn't do much: just tell the layer to repositon
    this.dbLayer.reposition( doc, where, beforeId, cb );

  },

  implementFetchOne: function( request, cb ){

    var self = this;

    // Make up the condition, based on the store's IDs
    var conditions = {};

    // Make up the selector.
    // Remote requests need to have the full filter based on request.params. Local ones
    // only have to have (and I mean HAVE TO) the idProperty
    if( request.remote ){
       conditions = self._enrichConditionsWithParams( conditions, request.params );
    } else {
      conditions = { type: 'eq', args: [ self.idProperty, request.params[ self.idProperty ]  ] };
    }

    // Make the database call
    self.dbLayer.select( { conditions: conditions, ranges: { limit: 1 } }, { children: true }, function( err, docs ){
      if( err ) return cb( err );

      if( docs.length === 0 ) return cb( null, null );
      cb( null, docs[ 0 ] );
    });

  },

  implementInsert: function( request, forceId, cb ){

    var self = this;

    var record = self._co( request.body );

    // _children are not inserted
    delete record._children;

    // If generatedId was passed, force the record to
    // that id
    if( forceId ) record[ self.idProperty ] = forceId;

    self.dbLayer.insert( record, { skipValidation: true, children: true }, cb );
  },

  implementUpdate: function( request, deleteUnsetFields, cb ){

    var self = this;
    var updateObject;

    // Make up the condition, based on the store's IDs
    var conditions = {};

    // Make up the selector.
    // Remote requests need to have the full conditions based on request.params. Local ones
    // only have to have (and I mean HAVE TO) the idProperty
    if( request.remote ){
       conditions = self._enrichConditionsWithParams( conditions, request.params );
    } else {
      conditions = { type: 'eq', args: [ self.idProperty, request.params[ self.idProperty ]  ] };
    }

    // Make up the `updateObject` variable, based on the passed `body`
    updateObject = self._co( request.body );
    delete updateObject._children;

    self.dbLayer.update( conditions, updateObject, { deleteUnsetFields: deleteUnsetFields, multi: false, skipValidation: true }, function( err, howMany, record ){
      if( err ) return cb( err );

      // There is no point in checking howMany: `record` will be null if record wasn't found anyway,
      // and that's what we want
      cb( null, record );
    });
  },

  implementDelete: function( request, cb ){

    var self = this;
    var conditions = {};

    // Make up the selector.
    // Remote requests need to have the full conditions based on request.params. Local ones
    // only have to have (and I mean HAVE TO) the idProperty
    if( request.remote ){
       conditions = self._enrichConditionsWithParams( conditions, request.params );
    } else {
      conditions = { type: 'eq', args: [ self.idProperty, request.params[ self.idProperty ]  ] };
    }

    self.dbLayer.delete( conditions, { multi: false, skipValidation: true }, function( err, howMany, record ){
      if( err ) return cb( err );

      // There is no point in checking howMany: `record` will be null if record wasn't found anyway,
      // and that's what we want
      cb( null, record );
    });

  },

  _queryMakeDbLayerFilter: function( remote, conditionsHash, sort, ranges, cb ){

    // The simleDbLayer filter that will get returned
    var filter = {};

    var self = this;
    var errors = [];
    var field, condition, value;

    // Straight fields that do not need any conversions as they are the same
    // between JsonRestStores and SimpleDbLayer
    filter.ranges = ranges;
    filter.sort = sort;
    filter.conditions = {};

    function getQueryFromQueryConditions( ){

      // Make up filter.conditions
      function visitQueryConditions( o, fc ){

        // Check o.ifDefined. If the corresponding element in onlineSearchSchema is not
        // defined, won't go there
        if( o.ifDefined ){
          if( ! conditionsHash[ o.ifDefined ] ){ return false; }
        }

        if( o.type === 'and' || o.type === 'or'){
          fc.type = o.type;
          fc.args = [];

          o.args.forEach( function( condition ){

            // If it's 'and' or 'or', check the length of what gets returned
            if( condition.type === 'and' || condition.type === 'or' ){

              // Make up the new condition, visit that one
              var newCondition = {};
              var f = visitQueryConditions( condition, newCondition );

              // Falsy return means "don't continue"
              if( f === false ) return;

              // newCondition is empty: do not add anything to fc
              if(  newCondition.args.length === 0 ){

                return ;
              // Only one condition returned: get rid of logical operator, add the straight condition
              } else if( newCondition.args.length === 1 ){
                var actualCondition = newCondition.args[ 0 ];
                fc.args.push( { type: actualCondition.type, args: actualCondition.args } );
              // Multiple conditions returned: the logical operator makes sense
              } else {
                fc.args.push( newCondition );
              }

            // If it's a leaf
            } else {
              var newCondition = {};
              var f = visitQueryConditions( condition, newCondition );
              if( f !== false ) fc.args.push( newCondition );
            }
          });

        // It's a terminal point
        } else {
          var arg0 = o.args[ 0 ];
          var arg1 = o.args[ 1 ];

          // No arg1: most likely a unary operator, let it live.
          if( typeof( arg1 ) === 'undefined'){
            fc.type = o.type;
            fc.args = [];
            fc.args[ 0 ] = arg0;
          }

          // The second argument has a "but!".
          // If it is in form #something#, it means that it's
          // actually a field in onlineSearchSchema
          var m = ( arg1.match && arg1.match( /^#(.*?)#$/) );
          if( m ) {
            var osf = m[ 1 ];

            // If it's in form #something#, then entry MUST be in onlineSearchSchema
            if( ! self.onlineSearchSchema.structure[ osf ] ) throw new Error("Searched for " + arg1 + ", but didn't find corresponding entry in onlineSearchSchema");

            if( conditionsHash[ osf ] ){
              fc.type = o.type;
              fc.args = [];
              fc.args[ 0 ] = arg0;
              fc.args[ 1 ] = conditionsHash[ osf ];
            } else {
              // For leaves, this will tell the callee NOT to add this.
              return false;
            };

          // The second argument is not in form #something#: it means it's a STRAIGHT value
          } else {
            fc.type = o.type;
            fc.args = [];
            fc.args[ 0 ] = arg0;
            fc.args[ 1 ] = arg1;
          }
        }
      }

      // This will be returned
      var res = {};
      visitQueryConditions( self.queryConditions, res );

      // visitQueryConditions does a great job avoiding duplication, but
      // top-level duplication needs to be checked here
      if( ( res.type === 'and' || res.type === 'or' )){
        if( res.args.length === 0 ) return {};
        if( res.args.length === 1 ) return res.args[ 0 ];

      // More than 1 condition: 'and' or 'or' will need to stay
      } else {
        return res;
      }

    }

    filter.conditions = getQueryFromQueryConditions();

    //console.log( self.collectionName );
    //console.log("QUERYCONDITIONS", require('util').inspect( self.queryConditions, { depth: 10 } ));
    //console.log("CONDITIONS HASH:", require('util').inspect( conditionsHash, { depth: 10 } ));
    //console.log("ORIG: ", require('util').inspect( self.queryConditions, { depth: 10 } ));
    //console.log("COPY: ", require('util').inspect( getQueryFromQueryConditions(), { depth: 10 } ));

    cb( null, filter );

  },

  implementQuery: function( request, next ){

    var self = this;
    var cursor;
    var dbLayerOptions = {};

    // If options.delete was on or if it's set at store-level
    // pass {delete: true } to the db layer
    if( request.options.delete || self.deleteAfterGetQuery ){
      dbLayerOptions.delete = true;
    }

    // Pass on skipHardLimitOnQueries to the dbLayer
    dbLayerOptions.skipHardLimitOnQueries = !!request.options.skipHardLimitOnQueries;

    // Children is always true
    dbLayerOptions.children = true;

    // Paranoid checks
    if( typeof( request.options.sort ) === 'undefined' || request.options.sort === null ) request.options.sort = {};
    if( typeof( request.options.ranges ) === 'undefined' || request.options.ranges === null ) request.options.ranges = {};

    self._queryMakeDbLayerFilter( request.remote, request.options.conditions, request.options.sort, request.options.ranges, function( err, filter ){
      if( err ){
        next( err );
      } else {

        if( request.remote) filter.conditions = self._enrichConditionsWithParams( filter.conditions, request.params );

        // Run the select based on the passed parameters
        self.dbLayer.select( filter, dbLayerOptions, next );
      }
    });
  },

});
<commit_msg>Resolved but that prevented filtering from working properly<commit_after>var
  dummy
, e = require('allhttperrors')
, declare = require('simpledeclare')
, Schema = require('simpleschema')
, url = require('url')
, async = require('async')
, querystring = require('querystring')
;

/*
KEEP IN MIND:
- If collectionName is not passed, it's assumed to be the same as storeName
- If two stores have the same collectionName, the second one will reuse the existing one.
- This means that you can create the stores beforehand, and then get JsonRestStores to use them (nice!)
- In case of reusing, schema, nested, idProperty and hardLimitOnQueries will be ignored (the dbLayer's are used)
- SimpleDbLayerMixin must be passed schema, nested, hardLimitOnQueries, idproperty to create the layer with
*/

exports = module.exports = declare( Object,  {

  // Must be defined for this module to be functional
  DbLayer: null,

  // The object representing the table in the DB layer
  dbLayer: null,

  nested: [],
  hardLimitOnQueries: 50,

  collectionName: null,

  _foreignSearchFields: [],

  constructor: function(){

    var self = this;

    // The db driver must be defined
    if( self.DbLayer == null ){
      throw( new Error("You must define a db driver in constructor class (creating " + self.storeName + ')' ));
    }

    // If collectionName is not specified, it will deduct it from storeName
    self.collectionName = this.collectionName ? this.collectionName : this.storeName;

    // The db layer already has a table called 'collectionName' in the registry!
    // This store will reuse it. In this case, the class' self.schema, self.nested and
    // self.hardLimitOnQueries will be ignored and the dbLayer's will be used instead
    var existingDbLayer = self.DbLayer.getLayer( self.collectionName );
    if( existingDbLayer ){

      // idProperty must match
      if( self.idProperty != existingDbLayer.idProperty ){
        throw( new Error("When reusing a db layer, idProperties must match.", self.storeName, 'is reusing', existingDbLayer.table ) );
      }

      self.schema = existingDbLayer.schema;
      self.nested = existingDbLayer.nested;
      self.hardLimitOnQueries = existingDbLayer.hardLimitOnQueries;
      self.strictSchemaOnFetch = existingDbLayer.strictSchemaOnFetch;

      self.dbLayer = existingDbLayer;

    } else {

      var layerOptions = {
        idProperty: self.idProperty,
        schema: self.schema,
        nested: self.nested,
        hardLimitOnQueries: self.hardLimitOnQueries,
        strictSchemaOnFetch: self.strictSchemaOnFetch,

        schemaError: self.UnprocessableEntityError,
        fetchChildrenByDefault: true,
      };
      if( self.position ){
        layerOptions.positionField = '__position';
        layerOptions.positionBase = self.paramIds.slice( 0, -1 );
      }

      layerOptions.table = self.collectionName;
      // Actually create the layer with the given parameters
      self.dbLayer = new self.DbLayer( layerOptions );
    }

    var schema = self.schema;

    // By default, added paramIds will be set as `searchable` in DB `schema`
    for( var i = 0, l = self.paramIds.length; i < l; i ++ ){
      self.dbLayer._makeFieldSearchable( self.paramIds[ i ] );
    }

    // Make all LOCAL fields in a search side of any query condition "searchable"
    // Remember: the _remote_ fields will be made searchable in path.field format by
    // layer._makeTablesHashes()
    function visitQueryConditions( o ){

      //console.log( require('util').inspect(o, { depth: 10 } ) );

      if( ! o.type ) throw new Error("Filter's 'type' attribute missing");
      if( ! Array.isArray( o.args) ) throw new Error("Filter's 'args' attribute must be an array");

      //console.log("Visiting: ", require('util').inspect( o, { depth: 10 } ) );
      if( o.type === 'and' || o.type === 'or'){
        //console.log("WILL ENTER: ", require('util').inspect( o.args ));
        o.args.forEach( function( condition ){
          //console.log("Entering:", condition );
          visitQueryConditions( condition );
        });
      } else {

        //console.log( require('util').inspect( o, { depth: 10 } ) );
        var field = o.args[ 0 ];
        // It's a local field: mark it as searchable
        if( field.indexOf( '.' ) === -1 ){

          // It's a local field: it MUST be in the schema
          if( typeof( self.schema.structure[ field ] ) === 'undefined' )
            throw new Error("Field " + field + " cannot be in query if it's not in the schema")

          self.dbLayer._makeFieldSearchable( field );
        }
      }
    }
    visitQueryConditions( self.queryConditions );
  },

  // *********************************************************************
  // *** FUNCTIONS THAT ACTUALLY ACCESS DATA THROUGH THE DB DRIVER
  // *********************************************************************

  _enrichConditionsWithParams: function( conditions, params ){

    var self = this;

    // This will have the list of items that will actually get filtered
    // (they are in self.paramIds and are also defined in `params`
    var list = [];
    var returnedConditions = conditions;
    var whereToPush;

    // Get the list of items that _actually_ need to be added
    self.paramIds.forEach( function( paramId ){
      if( params.hasOwnProperty( paramId ) ) list.push( paramId );
    });

    // If nothing needs to be added, leave filter as it is
    if( ! list.length ) return returnedConditions;

    if( conditions.type === 'and' ){
      whereToPush = conditions.args;
    } else {

      // Turn first condition into an 'and' condition
      returnedConditions = { type: 'and', args: [] };
      whereToPush = returnedConditions.args;
      if( conditions.type ) whereToPush.push( conditions );
    }

    // Add a condition for each paramId, so that it will get satisfied
    list.forEach( function( paramId ){
      whereToPush.push( { type: 'eq', args: [ paramId, params[ paramId ] ] } );
    });

    // If there is only one "and" condition, normalise it to the condition itself
    if( returnedConditions.type === 'and' && returnedConditions.args.length === 1 ){
      returnedConditions = returnedConditions.args[ 0 ];
    }

    return returnedConditions;
  },

  implementReposition: function( doc, where, beforeId, cb ){
    if( typeof( cb ) === 'undefined' ) cb = function(){};

    // No position field: nothing to do
    if( ! this.dbLayer.positionField ){
       return cb( null );
    }

    // Doesn't do much: just tell the layer to repositon
    this.dbLayer.reposition( doc, where, beforeId, cb );

  },

  implementFetchOne: function( request, cb ){

    var self = this;

    // Make up the condition, based on the store's IDs
    var conditions = {};

    // Make up the selector.
    // Remote requests need to have the full filter based on request.params. Local ones
    // only have to have (and I mean HAVE TO) the idProperty
    if( request.remote ){
       conditions = self._enrichConditionsWithParams( conditions, request.params );
    } else {
      conditions = { type: 'eq', args: [ self.idProperty, request.params[ self.idProperty ]  ] };
    }

    // Make the database call
    self.dbLayer.select( { conditions: conditions, ranges: { limit: 1 } }, { children: true }, function( err, docs ){
      if( err ) return cb( err );

      if( docs.length === 0 ) return cb( null, null );
      cb( null, docs[ 0 ] );
    });

  },

  implementInsert: function( request, forceId, cb ){

    var self = this;

    var record = self._co( request.body );

    // _children are not inserted
    delete record._children;

    // If generatedId was passed, force the record to
    // that id
    if( forceId ) record[ self.idProperty ] = forceId;

    self.dbLayer.insert( record, { skipValidation: true, children: true }, cb );
  },

  implementUpdate: function( request, deleteUnsetFields, cb ){

    var self = this;
    var updateObject;

    // Make up the condition, based on the store's IDs
    var conditions = {};

    // Make up the selector.
    // Remote requests need to have the full conditions based on request.params. Local ones
    // only have to have (and I mean HAVE TO) the idProperty
    if( request.remote ){
       conditions = self._enrichConditionsWithParams( conditions, request.params );
    } else {
      conditions = { type: 'eq', args: [ self.idProperty, request.params[ self.idProperty ]  ] };
    }

    // Make up the `updateObject` variable, based on the passed `body`
    updateObject = self._co( request.body );
    delete updateObject._children;

    self.dbLayer.update( conditions, updateObject, { deleteUnsetFields: deleteUnsetFields, multi: false, skipValidation: true }, function( err, howMany, record ){
      if( err ) return cb( err );

      // There is no point in checking howMany: `record` will be null if record wasn't found anyway,
      // and that's what we want
      cb( null, record );
    });
  },

  implementDelete: function( request, cb ){

    var self = this;
    var conditions = {};

    // Make up the selector.
    // Remote requests need to have the full conditions based on request.params. Local ones
    // only have to have (and I mean HAVE TO) the idProperty
    if( request.remote ){
       conditions = self._enrichConditionsWithParams( conditions, request.params );
    } else {
      conditions = { type: 'eq', args: [ self.idProperty, request.params[ self.idProperty ]  ] };
    }

    self.dbLayer.delete( conditions, { multi: false, skipValidation: true }, function( err, howMany, record ){
      if( err ) return cb( err );

      // There is no point in checking howMany: `record` will be null if record wasn't found anyway,
      // and that's what we want
      cb( null, record );
    });

  },

  _queryMakeDbLayerFilter: function( remote, conditionsHash, sort, ranges, cb ){

    // The simleDbLayer filter that will get returned
    var filter = {};

    var self = this;
    var errors = [];
    var field, condition, value;

    // Straight fields that do not need any conversions as they are the same
    // between JsonRestStores and SimpleDbLayer
    filter.ranges = ranges;
    filter.sort = sort;
    filter.conditions = {};

    function getQueryFromQueryConditions( ){

      // Make up filter.conditions
      function visitQueryConditions( o, fc ){

        // Check o.ifDefined. If the corresponding element in onlineSearchSchema is not
        // defined, won't go there
        if( o.ifDefined ){
          if( ! conditionsHash[ o.ifDefined ] ){ return false; }
        }

        if( o.type === 'and' || o.type === 'or'){
          fc.type = o.type;
          fc.args = [];

          o.args.forEach( function( condition ){

            // If it's 'and' or 'or', check the length of what gets returned
            if( condition.type === 'and' || condition.type === 'or' ){

              // Make up the new condition, visit that one
              var newCondition = {};
              var f = visitQueryConditions( condition, newCondition );

              // Falsy return means "don't continue"
              if( f === false ) return;

              // newCondition is empty: do not add anything to fc
              if(  newCondition.args.length === 0 ){

                return ;
              // Only one condition returned: get rid of logical operator, add the straight condition
              } else if( newCondition.args.length === 1 ){
                var actualCondition = newCondition.args[ 0 ];
                fc.args.push( { type: actualCondition.type, args: actualCondition.args } );
              // Multiple conditions returned: the logical operator makes sense
              } else {
                fc.args.push( newCondition );
              }

            // If it's a leaf
            } else {
              var newCondition = {};
              var f = visitQueryConditions( condition, newCondition );
              if( f !== false ) fc.args.push( newCondition );
            }
          });

        // It's a terminal point
        } else {
          var arg0 = o.args[ 0 ];
          var arg1 = o.args[ 1 ];

          // No arg1: most likely a unary operator, let it live.
          if( typeof( arg1 ) === 'undefined'){
            fc.type = o.type;
            fc.args = [];
            fc.args[ 0 ] = arg0;
          }

          // The second argument has a "but!".
          // If it is in form #something#, it means that it's
          // actually a field in onlineSearchSchema
          var m = ( arg1.match && arg1.match( /^#(.*?)#$/) );
          if( m ) {
            var osf = m[ 1 ];

            // If it's in form #something#, then entry MUST be in onlineSearchSchema
            if( ! self.onlineSearchSchema.structure[ osf ] ) throw new Error("Searched for " + arg1 + ", but didn't find corresponding entry in onlineSearchSchema");

            if( conditionsHash[ osf ] ){
              fc.type = o.type;
              fc.args = [];
              fc.args[ 0 ] = arg0;
              fc.args[ 1 ] = conditionsHash[ osf ];
            } else {
              // For leaves, this will tell the callee NOT to add this.
              return false;
            };

          // The second argument is not in form #something#: it means it's a STRAIGHT value
          } else {
            fc.type = o.type;
            fc.args = [];
            fc.args[ 0 ] = arg0;
            fc.args[ 1 ] = arg1;
          }
        }
      }

      // This will be returned
      var res = {};
      visitQueryConditions( self.queryConditions, res );

      // visitQueryConditions does a great job avoiding duplication, but
      // top-level duplication needs to be checked here
      if( ( res.type === 'and' || res.type === 'or' )){
        if( res.args.length === 0 ) return {};
        if( res.args.length === 1 ) return res.args[ 0 ];
      }
      return res;
    }

    filter.conditions = getQueryFromQueryConditions();

    //console.log( self.collectionName );
    //console.log("QUERYCONDITIONS", require('util').inspect( self.queryConditions, { depth: 10 } ));
    //console.log("CONDITIONS HASH:", require('util').inspect( conditionsHash, { depth: 10 } ));
    //console.log("ORIG: ", require('util').inspect( self.queryConditions, { depth: 10 } ));
    //console.log("COPY: ", require('util').inspect( getQueryFromQueryConditions(), { depth: 10 } ));

    cb( null, filter );

  },

  implementQuery: function( request, next ){

    var self = this;
    var cursor;
    var dbLayerOptions = {};

    // If options.delete was on or if it's set at store-level
    // pass {delete: true } to the db layer
    if( request.options.delete || self.deleteAfterGetQuery ){
      dbLayerOptions.delete = true;
    }

    // Pass on skipHardLimitOnQueries to the dbLayer
    dbLayerOptions.skipHardLimitOnQueries = !!request.options.skipHardLimitOnQueries;

    // Children is always true
    dbLayerOptions.children = true;

    // Paranoid checks
    if( typeof( request.options.sort ) === 'undefined' || request.options.sort === null ) request.options.sort = {};
    if( typeof( request.options.ranges ) === 'undefined' || request.options.ranges === null ) request.options.ranges = {};

    self._queryMakeDbLayerFilter( request.remote, request.options.conditions, request.options.sort, request.options.ranges, function( err, filter ){
      if( err ){
        next( err );
      } else {

        if( request.remote) filter.conditions = self._enrichConditionsWithParams( filter.conditions, request.params );

        // Run the select based on the passed parameters
        self.dbLayer.select( filter, dbLayerOptions, next );
      }
    });
  },

});
<|endoftext|>