<commit_before>#!/usr/bin/env node
var fs = require('fs')
var path = require('path')
var program = require('commander')
var chalk = require('chalk')
var plist = require('simple-plist')
var config = require('./config').get()

var pluginDirectory = config.pluginDirectory

function testDevMode (then) {
  var prefPath = path.join(require('os').homedir(), 'Library/Preferences/com.bohemiancoding.sketch3.plist')
  var data = plist.readFileSync(prefPath)

  if (!data.AlwaysReloadScript) {
    var yesno = require('yesno')
    console.log('The sketch developer mode is not enabled ' + chalk.dim('(http://developer.sketchapp.com/introduction/preferences/#always-reload-scripts-before-running)') + '.')
    yesno.ask('Do you want to enable it? (y/N)', false, function (ok) {
      if (ok) {
        data.AlwaysReloadScript = true
        plist.writeBinaryFileSync(prefPath, data)
      }
      then()
    })
  } else {
    then()
  }
}

program
  .usage('[options] <path>')
  .arguments('<path>')
  .action(function (path) {
    program.path = path
  })
  .parse(process.argv)

if (!program.path) {
  program.path = '.'
}

if (program.path.indexOf(pluginDirectory) !== -1) {
  console.error(chalk.red('error') + ' The path should be the one pointing to your new plugin folder, not the sketch plugins folder')
  process.exit(1)
}

function getPath (file) {
  return program.path[0] === '/'
  ? path.join(program.path, file) // absolute path
  : path.join(process.cwd(), program.path, file) // relative path
}

var packageJSON
try {
  packageJSON = require(getPath('package.json'))
} catch (err) {
  console.error(chalk.red('error') + ' Error while reading the package.json file')
  console.error(err)
  process.exit(1)
}

if (!packageJSON.main) {
  console.error(chalk.red('error') + ' Missing "main" field in the package.json. Should point to the ".sketchplugin" file')
  process.exit(1)
}

if (!packageJSON.name) {
  console.error(chalk.red('error') + ' Missing "name" field in the package.json.')
  process.exit(1)
}

console.log(chalk.dim('[1/1]') + ' 🔗  Symlinking the plugin ' + packageJSON.name + '...')

try {
  if (!fs.existsSync(path.join(pluginDirectory, packageJSON.name))) {
    fs.mkdirSync(path.join(pluginDirectory, packageJSON.name))
  }
  fs.symlinkSync(getPath(packageJSON.main), path.join(pluginDirectory, packageJSON.name, packageJSON.main))

  testDevMode(function () {
    console.log(chalk.green('success') + ' Plugin ' + packageJSON.name + ' symlinked')
    console.log(chalk.blue(packageJSON.name) + ' - ' + chalk.grey(packageJSON.version))
    process.exit(0)
  })
} catch (err) {
  console.log(chalk.red('error') + ' Error while symlinking the plugin ' + packageJSON.name)
  console.log((err || {}).body || err)
  process.exit(1)
}
<commit_msg>Generating a clearer error message when a plugin has already been linked<commit_after>#!/usr/bin/env node
var fs = require('fs')
var path = require('path')
var program = require('commander')
var chalk = require('chalk')
var plist = require('simple-plist')
var config = require('./config').get()

var pluginDirectory = config.pluginDirectory

function testDevMode (then) {
  var prefPath = path.join(require('os').homedir(), 'Library/Preferences/com.bohemiancoding.sketch3.plist')
  var data = plist.readFileSync(prefPath)

  if (!data.AlwaysReloadScript) {
    var yesno = require('yesno')
    console.log('The sketch developer mode is not enabled ' + chalk.dim('(http://developer.sketchapp.com/introduction/preferences/#always-reload-scripts-before-running)') + '.')
    yesno.ask('Do you want to enable it? (y/N)', false, function (ok) {
      if (ok) {
        data.AlwaysReloadScript = true
        plist.writeBinaryFileSync(prefPath, data)
      }
      then()
    })
  } else {
    then()
  }
}

program
  .usage('[options] <path>')
  .arguments('<path>')
  .action(function (path) {
    program.path = path
  })
  .parse(process.argv)

if (!program.path) {
  program.path = '.'
}

if (program.path.indexOf(pluginDirectory) !== -1) {
  console.error(chalk.red('error') + ' The path should be the one pointing to your new plugin folder, not the sketch plugins folder')
  process.exit(1)
}

function getPath (file) {
  return program.path[0] === '/'
  ? path.join(program.path, file) // absolute path
  : path.join(process.cwd(), program.path, file) // relative path
}

var packageJSON
try {
  packageJSON = require(getPath('package.json'))
} catch (err) {
  console.error(chalk.red('error') + ' Error while reading the package.json file')
  console.error(err)
  process.exit(1)
}

if (!packageJSON.main) {
  console.error(chalk.red('error') + ' Missing "main" field in the package.json. Should point to the ".sketchplugin" file')
  process.exit(1)
}

if (!packageJSON.name) {
  console.error(chalk.red('error') + ' Missing "name" field in the package.json.')
  process.exit(1)
}

console.log(chalk.dim('[1/1]') + ' 🔗  Symlinking the plugin ' + packageJSON.name + '...')

try {
  if (fs.existsSync(path.join(pluginDirectory, packageJSON.name))) {
    return console.log(chalk.red('error') + ' This plugin has already been linked.')
  }

  fs.mkdirSync(path.join(pluginDirectory, packageJSON.name))
  fs.symlinkSync(getPath(packageJSON.main), path.join(pluginDirectory, packageJSON.name, packageJSON.main))

  testDevMode(function () {
    console.log(chalk.green('success') + ' Plugin ' + packageJSON.name + ' symlinked')
    console.log(chalk.blue(packageJSON.name) + ' - ' + chalk.grey(packageJSON.version))
    process.exit(0)
  })
} catch (err) {
  console.log(chalk.red('error') + ' Error while symlinking the plugin ' + packageJSON.name)
  console.log((err || {}).body || err)
  process.exit(1)
}
<|endoftext|>


<commit_before>'use babel';

import { CompositeDisposable, Disposable, Point, Range } from 'atom';

export default {
  subscriptions: null,

  config: {
    autoHardWrap: {
      type: 'boolean',
      default: true
    },
    columnGuide: {
      type: 'integer',
      default: 80
    }
  },

  // FIXME: May not work for Windows.
  newline: /\n/,

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

    // Using observeTextEditors can make sure all future
    // editors are getting this event subscription.
    atom.workspace.observeTextEditors((editor) => {
      // Register command that wraps the current buffer
      this.subscriptions.add(atom.commands.add('atom-text-editor', {
        'slickwrap:wrap-buffer': () => this.wrapBuffer()
      }));

      var editorView = atom.views.getView(editor);
      this.subscribeEvent(editorView, 'keyup', (event) => {
        // keycode: 32 -> space, 13 -> enter, 9 -> tab
        var code = event.keyCode;
        if (code == 32 || code == 13 || code == 9) {
          if (atom.config.get('slickwrap.autoHardWrap')) {
            this.adjust(code);
          }
        }
      });
    });
  },

  subscribeEvent(observed, type, callback) {
    observed.addEventListener(type, callback);
    this.subscriptions.add(new Disposable(() => {
      observed.removeEventListener(type, callback)}));
  },

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

  wrapBuffer() {
    var editor = atom.workspace.getActiveTextEditor();
    var buffer = editor.getBuffer();
    buffer.getLines().map((t, i) => this.adjustRow(i));
  },
    // var buffer = editor.getBuffer();
    // var cursorPosition = editor.getCursorBufferPosition();
    // var cursorIndex = buffer.characterIndexForPosition(cursorPosition);

  adjustRow(row) {
    var editor = atom.workspace.getActiveTextEditor();
    var buffer = editor.getBuffer();
    var maxLen = atom.config.get('slickwrap.columnGuide');

    // if the line that contains cursor is longer than our limitation,
    // wrap the line at the closest whitespace before the limitation column.
    var text = buffer.lineForRow(row);

    // text is longer than our column guide
    // adjust the currentline by inserting newline character(s).
    if (text.length > maxLen) {
      // |i am a cake
      // |l         r
      var l = 0;
      var r = text.length - 1;

      // skip white spaces
      while (this.isWS(text[l]) && l < r) ++l;
      while (this.isWS(text[r]) && l < r) --r;

      // the actual text is not exceeding boundary so ignore
      if (r < maxLen || l >= r) return;

      // find left "gap", if we can find any, wrap.
      if (l < maxLen) {
        var li = maxLen;
        while (!this.isWS(text[li]) && li > l) --li;
        if (li == l) {
          var ri = maxLen;
          while (!this.isWS(text[ri]) && ri < r) ++ ri;
          if (ri == r) return;
          var rl = ri;
          while (this.isWS(text[rl])) --rl;
          var rr = ri;
          while (this.isWS(text[rr])) ++rr;
          this.setText(row, rl+1, rr);
          return;
        }
        var ll = li;
        while (this.isWS(text[ll])) --ll;
        var lr = li;
        while (this.isWS(text[lr])) ++lr;
        this.setText(row, ll+1, lr);
      }
    }

  },

  setText(row, left, right) {
    var editor = atom.workspace.getActiveTextEditor();
    var buffer = editor.getBuffer();
    var lbound = new Point(row, left);
    var rbound = new Point(row, right);
    buffer.setTextInRange(new Range(lbound, rbound), '\n');
  },

  // Adjust the current line and set cursor to the end.
  // mimicing the emacs behavior.
  adjust(code) {
    var editor = atom.workspace.getActiveTextEditor();
    var cursor = editor.getCursorBufferPosition();

    // if it's enter, we look at the previous row
    var row = (code == 13) ? cursor.row - 1 : cursor.row;

    this.adjustRow(row);
  },

  isWS(ch) {
    return ch == ' ' || ch == '\t';
  },

};
<commit_msg>Added supported extensions config.<commit_after>'use babel';

import { CompositeDisposable, Disposable, Point, Range } from 'atom';

export default {
  subscriptions: null,

  config: {
    autoHardWrap: {
      type: 'boolean',
      default: true
    },
    columnGuide: {
      type: 'integer',
      default: 80
    },
    supportedFiles: {
      type: 'array',
      default: ['md'],
      items: {
        type: 'string'
      }
    }
  },

  // FIXME: May not work for Windows.
  newline: /\n/,

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

    // Using observeTextEditors can make sure all future
    // editors are getting this event subscription.
    atom.workspace.observeTextEditors((editor) => {
      // Register command that wraps the current buffer
      this.subscriptions.add(atom.commands.add('atom-text-editor', {
        'slickwrap:wrap-buffer': () => this.wrapBuffer()
      }));

      var editorView = atom.views.getView(editor);
      this.subscribeEvent(editorView, 'keyup', (event) => {
        // keycode: 32 -> space, 13 -> enter, 9 -> tab
        var code = event.keyCode;
        var path = editor.getPath();
        var ext = path.substr((~-path.lastIndexOf(".") >>> 0) + 2);
        var supportedExts = atom.config.get('slickwrap.supportedFiles');
        var supported = (supportedExts.indexOf(ext) != -1);

        if ((code == 32 || code == 13 || code == 9) && supported) {
          if (atom.config.get('slickwrap.autoHardWrap')) {
            this.adjust(code);
          }
        }
      });
    });
  },

  subscribeEvent(observed, type, callback) {
    observed.addEventListener(type, callback);
    this.subscriptions.add(new Disposable(() => {
      observed.removeEventListener(type, callback)}));
  },

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

  wrapBuffer() {
    var editor = atom.workspace.getActiveTextEditor();
    var buffer = editor.getBuffer();
    buffer.getLines().map((t, i) => this.adjustRow(i));
  },
    // var buffer = editor.getBuffer();
    // var cursorPosition = editor.getCursorBufferPosition();
    // var cursorIndex = buffer.characterIndexForPosition(cursorPosition);

  adjustRow(row) {
    var editor = atom.workspace.getActiveTextEditor();
    var buffer = editor.getBuffer();
    var maxLen = atom.config.get('slickwrap.columnGuide');

    // if the line that contains cursor is longer than our limitation,
    // wrap the line at the closest whitespace before the limitation column.
    var text = buffer.lineForRow(row);

    // text is longer than our column guide
    // adjust the currentline by inserting newline character(s).
    if (text.length > maxLen) {
      // |i am a cake
      // |l         r
      var l = 0;
      var r = text.length - 1;

      // skip white spaces
      while (this.isWS(text[l]) && l < r) ++l;
      while (this.isWS(text[r]) && l < r) --r;

      // the actual text is not exceeding boundary so ignore
      if (r < maxLen || l >= r) return;

      // find left "gap", if we can find any, wrap.
      if (l < maxLen) {
        var li = maxLen;
        while (!this.isWS(text[li]) && li > l) --li;
        if (li == l) {
          var ri = maxLen;
          while (!this.isWS(text[ri]) && ri < r) ++ ri;
          if (ri == r) return;
          var rl = ri;
          while (this.isWS(text[rl])) --rl;
          var rr = ri;
          while (this.isWS(text[rr])) ++rr;
          this.setText(row, rl+1, rr);
          return;
        }
        var ll = li;
        while (this.isWS(text[ll])) --ll;
        var lr = li;
        while (this.isWS(text[lr])) ++lr;
        this.setText(row, ll+1, lr);
      }
    }

  },

  setText(row, left, right) {
    var editor = atom.workspace.getActiveTextEditor();
    var buffer = editor.getBuffer();
    var lbound = new Point(row, left);
    var rbound = new Point(row, right);
    buffer.setTextInRange(new Range(lbound, rbound), '\n');
  },

  // Adjust the current line and set cursor to the end.
  // mimicing the emacs behavior.
  adjust(code) {
    var editor = atom.workspace.getActiveTextEditor();
    var cursor = editor.getCursorBufferPosition();

    // if it's enter, we look at the previous row
    var row = (code == 13) ? cursor.row - 1 : cursor.row;

    this.adjustRow(row);
  },

  isWS(ch) {
    return ch == ' ' || ch == '\t';
  },

};
<|endoftext|>


<commit_before>var duplexer = require('duplexer2')
var parser = require('tap-out')
var PassThrough = require('stream').PassThrough

module.exports = function () {
  var tap = parser()
  var output = PassThrough()
  var dup = duplexer(tap, output)
  dup.output = output

  var test
  var duration = 0

  function handleTestEnd() {
    if (test) {
      test.end = new Date()
      test.duration = test.end - test.start
      duration += test.duration
      dup.emit('test.end', test)
    }
  }

  tap.on('test', function (res) {
    handleTestEnd()
    test = {
      name: res.name,
      pass: 0,
      fail: 0,
      start: new Date(),
    }
    dup.emit('test.start', test)
  })

  tap.on('pass', function () {
    ++test.pass
    dup.emit('test.pass', test)
  })

  tap.on('fail', function () {
    ++test.fail
    dup.emit('test.fail', test)
  })

  tap.on('output', function (res) {
    handleTestEnd()

    if (res.fail.length) {
      dup.failed = true
    }

    dup.emit('summary', {
      duration: duration,
      assertions: res.asserts.length,
      pass: res.pass.length,
      fail: res.fail.length,
      comments: res.comments.length,
    }, res.fail.reduce(function (o, assertion) {
      var name = getTest(assertion.test, res.tests).name
      o[name] = o[name] || []
      o[name].push(assertion)
      return o
    }, {}), res.comments.reduce(function (o, assertion) {
      var name = getTest(assertion.test, res.tests).raw
      o[name] = o[name] || []
      o[name].push(assertion.raw)
      return o
    }, {}), res)
  })

  return dup
}

function getTest(n, tests) {
  for (var i = 0, len = tests.length; i < len; ++i) {
    if (~~n === tests[i].number) {
      return tests[i]
    }
  }
  return null
}

<commit_msg>Add check for plans on output, fixes #8<commit_after>var duplexer = require('duplexer2')
var parser = require('tap-out')
var PassThrough = require('stream').PassThrough

module.exports = function () {
  var tap = parser()
  var output = PassThrough()
  var dup = duplexer(tap, output)
  dup.output = output

  var test
  var duration = 0

  function handleTestEnd() {
    if (test) {
      test.end = new Date()
      test.duration = test.end - test.start
      duration += test.duration
      dup.emit('test.end', test)
    }
  }

  tap.on('test', function (res) {
    handleTestEnd()
    test = {
      name: res.name,
      pass: 0,
      fail: 0,
      start: new Date(),
    }
    dup.emit('test.start', test)
  })

  tap.on('pass', function () {
    ++test.pass
    dup.emit('test.pass', test)
  })

  tap.on('fail', function () {
    ++test.fail
    dup.emit('test.fail', test)
  })

  tap.on('output', function (res) {
    handleTestEnd()

    if (res.fail.length) {
      dup.failed = true
    }

    if (res.plans.length < 1) {
      dup.failed = true
      process.exit(1)
    }

    dup.emit('summary', {
      duration: duration,
      assertions: res.asserts.length,
      pass: res.pass.length,
      fail: res.fail.length,
      comments: res.comments.length,
    }, res.fail.reduce(function (o, assertion) {
      var name = getTest(assertion.test, res.tests).name
      o[name] = o[name] || []
      o[name].push(assertion)
      return o
    }, {}), res.comments.reduce(function (o, assertion) {
      var name = getTest(assertion.test, res.tests).raw
      o[name] = o[name] || []
      o[name].push(assertion.raw)
      return o
    }, {}), res)
  })

  return dup
}

function getTest(n, tests) {
  for (var i = 0, len = tests.length; i < len; ++i) {
    if (~~n === tests[i].number) {
      return tests[i]
    }
  }
  return null
}
<|endoftext|>


<commit_before>var nunjucks = require('nunjucks');
var moment = require('moment');
var qs = require('qs');
var he = require('he');

/**
 * templates
 * @augments Augments the apos object with facilities for rendering Nunjucks templates
 * with access to appropriate data.
 * @see  aposLocals
 */

var _ = require('lodash');
var async = require('async');

module.exports = function(self) {
  // Render the specified `template` and send the result via the specified `res` object. The properties of the
  // `info` object are made available to Nunjucks template code. A wrapper for `apos.partial`.

  self.render = function(req, res, template, info) {
    return res.send(self.partial(req, template, info));
  };

  // Load and render a Nunjucks template by the specified name and give it the
  // specified data. All of the Apostrophe helpers are available as
  // aposArea, etc. from the template. You can also render another partial
  // from within your template by calling `{{ partial('name') }}`. You can pass a
  // full path for 'name' otherwise it is assumed to be relative to the first
  // directory on the `dirs` list that contains a matching file. When you call
  // `partial` from another partial, the dirs list is always the same list given
  // to the original `partial` call.
  //
  // The `views` folder of the Apostrophe module is always implicitly included in
  // the `dirs` list as the last entry. Earlier entries beat later ones, allowing
  // for easy overrides.
  //
  // The .html extension is assumed if no extension is present.

  self.partial = function(req, name, data, dirs) {
    if(typeof req === 'string'){
      // in case we didn't get any req object
      dirs = data;
      data = name;
      name = req;
      req = null;
    } else if(typeof req === 'object'){
      self.initI18nLocal(req);
    }

    if (!data) {
      data = {};
    }

    data.partial = function(name, data) {
      return self.partial(req, name, data, dirs);
    };

    // Make sure the apos-specific locals are visible to partials too.
    // If we import ALL the locals we'll point at the wrong views directory
    // and generally require the developer to worry about not breaking
    // our partials, which ought to be a black box they can ignore.

    _.defaults(data, self._aposLocals);

    var finalName = name;
    if (!finalName.match(/\.\w+$/)) {
      finalName += '.html';
    }

    return self.getNunjucksEnv(dirs).getTemplate(finalName).render(data);
  };

  // Render the Nunjucks template in the provided string and give it the
  // specified data. All of the Apostrophe helpers are available as
  // aposArea, etc. from the template. You can also render another partial
  // from within your template by calling `{{ partial('name') }}`. The
  // `views` folder of the Apostrophe module is always implicitly included in
  // the `dirs` list as the last entry. Earlier entries beat later ones, allowing
  // for easy overrides.
  //
  // This is mostly used by the mailMixin to render subject lines.

  self.partialString = function(s, data, dirs) {
    if (!data) {
      data = {};
    }

    if (typeof(data.partial) === 'undefined') {
      data.partial = self.partial;
    }

    // Make sure the apos-specific locals are visible to partials too.
    // If we import ALL the locals we'll point at the wrong views directory
    // and generally require the developer to worry about not breaking
    // our partials, which ought to be a black box they can ignore.

    _.defaults(data, self._aposLocals);

    return self.getNunjucksEnv(dirs).renderString(s, data);
  };

  var nunjucksEnvs = {};

  /**
   * Get a nunjucks environment in which "include," "extends," etc. search the
   * specified directories. You may specify a single directory or skip the
   * parameter. The views folder of the Apostrophe module is always the
   * last directory searched and you do not need to add it explicitly.
   * USUALLY YOU SHOULD USE `apos.partial` which invokes this method automatically.
   * Note you can specify search directories when calling `apos.partial`.
   * @param  {Array} dirs
   * @return {Object} a nunjucks environment
   */
  self.getNunjucksEnv = function(dirs) {
    if (!dirs) {
      dirs = [];
    }

    if (!Array.isArray(dirs)) {
      dirs = [ dirs ];
    }

    dirs = dirs.concat(self.options.partialPaths || []);

    // The apostrophe module's views directory is always the last
    // thing tried, so that you can extend the widgetEditor template, etc.
    dirs = dirs.concat([ __dirname + '/../views' ]);

    var dirsKey = dirs.join(':');
    if (!nunjucksEnvs[dirsKey]) {
      nunjucksEnvs[dirsKey] = self.newNunjucksEnv(dirs);
    }
    return nunjucksEnvs[dirsKey];
  };

  // Stringify the data as JSON, then escape any sequences that would
  // cause a


<commit_before>'use strict';
/*global require, module, self*/

(function(module) {
	var templates = {
			cache: {},
			globals: {}
		},
		helpers = {},
		loader,
		worker;

	var regexes = {
		nestedConditionals: /(?!^)<!-- IF([\s\S]*?)ENDIF[ a-zA-Z0-9\._:]*-->(?!$)/gi,
		conditionalBlock: /[\r\n?\n]*?<!-- ELSE -->[\r\n?\n]*?/,
		conditionalHelper: /IF function.([\s\S]*?)-->/i,
		removeTabspace: /^\t*?|^\r\n?\t*?|\t?$|\r\n?\t*?$/g,
		removeWhitespace: /(^[\r\n?|\n]*)|([\r\n\t]*$)/g,
		cleanupEmptyLoops: /\s*<!-- BEGIN([\s\S]*?)END ([\s\S]*?)-->/gi,
		cleanupMissingKeys: /[\r\n]*?\{[a-zA-Z0-9\.]+[\r\n]*?\}/g,
		getUndefinedKeys: /<!-- IF[\s!]*([\s\S]*?)[\s]*-->/gi,
		backReferenceFix: /\$+/g
	};

	if (typeof self !== 'undefined' && self.addEventListener) {
		self.addEventListener('message', function(ev) {
			var data = ev.data;

			self.postMessage({
				result: !data.block ? templates.parse(data.template, data.object) : templates.parse(data.template, data.block, data.object),
				signature: data.signature
			});
		}, false);
	}

	var callbacks = {},
		signature = 0,
		MAX_SAFE_INT = Math.pow(2, 53) - 1;

	templates.setupWebWorker = function(pathToScript) {
		try {
			worker = new Worker(pathToScript);

			worker.addEventListener('message', function(e) {
				callbacks[e.data.signature](e.data.result);
			}, false);
		} catch (err) {}
	};

	function launchWorker(template, obj, block, callback) {
		signature++;
		if (signature > MAX_SAFE_INT) {
			signature = 0;
		}

		worker.postMessage({
			template: template,
			object: obj,
			block: block,
			signature: signature
		});

		callbacks[signature] = function(result) {
			callback(result);
			delete callbacks[signature];
		};
	}

	templates.parse = function(template, block, obj, callback) {
		if (typeof block !== 'string') {
			callback = obj;
			obj = block;
			block = false;
		}

		if (!template) {
			return callback('');
		}

		obj = registerGlobals(obj || {});

		if (loader && callback) {
			if (!templates.cache[template]) {
				loader(template, function(loaded) {
					templates.cache[template] = loaded;

					launchCallback(loaded, obj, block, callback);
				});
			} else {
				launchCallback(templates.cache[template], obj, block, callback);
			}
		} else if (callback) {
			launchCallback(template, obj, block, callback);
		} else {
			return parseTemplate(block, template, obj);
		}
	};

	function launchCallback(template, obj, block, callback) {
		if (worker) {
			launchWorker(template, obj, block, callback);
		} else {
			callback(parseTemplate(block, template, obj));
		}
	}

	function parseTemplate(block, template, obj) {
		block = !block ? template : templates.getBlock(template, block);
		template = parse(block, obj);

		return parseFunctions(template, template, obj);
	}

	templates.registerHelper = function(name, func) {
		helpers[name] = func;
	};

	templates.registerLoader = function(func) {
		loader = func;
	};

	templates.setGlobal = function(key, value) {
		templates.globals[key] = value;
	};

	templates.getBlock = function(template, block) {
		return template.replace(new RegExp('[\\s\\S]*(<!--[\\s]*BEGIN ' + block + '[\\s]*-->[\\s\\S]*?<!--[\\s]*END ' + block + '[\\s]*-->)[\\s\\S]*', 'g'), '$1');
	};

	templates.flush = function() {
		templates.cache = {};
	};

	function express(filename, options, fn) {
		var fs = require('fs'),
			tpl = filename.replace(options.settings.views + '/', '');

		options._locals = null;

		if (!templates.cache[tpl]) {
			fs.readFile(filename, function(err, html) {
				templates.cache[tpl] = (html || '').toString();
				return fn(err, templates.parse(templates.cache[tpl], options));
			});
		} else {
			return fn(null, templates.parse(templates.cache[tpl], options));
		}
	}

	function replace(string, regex, value) {
		return string.replace(regex, value.toString().replace(regexes.backReferenceFix, '$$$'));
	}

	function replaceValue(template, namespacedKey, value) {
		var string = generateSubspacedRegex(namespacedKey);
		return replace(template, new RegExp('{' + string + '}', 'g'), value);
	}

	function generateSubspacedRegex(namespacedKey) {
		var prefix = '';

		if (namespacedKey.substring(0,1) === '!') {
			prefix = '!';
			namespacedKey = namespacedKey.substring(1);
		}

		var subspace = namespacedKey.split('.'),
			key = subspace.pop(),
			string = [];

		while(subspace.length) {
			string.push(subspace.join('.') + '.');
			subspace.shift();
		}

		string = string ? ('(' + string.join('|') + ')?' + key) : key;
		return prefix + string;
	}

	function makeRegex(block, namespace) {
		namespace = '(' + namespace + ')?';
		return new RegExp('[\\t ]*<!--[\\s]*BEGIN ' + namespace + block + '[\\s]*-->[\\s\\S]*?<!--[\\s]*END ' + namespace + block + '[\\s]*-->');
	}

	function makeBlockRegex(block, namespace) {
		namespace = '(' + namespace + ')?';
		return new RegExp('([\\t ]*<!--[\\s]*BEGIN ' + namespace + block + '[\\s]*-->[\\r\\n?|\\n]?)|(<!--[\\s]*END ' + namespace + block + '[\\s]*-->)', 'g');
	}

	function makeConditionalRegex(namespacedKey) {
		var block = generateSubspacedRegex(namespacedKey);
		return new RegExp('<!--[\\s]*IF ' + block + '[\\s]*-->([\\s\\S]*?)<!--[\\s]*ENDIF ' + block.split(',')[0] + '[\\s]*-->', 'g');
	}

	function makeStatementRegex(namespacedKey) {
		var key = generateSubspacedRegex(namespacedKey);
		return new RegExp('(<!--[\\s]*IF ' + key + '[\\s]*-->)|(<!--[\\s]*ENDIF ' + key.split(',')[0] + '[\\s]*-->)', 'g');
	}

	function registerGlobals(obj) {
		for (var g in templates.globals) {
			if (templates.globals.hasOwnProperty(g)) {
				obj[g] = obj[g] || templates.globals[g];
			}
		}

		return obj;
	}

	function checkConditionals(template, key, value, dev) {
		return checkConditional(checkConditional(template, '!' + key, !value, dev), key, value, dev);
	}

	function checkConditional(template, key, value, dev) {
		var matches = template.match(makeConditionalRegex(key));

		if (matches !== null) {
			for (var i = 0, ii = matches.length; i < ii; i++) {
				var statement = makeStatementRegex(key),
					nestedConditionals = matches[i].match(regexes.nestedConditionals),
					match = replace(matches[i].replace(statement, ''), regexes.nestedConditionals, '<!-- NESTED -->'),
					conditionalBlock = match.split(regexes.conditionalBlock);

				if (conditionalBlock[1]) {
					// there is an else statement
					template = replace(template, matches[i], replace(conditionalBlock[value ? 0 : 1], regexes.removeWhitespace, ''));
				} else {
					// regular if statement
					template = replace(template, matches[i], value ? replace(match, regexes.removeWhitespace, '') : '');
				}

				if (nestedConditionals) {
					for (var x = 0, xx = nestedConditionals.length; x < xx; x++) {
						template = replace(template, '<!-- NESTED -->', nestedConditionals[x]);
					}
				}
			}
		}

		return template;
	}

	function checkConditionalHelpers(template, obj) {
		var string,
			func;

		while ((string = template.match(regexes.conditionalHelper)) !== null) {
			string = string[1].trim();
			var args = string.split(/[ ]*,[ ]*/);
			func = args[0];

			if (helpers[func]) {
				args.shift();
				args.unshift(obj);
				template = checkConditionals(template, 'function.' + string, helpers[func].apply(null, args), 'dev');
			}
		}

		return template;
	}

	function callMethod(method, parameters) {
		return method.apply(templates, parameters);
	}

	function parseFunctions(block, result, obj) {
		var functions = block.match(/{function.*?}/gi, '');
		if (!functions) {
			return result;
		}

		for (var i=0, ii=functions.length; i<ii; ++i) {
			var search = functions[i],
				fn = functions[i].replace('{function.', '').split('}').shift().split(/[ ]*,[ ]*/),
				method = fn.shift(),
				parameters = [];

			if (fn.length) {
				for (var j = 0, jj = fn.length; j < jj; j++) {
					parameters.push(obj[fn[j]]);
				}
			} else {
				parameters = [obj];
			}

			if (helpers[method]) {
				result = replace(result, new RegExp(search, 'gi'), callMethod(helpers[method], parameters));
			}
		}

		return result;
	}

	function parseObject(template, array, key, namespace) {
		return parseArray(template, array, key, namespace, true);
	}

	function parseArray(template, array, key, namespace, isObject) {
		template = checkConditionals(template, namespace  + key + '.length', array[key].length);

		var regex = makeRegex(key, namespace), block, result;

		if (!array[key].length && !isObject) {
			return template.replace(regex, '');
		}

		while ((block = template.match(regex)) !== null) {
			block = block[0].replace(makeBlockRegex(key, namespace), '');
			result = parseArrayBlock(template, block, array[key], namespace + key + '.', isObject);
			template = replace(template, regex, result.replace(regexes.removeWhitespace, ''));
		}

		return template;
	}

	function parseArrayBlock(template, block, array, namespace, isObject) {
		var numblocks = array.length - 1,
			result = '';

		for (var iterator in array) {
			if (array.hasOwnProperty(iterator)) {
				if (!isObject) {
					iterator = parseInt(iterator, 10);
				}

				result += parse(block, array[iterator], namespace);
				if (!isObject) {
					result = checkConditionals(result, '@first', iterator === 0);
					result = checkConditionals(result, '@last', iterator === numblocks);
				}

				result = result
					.replace(regexes.removeTabspace, '');

				if (!isObject) {
					result = result.replace('@index', iterator);
				} else {
					result = result
						.replace('@key', iterator)
						.replace('@value', array[iterator]);
				}

				result = parseFunctions(block, result, array[iterator]);
			}
		}

		return result;
	}

	function parseValue(template, key, value) {
		template = checkConditionals(template, key, value);
		return replaceValue(template, key, value);
	}

	function parse(template, obj, namespace) {
		namespace = namespace || '';

		for (var key in obj) {
			if (obj.hasOwnProperty(key)) {
				if (typeof obj[key] === 'undefined' || typeof obj[key] === 'function') {
					continue;
				} else if (obj[key] === null) {
					template = replaceValue(template, namespace + key, '');
				} else if (obj[key].constructor === Array) {
					template = replaceValue(template, namespace + key + '.length', obj[key].length);
					template = parseArray(template, obj, key, namespace);
				} else if (obj[key] instanceof Object) {
					template = parseObject(template, obj, key, namespace);
					template = checkConditionals(template, key, obj[key]);
					template = parse(template, obj[key], namespace + key + '.');
				} else {
					template = parseValue(template, namespace + key, obj[key]);
				}
			}
		}

		if (namespace) {
			template = template.replace(new RegExp('{' + namespace + '\\.[\\s\\S]*?}', 'g'), '');
			namespace = '';
		} else {
			template = checkConditionalHelpers(template, obj);
			template = cleanup(template);
		}

		return template;
	}

	function cleanup(template) {
		template = template
			.replace(regexes.cleanupEmptyLoops, '')
			.replace(regexes.cleanupMissingKeys, '');

		var undefinedKey,
			undefinedKeys = {},
			parseUndefined;

		while ((undefinedKey = regexes.getUndefinedKeys.exec(template)) !== null) {
			undefinedKeys[undefinedKey[1]] = false;
			parseUndefined = true;
		}

		return parseUndefined ? parse(template, undefinedKeys, '') : template;
	}

	module.exports = templates;
	module.exports.__express = express;

	if ('undefined' !== typeof window) {
		window.templates = module.exports;
	}

})('undefined' === typeof module ? {
	module: {
		exports: {}
	}
} : module);
<commit_msg>prevent crash if cb isn't supplied<commit_after>'use strict';
/*global require, module, self*/

(function(module) {
	var templates = {
			cache: {},
			globals: {}
		},
		helpers = {},
		loader,
		worker;

	var regexes = {
		nestedConditionals: /(?!^)<!-- IF([\s\S]*?)ENDIF[ a-zA-Z0-9\._:]*-->(?!$)/gi,
		conditionalBlock: /[\r\n?\n]*?<!-- ELSE -->[\r\n?\n]*?/,
		conditionalHelper: /IF function.([\s\S]*?)-->/i,
		removeTabspace: /^\t*?|^\r\n?\t*?|\t?$|\r\n?\t*?$/g,
		removeWhitespace: /(^[\r\n?|\n]*)|([\r\n\t]*$)/g,
		cleanupEmptyLoops: /\s*<!-- BEGIN([\s\S]*?)END ([\s\S]*?)-->/gi,
		cleanupMissingKeys: /[\r\n]*?\{[a-zA-Z0-9\.]+[\r\n]*?\}/g,
		getUndefinedKeys: /<!-- IF[\s!]*([\s\S]*?)[\s]*-->/gi,
		backReferenceFix: /\$+/g
	};

	if (typeof self !== 'undefined' && self.addEventListener) {
		self.addEventListener('message', function(ev) {
			var data = ev.data;

			self.postMessage({
				result: !data.block ? templates.parse(data.template, data.object) : templates.parse(data.template, data.block, data.object),
				signature: data.signature
			});
		}, false);
	}

	var callbacks = {},
		signature = 0,
		MAX_SAFE_INT = Math.pow(2, 53) - 1;

	templates.setupWebWorker = function(pathToScript) {
		try {
			worker = new Worker(pathToScript);

			worker.addEventListener('message', function(e) {
				callbacks[e.data.signature](e.data.result);
			}, false);
		} catch (err) {}
	};

	function launchWorker(template, obj, block, callback) {
		signature++;
		if (signature > MAX_SAFE_INT) {
			signature = 0;
		}

		worker.postMessage({
			template: template,
			object: obj,
			block: block,
			signature: signature
		});

		callbacks[signature] = function(result) {
			callback(result);
			delete callbacks[signature];
		};
	}

	templates.parse = function(template, block, obj, callback) {
		if (typeof block !== 'string') {
			callback = obj;
			obj = block;
			block = false;
		}

		callback = callback || function() {};

		if (!template) {
			return callback('');
		}

		obj = registerGlobals(obj || {});

		if (loader && callback) {
			if (!templates.cache[template]) {
				loader(template, function(loaded) {
					templates.cache[template] = loaded;

					launchCallback(loaded, obj, block, callback);
				});
			} else {
				launchCallback(templates.cache[template], obj, block, callback);
			}
		} else if (callback) {
			launchCallback(template, obj, block, callback);
		} else {
			return parseTemplate(block, template, obj);
		}
	};

	function launchCallback(template, obj, block, callback) {
		if (worker) {
			launchWorker(template, obj, block, callback);
		} else {
			callback(parseTemplate(block, template, obj));
		}
	}

	function parseTemplate(block, template, obj) {
		block = !block ? template : templates.getBlock(template, block);
		template = parse(block, obj);

		return parseFunctions(template, template, obj);
	}

	templates.registerHelper = function(name, func) {
		helpers[name] = func;
	};

	templates.registerLoader = function(func) {
		loader = func;
	};

	templates.setGlobal = function(key, value) {
		templates.globals[key] = value;
	};

	templates.getBlock = function(template, block) {
		return template.replace(new RegExp('[\\s\\S]*(<!--[\\s]*BEGIN ' + block + '[\\s]*-->[\\s\\S]*?<!--[\\s]*END ' + block + '[\\s]*-->)[\\s\\S]*', 'g'), '$1');
	};

	templates.flush = function() {
		templates.cache = {};
	};

	function express(filename, options, fn) {
		var fs = require('fs'),
			tpl = filename.replace(options.settings.views + '/', '');

		options._locals = null;

		if (!templates.cache[tpl]) {
			fs.readFile(filename, function(err, html) {
				templates.cache[tpl] = (html || '').toString();
				return fn(err, templates.parse(templates.cache[tpl], options));
			});
		} else {
			return fn(null, templates.parse(templates.cache[tpl], options));
		}
	}

	function replace(string, regex, value) {
		return string.replace(regex, value.toString().replace(regexes.backReferenceFix, '$$$'));
	}

	function replaceValue(template, namespacedKey, value) {
		var string = generateSubspacedRegex(namespacedKey);
		return replace(template, new RegExp('{' + string + '}', 'g'), value);
	}

	function generateSubspacedRegex(namespacedKey) {
		var prefix = '';

		if (namespacedKey.substring(0,1) === '!') {
			prefix = '!';
			namespacedKey = namespacedKey.substring(1);
		}

		var subspace = namespacedKey.split('.'),
			key = subspace.pop(),
			string = [];

		while(subspace.length) {
			string.push(subspace.join('.') + '.');
			subspace.shift();
		}

		string = string ? ('(' + string.join('|') + ')?' + key) : key;
		return prefix + string;
	}

	function makeRegex(block, namespace) {
		namespace = '(' + namespace + ')?';
		return new RegExp('[\\t ]*<!--[\\s]*BEGIN ' + namespace + block + '[\\s]*-->[\\s\\S]*?<!--[\\s]*END ' + namespace + block + '[\\s]*-->');
	}

	function makeBlockRegex(block, namespace) {
		namespace = '(' + namespace + ')?';
		return new RegExp('([\\t ]*<!--[\\s]*BEGIN ' + namespace + block + '[\\s]*-->[\\r\\n?|\\n]?)|(<!--[\\s]*END ' + namespace + block + '[\\s]*-->)', 'g');
	}

	function makeConditionalRegex(namespacedKey) {
		var block = generateSubspacedRegex(namespacedKey);
		return new RegExp('<!--[\\s]*IF ' + block + '[\\s]*-->([\\s\\S]*?)<!--[\\s]*ENDIF ' + block.split(',')[0] + '[\\s]*-->', 'g');
	}

	function makeStatementRegex(namespacedKey) {
		var key = generateSubspacedRegex(namespacedKey);
		return new RegExp('(<!--[\\s]*IF ' + key + '[\\s]*-->)|(<!--[\\s]*ENDIF ' + key.split(',')[0] + '[\\s]*-->)', 'g');
	}

	function registerGlobals(obj) {
		for (var g in templates.globals) {
			if (templates.globals.hasOwnProperty(g)) {
				obj[g] = obj[g] || templates.globals[g];
			}
		}

		return obj;
	}

	function checkConditionals(template, key, value, dev) {
		return checkConditional(checkConditional(template, '!' + key, !value, dev), key, value, dev);
	}

	function checkConditional(template, key, value, dev) {
		var matches = template.match(makeConditionalRegex(key));

		if (matches !== null) {
			for (var i = 0, ii = matches.length; i < ii; i++) {
				var statement = makeStatementRegex(key),
					nestedConditionals = matches[i].match(regexes.nestedConditionals),
					match = replace(matches[i].replace(statement, ''), regexes.nestedConditionals, '<!-- NESTED -->'),
					conditionalBlock = match.split(regexes.conditionalBlock);

				if (conditionalBlock[1]) {
					// there is an else statement
					template = replace(template, matches[i], replace(conditionalBlock[value ? 0 : 1], regexes.removeWhitespace, ''));
				} else {
					// regular if statement
					template = replace(template, matches[i], value ? replace(match, regexes.removeWhitespace, '') : '');
				}

				if (nestedConditionals) {
					for (var x = 0, xx = nestedConditionals.length; x < xx; x++) {
						template = replace(template, '<!-- NESTED -->', nestedConditionals[x]);
					}
				}
			}
		}

		return template;
	}

	function checkConditionalHelpers(template, obj) {
		var string,
			func;

		while ((string = template.match(regexes.conditionalHelper)) !== null) {
			string = string[1].trim();
			var args = string.split(/[ ]*,[ ]*/);
			func = args[0];

			if (helpers[func]) {
				args.shift();
				args.unshift(obj);
				template = checkConditionals(template, 'function.' + string, helpers[func].apply(null, args), 'dev');
			}
		}

		return template;
	}

	function callMethod(method, parameters) {
		return method.apply(templates, parameters);
	}

	function parseFunctions(block, result, obj) {
		var functions = block.match(/{function.*?}/gi, '');
		if (!functions) {
			return result;
		}

		for (var i=0, ii=functions.length; i<ii; ++i) {
			var search = functions[i],
				fn = functions[i].replace('{function.', '').split('}').shift().split(/[ ]*,[ ]*/),
				method = fn.shift(),
				parameters = [];

			if (fn.length) {
				for (var j = 0, jj = fn.length; j < jj; j++) {
					parameters.push(obj[fn[j]]);
				}
			} else {
				parameters = [obj];
			}

			if (helpers[method]) {
				result = replace(result, new RegExp(search, 'gi'), callMethod(helpers[method], parameters));
			}
		}

		return result;
	}

	function parseObject(template, array, key, namespace) {
		return parseArray(template, array, key, namespace, true);
	}

	function parseArray(template, array, key, namespace, isObject) {
		template = checkConditionals(template, namespace  + key + '.length', array[key].length);

		var regex = makeRegex(key, namespace), block, result;

		if (!array[key].length && !isObject) {
			return template.replace(regex, '');
		}

		while ((block = template.match(regex)) !== null) {
			block = block[0].replace(makeBlockRegex(key, namespace), '');
			result = parseArrayBlock(template, block, array[key], namespace + key + '.', isObject);
			template = replace(template, regex, result.replace(regexes.removeWhitespace, ''));
		}

		return template;
	}

	function parseArrayBlock(template, block, array, namespace, isObject) {
		var numblocks = array.length - 1,
			result = '';

		for (var iterator in array) {
			if (array.hasOwnProperty(iterator)) {
				if (!isObject) {
					iterator = parseInt(iterator, 10);
				}

				result += parse(block, array[iterator], namespace);
				if (!isObject) {
					result = checkConditionals(result, '@first', iterator === 0);
					result = checkConditionals(result, '@last', iterator === numblocks);
				}

				result = result
					.replace(regexes.removeTabspace, '');

				if (!isObject) {
					result = result.replace('@index', iterator);
				} else {
					result = result
						.replace('@key', iterator)
						.replace('@value', array[iterator]);
				}

				result = parseFunctions(block, result, array[iterator]);
			}
		}

		return result;
	}

	function parseValue(template, key, value) {
		template = checkConditionals(template, key, value);
		return replaceValue(template, key, value);
	}

	function parse(template, obj, namespace) {
		namespace = namespace || '';

		for (var key in obj) {
			if (obj.hasOwnProperty(key)) {
				if (typeof obj[key] === 'undefined' || typeof obj[key] === 'function') {
					continue;
				} else if (obj[key] === null) {
					template = replaceValue(template, namespace + key, '');
				} else if (obj[key].constructor === Array) {
					template = replaceValue(template, namespace + key + '.length', obj[key].length);
					template = parseArray(template, obj, key, namespace);
				} else if (obj[key] instanceof Object) {
					template = parseObject(template, obj, key, namespace);
					template = checkConditionals(template, key, obj[key]);
					template = parse(template, obj[key], namespace + key + '.');
				} else {
					template = parseValue(template, namespace + key, obj[key]);
				}
			}
		}

		if (namespace) {
			template = template.replace(new RegExp('{' + namespace + '\\.[\\s\\S]*?}', 'g'), '');
			namespace = '';
		} else {
			template = checkConditionalHelpers(template, obj);
			template = cleanup(template);
		}

		return template;
	}

	function cleanup(template) {
		template = template
			.replace(regexes.cleanupEmptyLoops, '')
			.replace(regexes.cleanupMissingKeys, '');

		var undefinedKey,
			undefinedKeys = {},
			parseUndefined;

		while ((undefinedKey = regexes.getUndefinedKeys.exec(template)) !== null) {
			undefinedKeys[undefinedKey[1]] = false;
			parseUndefined = true;
		}

		return parseUndefined ? parse(template, undefinedKeys, '') : template;
	}

	module.exports = templates;
	module.exports.__express = express;

	if ('undefined' !== typeof window) {
		window.templates = module.exports;
	}

})('undefined' === typeof module ? {
	module: {
		exports: {}
	}
} : module);
<|endoftext|>


<commit_before>/* jshint node:true */
(function (root) {

	'use strict';

	var fs = require('fs'),
		path = require('path'),
		_ = require('underscore'),
		Promise = require('node-promise')
			.Promise,
		all = require('node-promise').all,
		optimist = require('optimist'),
		u = require('../utils.js'),
		spawn = require('child_process').spawn;

	var argv = optimist.argv,
		logging = false,
		configPath,
		config,
		promises = [],
		tmpFiles = [],
		singleRun = true;

	var log = function (msg) {
		if (logging === false) {
			return;
		}
		console.log(msg);
	};

	var angularGlobals = ['LOG_DISABLE', 'LOG_ERROR', 'LOG_WARN', 'LOG_INFO',
		'LOG_DEBUG', 'JASMINE', 'JASMINE_ADAPTER', 'MOCHA', 'MOCHA_ADAPTER',
		'QUNIT', 'QUNIT_ADAPTER', 'REQUIRE', 'REQUIRE_ADAPTER',
		'ANGULAR_SCENARIO', 'ANGULAR_SCENARIO_ADAPTER'];

	_.each(angularGlobals, function (glob) {
		global[glob] = glob;
	});

	optimist
		.usage('Usage: $0 [config file]')
		.alias('l', 'log-stdout')
		.alias('h', 'help')
		.alias('o', 'output-directory')
		.alias('p', 'params')
		.alias('c', 'continuous')
		.boolean('c')
		.describe('l', 'Output logs to stdout')
		.describe('h', 'Display help')
		.describe('o', 'Where to put test results')
		.describe('p', 'Parameters (in JSON) to pass to tests')
		.describe('c', 'Run testing continuously');

	if (argv.h || argv.help) {
		optimist.showHelp();
		process.exit(0);
	}

	if (argv.l || argv['log-stdout']) {
		logging = true;
	}

	if (!argv._.length) {
		optimist.showHelp();
		process.exit(1);
	} else {
		log('loading config ' + argv._[0]);
		configPath = u.relativeToCwd(argv._[0]);
		config = require(configPath);
	}

	if (argv.o || argv['output-directory']) {
		config.outputDirectory = u.relativeToCwd(argv.o || argv['output-directory']);
	} else {
		config.outputDirectory = __dirname;
	}

	if (argv.c || argv.continuous) {
		singleRun = false;
	}

	_.defaults(config.karma, {

		// test results reporter to use
		// possible values: 'dots', 'progress', 'junit'
		reporters: ['progress', 'junit'],

		// web server port
		port: 9876,

		// cli runner port
		runnerPort: 9100,

		// enable / disable colors in the output (reporters and logs)
		colors: true,

		// level of logging
		// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
		logLevel: LOG_INFO,

		// enable / disable watching file and executing tests whenever any file changes
		autoWatch: true,

		// Start these browsers, currently available:
		// - Chrome
		// - ChromeCanary
		// - Firefox
		// - Opera
		// - Safari (only Mac)
		// - PhantomJS
		// - IE (only Windows)
		browsers: ['Chrome'],

		// If browser does not capture in given timeout [ms], kill it
		captureTimeout: 60000,

		// Continuous Integration mode
		// if true, it capture browsers, run tests and exit
		singleRun: singleRun,

		junitReporter: {
			outputFile: config.outputDirectory + '/test-results.xml'
		}
	});

	// Make basePath absolute so karma can find it
	if (config.karma.basePath.indexOf('/') !== 0) {
		config.karma.basePath = path.normalize(path.dirname(configPath) + '/' + config.karma.basePath);
	}

	if (argv.p || argv.params) {
		var tmpParamsPromise = new Promise(),
			params = JSON.parse(argv.p || argv.params),
			tmpParamsName = 'tmp.karma-params-' + Math.round(Math.random() * 1000000) + '.js',
			tmpParamsPath = path.normalize(config.karma.basePath + '/' + tmpParamsName),
			content = 'window.params = ' + JSON.stringify(params) + ';';
		fs.writeFile(tmpParamsPath, content, function (err) {
			if (err) { throw 'Error writing params'; }
			tmpParamsPromise.resolve();
		});
		promises.push(tmpParamsPromise);
		console.log(content, '-->', tmpParamsPath);
		console.log(config)
		config.karma.files.unshift(tmpParamsName);
		tmpFiles.push(tmpParamsPath);
	}

	// Build the karma config file
	var karmaConfig = '';
	_.each(config.karma, function (v, k) {
		karmaConfig += 'var ' + k + ' = ' + JSON.stringify(v, null , ' ') + ';\n';
	});
	// put back karma globals
	_.each(angularGlobals, function (glob) {
		karmaConfig = karmaConfig.replace('"' + glob + '"', glob);
	});

	all(promises).then(function () {

		var ready = new Promise();
		if (config.before) {
			config.before(ready);
		} else {
			ready.resolve();
		}

		ready.then(function () {
			var tmpName = '/tmp/tmp.karma-config-' + (Math.random() * 100000000) + '.js';
			console.log('Writing temporary config to ' + tmpName);
			fs.writeFile(tmpName, karmaConfig, function (err) {
				if(err) throw err;
				console.log('Starting karma');
				var runner = spawn(u.indxRoot + 'lib/tests/node_modules/.bin/karma', [ 'start', tmpName ]);
				runner.stdout.on('data', function (data) {
					console.log(String(data));
				});

				runner.stderr.on('data', function (data) {
					console.log(String(data));
				});

				runner.on('close', function (code) {
					console.log('Karma exited with code ' + code);
					console.log('Deleting temporary files');
					_.each(tmpFiles, function (file) {
						fs.unlink(file, function () {
							process.exit(code);
						});
					});
				});
			});
		});
	});

	//

}(this));<commit_msg>now using separate test runners<commit_after>/* jshint node:true */
(function (root) {

	'use strict';

	var fs = require('fs'),
		path = require('path'),
		_ = require('underscore'),
		Promise = require('node-promise')
			.Promise,
		all = require('node-promise').all,
		optimist = require('optimist'),
		u = require('../utils.js'),
		spawn = require('child_process').spawn,
		glob = require('glob'),
		Emitter = require('events').EventEmitter;

	var argv = optimist.argv,
		logging = false,
		configPath,
		config,
		runner,
		files,
		promises = [],
		tmpFiles = [],
		emitter = new Emitter(),
		singleRun = true;

	var log = function (msg) {
		if (logging === false) {
			return;
		}
		console.log(msg);
	};

	var runners = _(fs.readdirSync('./runners')).filter(function (file) {
		return fs.lstatSync('./runners/' + file).isDirectory();
	});

	var usage = ['Usage: $0 [test-runner] [config file]',
			'Run test config using specified test runner',
			'',
			'Available test runners:'];

	_.each(runners, function (runner) {
		usage.push('  * ' + runner);
	});

	optimist
		.usage(usage.join('\n'))
		.alias('l', 'log-stdout')
		.alias('h', 'help')
		.alias('o', 'output-directory')
		.alias('p', 'params')
		.alias('s', 'single-run')
		.boolean('s')
		.describe('l', 'Output logs to stdout')
		.describe('h', 'Display help')
		.describe('o', 'Where to put test results')
		.describe('p', 'Parameters (in JSON) to pass to tests')
		.describe('s', 'Run testing only once');

	if (argv.h || argv.help) {
		optimist.showHelp();
		process.exit(0);
	}

	if (argv.l || argv['log-stdout']) {
		logging = true;
	}

	if (argv._.length < 2) {
		optimist.showHelp();
		process.exit(1);
	} else {
		log('Loading test-runner ' + argv._[0]);
		runner = require('./runners/' + argv._[0]); // node will look for index.js
		log('Loading config ' + argv._[1]);
		configPath = u.relativeToCwd(argv._[1]);
		config = require(configPath);
	}

	if (argv.o || argv['output-directory']) {
		config.outputDirectory = u.relativeToCwd(argv.o || argv['output-directory']);
	} else {
		config.outputDirectory = __dirname;
	}

	if (argv.s || argv['single-run']) {
		singleRun = true;
	}

	// Make basePath absolute
	if (config.basePath.indexOf('/') !== 0) {
		config.basePath = path.normalize(path.dirname(configPath) + '/' + config.basePath);
	}

	if (argv.p || argv.params) {
		runner.params(JSON.parse(argv.p || argv.params));
	}

	config.globs = config.files;
	config.files = _(config.globs).reduce(function (files, pattern) {
		var originalFile;

		if (_.isObject(pattern)) {
			originalFile = pattern;
			pattern = originalFile.pattern;
		}

		var newFiles = glob.sync(path.normalize(config.basePath + pattern));

		return files.concat(_.map(newFiles, function (file) {
			return _.extend({
				watched: true,
				served: true,
				included: true
			}, originalFile, { pattern: file });
		}));

	}, []);

	// watch files
	_.each(config.files, function (file) {
		// note: node documentation lists this as "unstable"
		if (file.watched) {
			fs.watch(file.pattern, function (status) {
				if (status === 'change') {
					log('Saw change on file ' + file.pattern);
					run();
				}
			});
		}
	});

	if (config.initialize) {
		config.initialize.call(emitter);
	}

	function run (start) {
		var _run = start ? runner.start : runner.run;
		var eventOptions = {};
		emitter.emit('before-run');
		if (eventOptions.wait) {
			eventOptions.wait().then(function () {
				_run();
			});
		} else {
			_run();
		}
	};
	runner.config(config);
	run(true);

}(this));<|endoftext|>


<commit_before>'use strict';
var test = require('prova')
  , <%= safeSlugname %> = require('../')

test('<%= slugname %>#get', function getTest(t){
  <%= safeSlugname %>()
  t.equal(
    false
    , 'I was too lazy to write any tests. Shame on me.'
    , 'must have at least one test'
  )
})
<commit_msg>update test file to use tape #oops<commit_after>'use strict';
var test = require('tape')
  , <%= safeSlugname %> = require('../')

test('<%= slugname %>#get', function getTest(t){
  <%= safeSlugname %>()
  t.equal(
    false
    , 'I was too lazy to write any tests. Shame on me.'
    , 'must have at least one test'
  )
})
<|endoftext|>


<commit_before>const shell = require("shelljs");
const chalk = require("chalk");
const path = require("path");
const fs = require("fs");
const _ = require("lodash");
const minimist = require("minimist");
const emoji = require("node-emoji");

function exec(cmd) {
  const { stdout, stderr, code } = shell.exec(cmd, {
    silent: true
  });

  console.log(stdout);
  console.log(stderr);
  return code;
}

const firefoxPath = "./firefox";

let code;
console.log(chalk.blue("Copying Assets"));
code = exec(`node ./bin/copy-assets.js --mc ${firefoxPath}`);
if (code !== 0) {
  process.exit(code);
}

const fileSizes = {
  "debugger.js": 50000,
  "parser-worker.js": 57000,
  "pretty-print-worker.js": 10000,
  "search-worker.js": 5000
};

Object.keys(fileSizes).forEach(key => {
  const fullFirefoxPath = path.join(process.cwd(), firefoxPath);
  const testFile = fs.readFileSync(
    path.join(fullFirefoxPath, `devtools/client/debugger/new/${key}`),
    "utf8"
  );
  const lineCount = testFile.split("\n").length;
  if (lineCount > fileSizes[key]) {
    console.log(
      chalk.red(
        `Oh no, ${key} is ${lineCount} lines, which is greater than ${
          fileSizes[key]
        } lines`
      )
    );
    process.exit(1);
  } else {
    console.log(
      chalk.yellow(
        `${key} is ${lineCount} lines, which is not great, but fine...`
      )
    );
  }
});

const otherTests = [
  "browser_net_open_in_debugger.js"
];

console.log(chalk.blue("Running Tests"));
const defaultPath = `devtools/client/debugger/new`;
const mcPath = `--mc ${firefoxPath}`;

shell.cd("firefox");

const dbg_mochitest = `./mach mochitest --headless --log-tbpl=dbg_mochitest.log ${defaultPath}`;
const dbg_out = shell.exec(dbg_mochitest, { silent: true });

const dt_mochitest = `./mach mochitest --headless --log-tbpl=dt_mochitest.log ${otherTests.join(" ")}`;
const dt_out = shell.exec(dt_mochitest, { silent: true });

shell.cd("..")

exec(`./node_modules/.bin/mochii --read ./firefox/dbg_mochitest.log`);
exec(`./node_modules/.bin/mochii --read ./firefox/dt_mochitest.log`);

if (dbg_out.code !== 0 || dt_out.code !== 0) {
  process.exit(dbg_out.code || dt_out.code);
}

process.exit(0);
<commit_msg>CI: add mochitests using the debugger and located in non-debugger test suites (#5197)<commit_after>const shell = require("shelljs");
const chalk = require("chalk");
const path = require("path");
const fs = require("fs");
const _ = require("lodash");
const minimist = require("minimist");
const emoji = require("node-emoji");

function exec(cmd) {
  const { stdout, stderr, code } = shell.exec(cmd, {
    silent: true
  });

  console.log(stdout);
  console.log(stderr);
  return code;
}

const firefoxPath = "./firefox";

let code;
console.log(chalk.blue("Copying Assets"));
code = exec(`node ./bin/copy-assets.js --mc ${firefoxPath}`);
if (code !== 0) {
  process.exit(code);
}

const fileSizes = {
  "debugger.js": 50000,
  "parser-worker.js": 57000,
  "pretty-print-worker.js": 10000,
  "search-worker.js": 5000
};

Object.keys(fileSizes).forEach(key => {
  const fullFirefoxPath = path.join(process.cwd(), firefoxPath);
  const testFile = fs.readFileSync(
    path.join(fullFirefoxPath, `devtools/client/debugger/new/${key}`),
    "utf8"
  );
  const lineCount = testFile.split("\n").length;
  if (lineCount > fileSizes[key]) {
    console.log(
      chalk.red(
        `Oh no, ${key} is ${lineCount} lines, which is greater than ${
          fileSizes[key]
        } lines`
      )
    );
    process.exit(1);
  } else {
    console.log(
      chalk.yellow(
        `${key} is ${lineCount} lines, which is not great, but fine...`
      )
    );
  }
});

const otherTests = [
  "devtools/client/framework/test/browser_browser_toolbox_debugger.js",
  "devtools/client/framework/test/browser_keybindings_01.js",
  "devtools/client/framework/test/browser_toolbox_getpanelwhenready.js",
  "devtools/client/inspector/markup/test/browser_markup_links_06.js",
  "devtools/client/netmonitor/test/browser_net_open_in_debugger.js",
  "devtools/client/netmonitor/test/browser_net_view-source-debugger.js",
  "devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_autocomplete_in_debugger_stackframe.js",
  "devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_eval_in_debugger_stackframe1.js",
  "devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_eval_in_debugger_stackframe2.js",
  "devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_location_debugger_link.js",
  "devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_stacktrace_location_debugger_link.js",
];

console.log(chalk.blue("Running Tests"));
const defaultPath = `devtools/client/debugger/new`;
const mcPath = `--mc ${firefoxPath}`;

shell.cd("firefox");

const dbg_mochitest = `./mach mochitest --headless --log-tbpl=dbg_mochitest.log ${defaultPath}`;
const dbg_out = shell.exec(dbg_mochitest, { silent: true });

const dt_mochitest = `./mach mochitest --headless --log-tbpl=dt_mochitest.log ${otherTests.join(" ")}`;
const dt_out = shell.exec(dt_mochitest, { silent: true });

shell.cd("..")

exec(`./node_modules/.bin/mochii --read ./firefox/dbg_mochitest.log`);
exec(`./node_modules/.bin/mochii --read ./firefox/dt_mochitest.log`);

if (dbg_out.code !== 0 || dt_out.code !== 0) {
  process.exit(dbg_out.code || dt_out.code);
}

process.exit(0);
<|endoftext|>


<commit_before>import BaseStore from 'stores/BaseStore';
import QuotaCollection from 'collections/QuotaCollection';
import Dispatcher from 'dispatchers/Dispatcher';
import QuotaConstants from 'constants/QuotaConstants';

let QuotaStore = BaseStore.extend({
    collection: QuotaCollection,

    queryParams: {
        page_size: 100
    }
});

var store = new QuotaStore();

Dispatcher.register(function (dispatch) {
    var actionType = dispatch.action.actionType;
    var payload = dispatch.action.payload;
    var options = dispatch.action.options || options;

    switch (actionType) {
        case QuotaConstants.CREATE_QUOTA:
            store.add(payload.quota);
            break;
        default:
            return true;
    }

    if (!options.silent) {
        store.emitChange();
    }

    return true;
});

export default store;
<commit_msg>Remove unused query parameters<commit_after>import BaseStore from 'stores/BaseStore';
import QuotaCollection from 'collections/QuotaCollection';
import Dispatcher from 'dispatchers/Dispatcher';
import QuotaConstants from 'constants/QuotaConstants';

let QuotaStore = BaseStore.extend({
    collection: QuotaCollection,
});

var store = new QuotaStore();

Dispatcher.register(function (dispatch) {
    var actionType = dispatch.action.actionType;
    var payload = dispatch.action.payload;
    var options = dispatch.action.options || options;

    switch (actionType) {
        case QuotaConstants.CREATE_QUOTA:
            store.add(payload.quota);
            break;
        default:
            return true;
    }

    if (!options.silent) {
        store.emitChange();
    }

    return true;
});

export default store;
<|endoftext|>


<commit_before>// hesperian_mobile_init.js
// This is loaded before jquery mobile so you can set up jqm initialization.
// jquery itself is available.

// iPhone / Mobile Safari workarounds
function isiPhone(){
    return ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)));
};

$(document).bind("mobileinit", function(){
	$.mobile.defaultPageTransition = "none";
	//$.mobile.hashListeningEnabled = false;
});

$("div:jqmData(role='page')").live('pagebeforecreate',function(event){
	if ($(this).attr("swipe") == "true") {
		var html = "";
		$("div.sequence-bar",this).each(function(index) {
			if (html == "") {
				var seq_length = $(this).attr("seq-length");
				var seq_position = $(this).attr("seq-position");
				var pos = 0;
				//$("div.sequence-dots",this).empty();

				while (pos < seq_length) {
					if (pos + 1 == seq_position) {
						html += '

';
						//$("div.sequence-dots",this).append('

');
					} else {
						html += '

';
						//$("div.sequence-dots",this).append('

');
					}
					pos++;
				}
			}
			$("div.sequence-dots",this).append(html);

		//	$("a.seq-nav-button",this).each(function(index,el) {
		//		if (el.href == "javascript:;") {
		//			$(el).addClass("hidden").attr("disabled",true);
		//		}
		//	});

		});
	}
});

//binds swipe events to the specified elements and maps them to clicks on the previous and next links based on them having the appropriate class
function swipeToClick(el) {
	//console.log("swiper");
	$(el).bind("swiperight swipeleft", function(event) {
		//console.log($("a.seq-nav-button-left:first",this));
		event.preventDefault();
		if (event.type == "swipeleft") {
			//$("a.seq-nav-button-right:first:not(:disabled)",this).click();
			var href = $("a.seq-nav-button-right:first",this).attr("href");
			if (href != "javascript:;")
				$.mobile.changePage(href,"none");
		}
		else if (event.type == "swiperight") {
			//$("a.seq-nav-button-left:first:not(:disabled)",this).click();
			var href = $("a.seq-nav-button-left:first",this).attr("href");
			//console.log(href);
			if (href != "javascript:;")
				$.mobile.changePage(href,"none");
		}

	});
}

//swiping would need to be selectively added to pages where we wanted it
$("div:jqmData(role='page')").live("pagecreate",function(event) {
	//console.log(this);
	var page = $(this);
	//pass page to bind swipe after filtering it for pages containing a div with class sequence bar, this identifies that swiping is to be enabled.
	//console.log(page.attr("swipe"));
	if (page.attr("swipe") == "true")
		swipeToClick(page);
});

// jquery mobile configuration
<commit_msg>Removed obsolete commented code<commit_after>// hesperian_mobile_init.js
// This is loaded before jquery mobile so you can set up jqm initialization.
// jquery itself is available.

// iPhone / Mobile Safari workarounds
function isiPhone(){
    return ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)));
};

$(document).bind("mobileinit", function(){
	$.mobile.defaultPageTransition = "none";
});

$("div:jqmData(role='page')").live('pagebeforecreate',function(event){
	if ($(this).attr("swipe") == "true") {
		var html = "";
		$("div.sequence-bar",this).each(function(index) {
			if (html == "") {
				var seq_length = $(this).attr("seq-length");
				var seq_position = $(this).attr("seq-position");
				var pos = 0;
				while (pos < seq_length) {
					if (pos + 1 == seq_position) {
						html += '

';
					} else {
						html += '

';
					}
					pos++;
				}
			}
			$("div.sequence-dots",this).append(html);
		});
	}
});

//binds swipe events to the specified elements and maps them to clicks on the previous and next links based on them having the appropriate class
function swipeToClick(el) {
	$(el).bind("swiperight swipeleft", function(event) {
		event.preventDefault();
		if (event.type == "swipeleft") {
			var href = $("a.seq-nav-button-right:first",this).attr("href");
			if (href != "javascript:;")
				$.mobile.changePage(href,"none");
		}
		else if (event.type == "swiperight") {
			var href = $("a.seq-nav-button-left:first",this).attr("href");
			if (href != "javascript:;")
				$.mobile.changePage(href,"none");
		}
	});
}

//swiping would need to be selectively added to pages where we wanted it
$("div:jqmData(role='page')").live("pagecreate",function(event) {
	var page = $(this);
	//pass page to bind swipe after filtering it for pages containing a div with class sequence bar, this identifies that swiping is to be enabled.
	if (page.attr("swipe") == "true")
		swipeToClick(page);
});

// jquery mobile configuration
<|endoftext|>


<commit_before>const fs = require('fs')
const gtp = require('gettext-parser')
const po = gtp.po
const escape_quotes = require('escape-quotes');
const plural_pattern = new RegExp('(?:([0-9]+)\\;\\s(?:plural\\=\\((.*)\\)\\;))')

exports.getTrans = (file, translations, encoding) => {
  const content = fs.readFileSync(file)
  const pocontent = po.parse(content, encoding)
  if (!pocontent.headers.language) {
      console.log(`\nError! language header not defined. \n`.red)
      throw `Define language in ${file}`
  }
  const trans = pocontent.translations['']
  const lang = pocontent.headers.language.replace('_', '-')

  // Initializing language dictionary
  translations[lang] = {}

  for (let k in trans) {
    const value = trans[k]

    if (value.msgid && value.msgstr.length) {
      translations[lang][value.msgid] = value.msgstr[0]
      if (value.msgid_plural && value.msgid_plural.length) {
        translations[lang][value.msgid_plural] = value.msgstr[1] || value.msgstr[0] // #78
      }
    }
  }

  // Options
  translations['options'] = {}
  if (pocontent.headers && pocontent.headers['plural-forms']) {
    const result = (plural_pattern.exec(pocontent.headers['plural-forms']))
    if (result) {
      translations['options']['plural_rule'] = result[2]
      translations['options']['plural_number'] = result[1]
    }
  }

}

// Escape single quotes and escape newlines
function escape(str) {
  return escape_quotes(str).replace(/(\r\n|\n|\r)/gm, '\\n')
}

exports.transToTxt = (trans) => {
  let txt = 'export const translations = {\n'

  for (let lang in trans) {
    txt += `  '${lang}': {\n`

    for (let k in trans[lang]) {
      txt += `    '${escape(k)}': '${escape(trans[lang][k])}',\n`
    }

    txt += `  },\n`
  }

  txt += '}\n'
  return txt
}
<commit_msg>Fix plural fallback<commit_after>const fs = require('fs')
const gtp = require('gettext-parser')
const po = gtp.po
const escape_quotes = require('escape-quotes');
const plural_pattern = new RegExp('(?:([0-9]+)\\;\\s(?:plural\\=\\((.*)\\)\\;))')

exports.getTrans = (file, translations, encoding) => {
  const content = fs.readFileSync(file)
  const pocontent = po.parse(content, encoding)
  if (!pocontent.headers.language) {
      console.log(`\nError! language header not defined. \n`.red)
      throw `Define language in ${file}`
  }
  const trans = pocontent.translations['']
  const lang = pocontent.headers.language.replace('_', '-')

  // Initializing language dictionary
  translations[lang] = {}

  for (let k in trans) {
    const value = trans[k]

    if (value.msgid && value.msgstr.length) {
      translations[lang][value.msgid] = value.msgstr[0]
      if (value.msgid_plural && value.msgid_plural.length) {
        translations[lang][value.msgid_plural] = value.msgstr[1] || ''
      }
    }
  }

  // Options
  translations['options'] = {}
  if (pocontent.headers && pocontent.headers['plural-forms']) {
    const result = (plural_pattern.exec(pocontent.headers['plural-forms']))
    if (result) {
      translations['options']['plural_rule'] = result[2]
      translations['options']['plural_number'] = result[1]
    }
  }

}

// Escape single quotes and escape newlines
function escape(str) {
  return escape_quotes(str).replace(/(\r\n|\n|\r)/gm, '\\n')
}

exports.transToTxt = (trans) => {
  let txt = 'export const translations = {\n'

  for (let lang in trans) {
    txt += `  '${lang}': {\n`

    for (let k in trans[lang]) {
      txt += `    '${escape(k)}': '${escape(trans[lang][k])}',\n`
    }

    txt += `  },\n`
  }

  txt += '}\n'
  return txt
}
<|endoftext|>


<commit_before>/* jshint node: true */

/**
 * Packet decoding and extraction streams.
 *
 * These are meant to provide a single interface to all decoding and extracting
 * functions (also providing link type inference when possible).
 *
 */
(function (root) {
  'use strict';

  var stream = require('stream'),
      util = require('util'),
      decoders = require('./decoders'),
      extractors = require('./extractors');

  /**
   * Decoder stream class.
   *
   * This class will "dynamically" look up the correct transform function based
   * on the link type of the source stream. This is done by having a
   * placeholder `_transform` method replace itself when it is first called.
   *
   */
  function Decoder(opts) {

    opts = opts || {};
    var linkType = opts.linkType || null; // Inferred below.

    stream.Transform.call(this, {objectMode: true});

    this.on('pipe', function (src) {
      // Infer link type from first capture stream piped.

      if (!linkType) {
        linkType = src.getLinkType();
      } else if (linkType !== src.getLinkType()) {
        return this.emit('error', new Error('Inconsistent link type.'));
      }

    });

    this.getLinkType = function () { return linkType; };

    this._transform = function (data, encoding, callback) {

      if (!linkType) {
        return this.emit('error', new Error('No link type specified.'));
      } else if (!(linkType in decoders)) {
        return this.emit('error', new Error('Unsupported link type.'));
      }

      decoders[linkType].Transform.call(this, opts);
      this._transform(data, encoding, callback);

    };

  }
  util.inherits(Decoder, stream.Transform);

  /**
   * Extractor stream class.
   *
   * Similarly to the decoder stream above, in order to be functional, this
   * duplex stream must be activated first (see method below). The first time
   * `_read` or `_write` is called, the extractor will look up the appropriate
   * extracting function based on the link type and override both methods.
   *
   */
  function Extractor(opts) {

    opts = opts || {};
    var fromLinkType = opts.fromLinkType;
    var toLinkType = opts.toLinkType;

    stream.Duplex.call(this, {objectMode: true});

    this.getLinkType = function (incoming) {

      return incoming ? fromLinkType : toLinkType;

    };

    this.on('pipe', function (src) {

      if (!fromLinkType) {
        fromLinkType = src.getLinkType();
      } else if (fromLinkType !== src.getLinkType()) {
        return this.emit('error', new Error('Inconsistent source link type.'));
      }

      if (!toLinkType) {
        if (fromLinkType in extractors) {
          var toLinkTypes = Object.keys(extractors[fromLinkType]);
          if (toLinkTypes.length === 1) {
            toLinkType = toLinkTypes[0];
          } else {
            return this.emit('error', new Error('Ambiguous target link type.'));
          }
        } else {
          return this.emit('error', new Error('No valid target link type.'));
        }
      }

    });

    this.once('finish', function () { this.push(null); });
    // Emit 'end' event when writable sides finishes (duplex streams don't seem
    // to do so, unlike transform streams).

    this._read = function () {

      activate();
      this._read();

    };

    this._write = function (data, encoding, callback) {

      activate();
      this._write(data, encoding, callback);

    };

    var self = this;

    function activate() {

      if (fromLinkType && toLinkType) {
        var srcExtractors = extractors[fromLinkType];
        if (srcExtractors && toLinkType in srcExtractors) {
          srcExtractors[toLinkType].call(self, opts);
        } else {
          self.emit('error', new Error('Unsupported link types.'));
        }
      } else {
        self.emit('error', new Error('Missing link types.'));
      }

    }

  }
  util.inherits(Extractor, stream.Duplex);

  // Export things.

  root.exports = {
    Decoder: Decoder,
    Extractor: Extractor
  };

})(module);
<commit_msg>Improve error messages for unsupported transform link types.<commit_after>/* jshint node: true */

/**
 * Packet decoding and extraction streams.
 *
 * These are meant to provide a single interface to all decoding and extracting
 * functions (also providing link type inference when possible).
 *
 */
(function (root) {
  'use strict';

  var stream = require('stream'),
      util = require('util'),
      decoders = require('./decoders'),
      extractors = require('./extractors');

  /**
   * Decoder stream class.
   *
   * This class will "dynamically" look up the correct transform function based
   * on the link type of the source stream. This is done by having a
   * placeholder `_transform` method replace itself when it is first called.
   *
   */
  function Decoder(opts) {

    opts = opts || {};
    var linkType = opts.linkType || null; // Inferred below.

    stream.Transform.call(this, {objectMode: true});

    this.on('pipe', function (src) {
      // Infer link type from first capture stream piped.

      if (!linkType) {
        linkType = src.getLinkType();
      } else if (linkType !== src.getLinkType()) {
        return this.emit('error', new Error('Inconsistent link type.'));
      }

    });

    this.getLinkType = function () { return linkType; };

    this._transform = function (data, encoding, callback) {

      if (!linkType) {
        return this.emit('error', new Error('No link type specified.'));
      } else if (!(linkType in decoders)) {
        return this.emit(
          'error',
          new Error('Unsupported link type: ' + linkType)
        );
      }

      decoders[linkType].Transform.call(this, opts);
      this._transform(data, encoding, callback);

    };

  }
  util.inherits(Decoder, stream.Transform);

  /**
   * Extractor stream class.
   *
   * Similarly to the decoder stream above, in order to be functional, this
   * duplex stream must be activated first (see method below). The first time
   * `_read` or `_write` is called, the extractor will look up the appropriate
   * extracting function based on the link type and override both methods.
   *
   */
  function Extractor(opts) {

    opts = opts || {};
    var fromLinkType = opts.fromLinkType;
    var toLinkType = opts.toLinkType;

    stream.Duplex.call(this, {objectMode: true});

    this.getLinkType = function (incoming) {

      return incoming ? fromLinkType : toLinkType;

    };

    this.on('pipe', function (src) {

      if (!fromLinkType) {
        fromLinkType = src.getLinkType();
      } else if (fromLinkType !== src.getLinkType()) {
        return this.emit('error', new Error('Inconsistent source link type.'));
      }

      if (!toLinkType) {
        if (fromLinkType in extractors) {
          var toLinkTypes = Object.keys(extractors[fromLinkType]);
          if (toLinkTypes.length === 1) {
            toLinkType = toLinkTypes[0];
          } else {
            return this.emit('error', new Error('Ambiguous target link type.'));
          }
        } else {
          return this.emit(
            'error', new Error('No valid target link type from ' + fromLinkType)
          );
        }
      }

    });

    this.once('finish', function () { this.push(null); });
    // Emit 'end' event when writable sides finishes (duplex streams don't seem
    // to do so, unlike transform streams).

    this._read = function () {

      activate();
      this._read();

    };

    this._write = function (data, encoding, callback) {

      activate();
      this._write(data, encoding, callback);

    };

    var self = this;

    function activate() {

      if (fromLinkType && toLinkType) {
        var srcExtractors = extractors[fromLinkType];
        if (srcExtractors && toLinkType in srcExtractors) {
          srcExtractors[toLinkType].call(self, opts);
        } else {
          self.emit(
            'error', new Error(
              'Unsupported link types: ' + fromLinkType + ' -> ' + toLinkType
            )
          );
        }
      } else {
        self.emit('error', new Error('Missing link types.'));
      }

    }

  }
  util.inherits(Extractor, stream.Duplex);

  // Export things.

  root.exports = {
    Decoder: Decoder,
    Extractor: Extractor
  };

})(module);
<|endoftext|>


<commit_before>import SearchIndex from 'appkit/utils/search-index';

export default Ember.TextField.extend({
  resultsPage: 1,

  searchQueryObserver: Ember.debouncedObserver(function() {

    var query = this.get('value');
    var type = this.get('filter');

    if (Ember.isEmpty(query)) {
      this.getWithDefault('parentView.results', Ember.A([])).clear();
    } else {

      var parentView = this.get('parentView');

      parentView.set('isLoading', true);

      SearchIndex.search(query, this.get('resultsPage'), type).then(function (results) {
        parentView.set('isLoading', false);
        var canCreate = true;
        results = Ember.A(results).map(function (result) {
          if (query.toLowerCase() === Ember.$('<span>').html(result.name).text().toLowerCase()) {
            canCreate = false;
          }
          return Ember.Object.create(result);
        });
        if (canCreate) {
          results.addObject(Ember.Object.create({
            name: 'Create "%@"'.fmt(Ember.Handlebars.Utils.escapeExpression(query)),
            value: query,
            createStub: true,
            type: type
          }));
        }
        parentView.set('results', results);
      }, function (error) {
        parentView.set('isLoading', false);
        Ember.Logger.error(error);
      });

    }

  }, 'value', 500).on('init'),

  keyDown: function (event) {

    var currentObject, indexObject;

    switch (event.keyCode) {
    case 8: // delete
      if (!this.get('value')) {
        this.get('parentView.controller').send('removeLastSelection');
      }
      break;

    case 13: // enter
      event.preventDefault();
      if (!Ember.isEmpty(this.get('parentView.results'))) {
        this.get('parentView.controller').send('addToSelection', this.get('parentView.results').findBy('isSelected'));
      }
      break;

    case 38: // up
      event.preventDefault();
      currentObject = this.get('parentView.results').findBy('isSelected');
      if (currentObject) {
        indexObject = this.get('parentView.results').indexOf(currentObject);
        if (indexObject > 0) {
          currentObject.set('isSelected', false);
          this.get('parentView.results').objectAt(indexObject - 1).set('isSelected', true);
        }
      }
      break;

    case 40: // down
      event.preventDefault();
      currentObject = this.get('parentView.results').findBy('isSelected');
      if (currentObject) {
        indexObject = this.get('parentView.results').indexOf(currentObject);
        if (indexObject + 1 < this.get('parentView.results.length')) {
          currentObject.set('isSelected', false);
          this.get('parentView.results').objectAt(indexObject + 1).set('isSelected', true);
        }
      }
      break;

    default:
      this.get('parentView.controller').send('keyDown');
      break;

    }

  }
});
<commit_msg>Escape create value as well.<commit_after>import SearchIndex from 'appkit/utils/search-index';

export default Ember.TextField.extend({
  resultsPage: 1,

  searchQueryObserver: Ember.debouncedObserver(function() {

    var query = this.get('value');
    var type = this.get('filter');

    if (Ember.isEmpty(query)) {
      this.getWithDefault('parentView.results', Ember.A([])).clear();
    } else {

      var parentView = this.get('parentView');

      parentView.set('isLoading', true);

      SearchIndex.search(query, this.get('resultsPage'), type).then(function (results) {
        parentView.set('isLoading', false);
        var canCreate = true;
        results = Ember.A(results).map(function (result) {
          if (query.toLowerCase() === Ember.$('<span>').html(result.name).text().toLowerCase()) {
            canCreate = false;
          }
          return Ember.Object.create(result);
        });
        if (canCreate) {
          var escapedQuery = Ember.Handlebars.Utils.escapeExpression(query);
          results.addObject(Ember.Object.create({
            name: 'Create "%@"'.fmt(escapedQuery),
            value: escapedQuery,
            createStub: true,
            type: type
          }));
        }
        parentView.set('results', results);
      }, function (error) {
        parentView.set('isLoading', false);
        Ember.Logger.error(error);
      });

    }

  }, 'value', 500).on('init'),

  keyDown: function (event) {

    var currentObject, indexObject;

    switch (event.keyCode) {
    case 8: // delete
      if (!this.get('value')) {
        this.get('parentView.controller').send('removeLastSelection');
      }
      break;

    case 13: // enter
      event.preventDefault();
      if (!Ember.isEmpty(this.get('parentView.results'))) {
        this.get('parentView.controller').send('addToSelection', this.get('parentView.results').findBy('isSelected'));
      }
      break;

    case 38: // up
      event.preventDefault();
      currentObject = this.get('parentView.results').findBy('isSelected');
      if (currentObject) {
        indexObject = this.get('parentView.results').indexOf(currentObject);
        if (indexObject > 0) {
          currentObject.set('isSelected', false);
          this.get('parentView.results').objectAt(indexObject - 1).set('isSelected', true);
        }
      }
      break;

    case 40: // down
      event.preventDefault();
      currentObject = this.get('parentView.results').findBy('isSelected');
      if (currentObject) {
        indexObject = this.get('parentView.results').indexOf(currentObject);
        if (indexObject + 1 < this.get('parentView.results.length')) {
          currentObject.set('isSelected', false);
          this.get('parentView.results').objectAt(indexObject + 1).set('isSelected', true);
        }
      }
      break;

    default:
      this.get('parentView.controller').send('keyDown');
      break;

    }

  }
});
<|endoftext|>