<commit_before>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint node: true, nomen: true, indent: 4, plusplus: true */
/*global YUI, YUITest */

YUI.add('request-cache', function (Y, NAME) {
    'use strict';

    var staticAppConfig,
        refreshAddons,
        enabled = true,
        originalDispatchFn = Y.mojito.Dispatcher.dispatch;

    //-- ExpandedResource -----------------------------------------------------

    function ExpandedResource(options) {
        this.actionContext = options.actionContext;
        this.controller = options.controller;
    }

    //-- RequestCacheActionContext --------------------------------------------

    function RequestCacheActionContext(options) {
        RequestCacheActionContext.superclass.constructor.call(this, options);
    }

    Y.extend(RequestCacheActionContext, Y.mojito.ActionContext, {

        // This function is invoked from ac.done() and ac.error() (see below)
        // and releases (i.e. puts back into the list of available resources)
        // the resources (controller, action context) it was using. This is the
        // reverse operation that was done in our custom dispatcher function.
        _releaseCachedResources: function () {

            if (!enabled) {
                return;
            }

            var cache,
                cacheKey,
                availableResourceList,
                req = this._adapter.req,
                instance = this.instance,
                controller = instance.controller,
                resource = new ExpandedResource({
                    actionContext: this,
                    controller: controller
                });

            if (!req.globals) {
                req.globals = {};
            }

            if (!req.globals['request-cache']) {
                req.globals['request-cache'] = {
                    byBase: {},
                    byType: {}
                };
            }

            cache = req.globals['request-cache'];

            if (instance.base && !cache.byBase[instance.base]) {
                cache.byBase[instance.base] = [];
            } else if (instance.type && !cache.byType[instance.type]) {
                cache.byType[instance.type] = [];
            }

            if (instance.base) {
                availableResourceList = cache.byBase[instance.base];
                cacheKey = 'base=' + instance.base;
            } else if (instance.type) {
                availableResourceList = cache.byType[instance.type];
                cacheKey = 'type=' + instance.type;
            }

            Y.log('Releasing instance for mojit [' + cacheKey + ']', 'debug', NAME);

            availableResourceList.push(resource);
        },

        done: function () {
            RequestCacheActionContext.superclass.done.apply(this, arguments);
            this._releaseCachedResources();
        },

        error: function () {
            RequestCacheActionContext.superclass.error.apply(this, arguments);
            this._releaseCachedResources();
        }
    });

    Y.mojito.ActionContext = RequestCacheActionContext;

    //-- Y.mojito.Dispatcher.dispatch -----------------------------------------

    Y.mojito.Dispatcher.dispatch = function (command, adapter) {

        var config,
            cache,
            availableResourceList,
            availableResource,
            newCommand,
            action,
            error,
            addonInstance,
            AddonConstuctor,
            freshInstance = command.instance,
            i;

        if (!staticAppConfig) {

            // Retrieve the cache configuration on the first dispatch.

            staticAppConfig = adapter.page.staticAppConfig;
            config = staticAppConfig['request-cache'] || {};

            // The cache is enabled if you don't set the "enabled" property in
            // the cache configuration, or if you set that property to a value
            // that evaluates to "true".
            if (config.hasOwnProperty('enabled')) {
                enabled = !!config.enabled;
            }

            refreshAddons = config.refreshAddons || [];
        }

        if (enabled) {

            // Build the cache if it doesn't exist...

            if (!adapter.req.globals) {
                adapter.req.globals = {};
            }

            if (!adapter.req.globals['request-cache']) {
                adapter.req.globals['request-cache'] = {
                    byBase: {},
                    byType: {}
                };
            }

            cache = adapter.req.globals['request-cache'];

            // See if there is an available cached resource that matches our instance.
            availableResourceList = (freshInstance.base && cache.byBase[freshInstance.base]) ||
                (freshInstance.type && cache.byType[freshInstance.type]);

            if (availableResourceList && availableResourceList.length > 0) {
                // Use the available cached resource by removing it from the
                // list of available resources. This guarantees that mojits
                // which controller may execute asynchronously do not share
                // the same resources!
                availableResource = availableResourceList.pop();
            }
        } else {

            Y.log('mojito-cache is disabled', 'debug', NAME);
        }

        // If there is a cached resource, dispatch with that.
        if (availableResource) {

            Y.log('Using cached instance for mojit [' +
                    ((freshInstance.base && cache.byBase[freshInstance.base]) ? 'base=' + freshInstance.base :
                        (freshInstance.type && cache.byType[freshInstance.type]) ? 'type=' + freshInstance.type : 'N/A') +
                    ']', 'debug', NAME);

            // We reference this here just to easily refer
            // to availableResource.actionContext.command
            newCommand = availableResource.actionContext.command;

            // We want the new params and action
            newCommand.params = command.params;
            newCommand.action = command.instance.action || newCommand.action;

            // This is specific to mojito-pipeline
            newCommand.task = command.task;

            // Reap anything else that might have gone into the instance config.
            Y.mix(newCommand.instance.config, command.instance.config, true);

            // Instantiate again the addons that need to be refreshed if any
            for (i = 0; i < refreshAddons.length; i++) {
                AddonConstuctor = Y.mojito.addons.ac[refreshAddons[i]];
                if (AddonConstuctor) {
                    addonInstance = new AddonConstuctor(newCommand, adapter, availableResource.actionContext);
                    if (addonInstance.namespace && availableResource.actionContext[addonInstance.namespace]) {
                        availableResource.actionContext[addonInstance.namespace] = addonInstance;
                    }
                }
            }

            // The adapter and its callbacks need to be refreshed
            availableResource.actionContext._adapter = adapter;

            // Handle the __call case
            if (Y.Lang.isFunction(availableResource.controller[newCommand.action])) {
                action = newCommand.action;
            } else if (Y.Lang.isFunction(availableResource.controller.__call)) {
                action = '__call';
            } else {
                error = new Error('No method "' + newCommand.action + '" on controller type "' + newCommand.instance.type + '"');
                error.code = 404;
                throw error;
            }

            // Handle controller timeout
            if (staticAppConfig.actionTimeout) {

                // This will be cleared in ActionContext.done if it happens in time
                availableResource.actionContext._timer = setTimeout(function () {
                    var err,
                        msg = 'Killing potential zombie context for Mojit type: ' +
                            command.instance.type +
                            ', controller: ' + availableResource.controller +
                            ', action: ' + action;

                    // Clear the timer reference so our invocation of error()
                    // doesn't try to clear it.
                    availableResource.actionContext._timer = null;

                    // Create an HTTP Timeout error with controller/action info.
                    err = new Error(msg);
                    err.code = 408;

                    availableResource.actionContext.error(err);

                }, staticAppConfig.actionTimeout);
            }

            availableResource.controller[action](availableResource.actionContext);

        } else {
            Y.log('Creating a new instance for mojit [' +
                    (freshInstance.base ? 'base=' + freshInstance.base :
                        freshInstance.type ? 'type=' + freshInstance.type : 'N/A') +
                    ']', 'debug', NAME);

            // No cache, expand the command.instance and create a new AC
            // with our custom RequestCacheActionContext constructor
            // which then instanciates all the addons and calls the controller
            // This is normal mojito workflow, except our custom constructor
            // populates the cache so next time we can find it and avoid
            // doing this.
            originalDispatchFn.apply(this, arguments);
        }
    };

}, '0.1.0', {
    requires: [
        'mojito-dispatcher',
        'mojito-action-context'
    ]
});
<commit_msg>instance.controller is a string, not an object as I was expecting<commit_after>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint node: true, nomen: true, indent: 4, plusplus: true */
/*global YUI, YUITest */

YUI.add('request-cache', function (Y, NAME) {
    'use strict';

    var staticAppConfig,
        refreshAddons,
        enabled = true,
        originalDispatchFn = Y.mojito.Dispatcher.dispatch;

    //-- ExpandedResource -----------------------------------------------------

    function ExpandedResource(options) {
        this.actionContext = options.actionContext;
        this.controller = options.controller;
    }

    //-- RequestCacheActionContext --------------------------------------------

    function RequestCacheActionContext(options) {
        this.controller = options.controller;
        RequestCacheActionContext.superclass.constructor.call(this, options);
    }

    Y.extend(RequestCacheActionContext, Y.mojito.ActionContext, {

        // This function is invoked from ac.done() and ac.error() (see below)
        // and releases (i.e. puts back into the list of available resources)
        // the resources (controller, action context) it was using. This is the
        // reverse operation that was done in our custom dispatcher function.
        _releaseCachedResources: function () {

            if (!enabled) {
                return;
            }

            var cache,
                cacheKey,
                availableResourceList,
                req = this._adapter.req,
                instance = this.instance,
                controller = this.controller,
                resource = new ExpandedResource({
                    actionContext: this,
                    controller: controller
                });

            if (!req.globals) {
                req.globals = {};
            }

            if (!req.globals['request-cache']) {
                req.globals['request-cache'] = {
                    byBase: {},
                    byType: {}
                };
            }

            cache = req.globals['request-cache'];

            if (instance.base && !cache.byBase[instance.base]) {
                cache.byBase[instance.base] = [];
            } else if (instance.type && !cache.byType[instance.type]) {
                cache.byType[instance.type] = [];
            }

            if (instance.base) {
                availableResourceList = cache.byBase[instance.base];
                cacheKey = 'base=' + instance.base;
            } else if (instance.type) {
                availableResourceList = cache.byType[instance.type];
                cacheKey = 'type=' + instance.type;
            }

            Y.log('Releasing instance for mojit [' + cacheKey + ']', 'debug', NAME);

            availableResourceList.push(resource);
        },

        done: function () {
            RequestCacheActionContext.superclass.done.apply(this, arguments);
            this._releaseCachedResources();
        },

        error: function () {
            RequestCacheActionContext.superclass.error.apply(this, arguments);
            this._releaseCachedResources();
        }
    });

    Y.mojito.ActionContext = RequestCacheActionContext;

    //-- Y.mojito.Dispatcher.dispatch -----------------------------------------

    Y.mojito.Dispatcher.dispatch = function (command, adapter) {

        var config,
            cache,
            availableResourceList,
            availableResource,
            newCommand,
            action,
            error,
            addonInstance,
            AddonConstuctor,
            freshInstance = command.instance,
            i;

        if (!staticAppConfig) {

            // Retrieve the cache configuration on the first dispatch.

            staticAppConfig = adapter.page.staticAppConfig;
            config = staticAppConfig['request-cache'] || {};

            // The cache is enabled if you don't set the "enabled" property in
            // the cache configuration, or if you set that property to a value
            // that evaluates to "true".
            if (config.hasOwnProperty('enabled')) {
                enabled = !!config.enabled;
            }

            refreshAddons = config.refreshAddons || [];
        }

        if (enabled) {

            // Build the cache if it doesn't exist...

            if (!adapter.req.globals) {
                adapter.req.globals = {};
            }

            if (!adapter.req.globals['request-cache']) {
                adapter.req.globals['request-cache'] = {
                    byBase: {},
                    byType: {}
                };
            }

            cache = adapter.req.globals['request-cache'];

            // See if there is an available cached resource that matches our instance.
            availableResourceList = (freshInstance.base && cache.byBase[freshInstance.base]) ||
                (freshInstance.type && cache.byType[freshInstance.type]);

            if (availableResourceList && availableResourceList.length > 0) {
                // Use the available cached resource by removing it from the
                // list of available resources. This guarantees that mojits
                // which controller may execute asynchronously do not share
                // the same resources!
                availableResource = availableResourceList.pop();
            }
        } else {

            Y.log('mojito-cache is disabled', 'debug', NAME);
        }

        // If there is a cached resource, dispatch with that.
        if (availableResource) {

            Y.log('Using cached instance for mojit [' +
                    ((freshInstance.base && cache.byBase[freshInstance.base]) ? 'base=' + freshInstance.base :
                        (freshInstance.type && cache.byType[freshInstance.type]) ? 'type=' + freshInstance.type : 'N/A') +
                    ']', 'debug', NAME);

            // We reference this here just to easily refer
            // to availableResource.actionContext.command
            newCommand = availableResource.actionContext.command;

            // We want the new params and action
            newCommand.params = command.params;
            newCommand.action = command.instance.action || newCommand.action;

            // This is specific to mojito-pipeline
            newCommand.task = command.task;

            // Reap anything else that might have gone into the instance config.
            Y.mix(newCommand.instance.config, command.instance.config, true);

            // Instantiate again the addons that need to be refreshed if any
            for (i = 0; i < refreshAddons.length; i++) {
                AddonConstuctor = Y.mojito.addons.ac[refreshAddons[i]];
                if (AddonConstuctor) {
                    addonInstance = new AddonConstuctor(newCommand, adapter, availableResource.actionContext);
                    if (addonInstance.namespace && availableResource.actionContext[addonInstance.namespace]) {
                        availableResource.actionContext[addonInstance.namespace] = addonInstance;
                    }
                }
            }

            // The adapter and its callbacks need to be refreshed
            availableResource.actionContext._adapter = adapter;

            // Handle the __call case
            if (Y.Lang.isFunction(availableResource.controller[newCommand.action])) {
                action = newCommand.action;
            } else if (Y.Lang.isFunction(availableResource.controller.__call)) {
                action = '__call';
            } else {
                error = new Error('No method "' + newCommand.action + '" on controller type "' + newCommand.instance.type + '"');
                error.code = 404;
                throw error;
            }

            // Handle controller timeout
            if (staticAppConfig.actionTimeout) {

                // This will be cleared in ActionContext.done if it happens in time
                availableResource.actionContext._timer = setTimeout(function () {
                    var err,
                        msg = 'Killing potential zombie context for Mojit type: ' +
                            command.instance.type +
                            ', controller: ' + availableResource.controller +
                            ', action: ' + action;

                    // Clear the timer reference so our invocation of error()
                    // doesn't try to clear it.
                    availableResource.actionContext._timer = null;

                    // Create an HTTP Timeout error with controller/action info.
                    err = new Error(msg);
                    err.code = 408;

                    availableResource.actionContext.error(err);

                }, staticAppConfig.actionTimeout);
            }

            availableResource.controller[action](availableResource.actionContext);

        } else {
            Y.log('Creating a new instance for mojit [' +
                    (freshInstance.base ? 'base=' + freshInstance.base :
                        freshInstance.type ? 'type=' + freshInstance.type : 'N/A') +
                    ']', 'debug', NAME);

            // No cache, expand the command.instance and create a new AC
            // with our custom RequestCacheActionContext constructor
            // which then instanciates all the addons and calls the controller
            // This is normal mojito workflow, except our custom constructor
            // populates the cache so next time we can find it and avoid
            // doing this.
            originalDispatchFn.apply(this, arguments);
        }
    };

}, '0.1.0', {
    requires: [
        'mojito-dispatcher',
        'mojito-action-context'
    ]
});
<|endoftext|>


<commit_before>// abc_editor.js
// ABCEditor is the interface class for the area that contains the ABC text. It is responsible for
// holding the text of the tune and calling the parser and the rendering engines.
//
// EditArea is an example of using a textarea as the control that is shown to the user. As long as
// the same interface is used, ABCEditor can use a different type of object.
//
// EditArea:
// - constructor(textareaid)
//		This contains the id of a textarea control that will be used.
// - addSelectionListener(listener)
//		A callback class that contains the entry point fireSelectionChanged()
// - addChangeListener(listener)
//		A callback class that contains the entry point fireChanged()
// - getSelection()
//		returns the object { start: , end: } with the current selection in characters
// - setSelection(start, end)
//		start and end are the character positions that should be selected.
// - getString()
//		returns the ABC text that is currently displayed.
// - setString(str)
//		sets the ABC text that is currently displayed, and resets the initialText variable
// - getElem()
//		returns the textarea element
// - string initialText
//		Contains the starting text. This can be compared against the current text to see if anything changed.
//

/*global document, clearTimeout, setTimeout */
/*global ABCMidiWriter, AbcTuneBook, AbcParse, Raphael, ABCPrinter, ABCTextPrinter */
/*extern EditArea, ABCEditor */

function EditArea(textareaid) {
  this.textarea = document.getElementById(textareaid);
  this.initialText = this.textarea.value;
  this.isDragging = false;
}

EditArea.prototype.addSelectionListener = function(listener) {
  this.textarea.onmousemove = function(ev) {
	  if (this.isDragging)
	    listener.fireSelectionChanged();
  };
};

EditArea.prototype.addChangeListener = function(listener) {
  this.changelistener = listener;
  this.textarea.onkeyup = function() {
    listener.fireChanged();
  };
  this.textarea.onmousedown = function() {
	this.isDragging = true;
    listener.fireSelectionChanged();
  };
  this.textarea.onmouseup = function() {
	this.isDragging = false;
    listener.fireChanged();
  };
  this.textarea.onchange = function() {
    listener.fireChanged();
  };
};

//TODO won't work under IE?
EditArea.prototype.getSelection = function() {
  return {start: this.textarea.selectionStart, end: this.textarea.selectionEnd};
};

EditArea.prototype.setSelection = function(start, end) {
  this.textarea.setSelectionRange(start,end);
  this.textarea.focus();
};

EditArea.prototype.getString = function() {
  return this.textarea.value;
};

EditArea.prototype.setString = function(str) {
  this.textarea.value = str;
  this.initialText = this.getString();
  if (this.changelistener) {
    this.changelistener.fireChanged();
  }
};

EditArea.prototype.getElem = function() {
  return this.textarea;
};

//
// ABCEditor:
//
// constructor(editarea, params)
//		if editarea is a string, then it is an HTML id of a textarea control.
//		Otherwise, it should be an instantiation of an object that expresses the EditArea interface.
//
//		params is a hash of:
//		canvas_id: or paper_id: HTML id to draw in. If not present, then the drawing happens just below the editor.
//		generate_midi: if present, then midi is generated.
//		midi_id: if present, the HTML id to place the midi control. Otherwise it is placed in the same div as the paper.
//		generate_warnings: if present, then parser warnings are displayed on the page.
//		warnings_id: if present, the HTML id to place the warnings. Otherwise they are placed in the same div as the paper.
//		onchange: if present, the callback function to call whenever there has been a change.
//		gui: if present, the paper can send changes back to the editor (presumably because the user changed something directly.)
//		parser_options: options to send to the parser engine.
//		midi_options: options to send to the midi engine.
//		render_options: options to send to the render engine.
//		indicate_changed: the dirty flag is set if this is true.
//
// - setReadOnly(bool)
//		adds or removes the class abc_textarea_readonly, and adds or removes the attribute readonly=yes
// - setDirtyStyle(bool)
//		adds or removes the class abc_textarea_dirty
// - renderTune(abc, parserparams, div)
//		Immediately renders the tune. (Useful for creating the SVG output behind the scenes, if div is hidden)
//		string abc: the ABC text
//		parserparams: params to send to the parser
//		div: the HTML id to render to.
// - modelChanged()
//		Called when the model has been changed to trigger re-rendering
// - parseABC()
//		Called internally by fireChanged()
//		returns true if there has been a change since last call.
// - updateSelection()
//		Called when the user has changed the selection. This calls the printer to show the selection.
// - fireSelectionChanged()
//		Called by the textarea object when the user has changed the selection.
// - paramChanged(printerparams)
//		Called to signal that the printer params have changed, so re-rendering should occur.
// - fireChanged()
//		Called by the textarea object when the user has changed something.
// - setNotDirty()
//		Called by the client app to reset the dirty flag
// - isDirty()
//		Returns true or false, whether the textarea contains the same text that it started with.
// - highlight(abcelem)
//		Called by the printer to highlight an area.
// - pause(bool)
//		Stops the automatic rendering when the user is typing.
//

function ABCEditor(editarea, params) {
	if (params.indicate_changed)
		this.indicate_changed = true;
  if (typeof editarea === "string") {
    this.editarea = new EditArea(editarea);
  } else {
    this.editarea = editarea;
  }
  this.editarea.addSelectionListener(this);
  this.editarea.addChangeListener(this);

  if (params.canvas_id) {
    this.div = document.getElementById(params.canvas_id);
  } else if (params.paper_id) {
    this.div = document.getElementById(params.paper_id);
  } else {
    this.div = document.createElement("DIV");
    this.editarea.getElem().parentNode.insertBefore(this.div, this.editarea.getElem());
  }

  if (params.generate_midi || params.midi_id) {
    if (params.midi_id) {
      this.mididiv = document.getElementById(params.midi_id);
    } else {
      this.mididiv = this.div;
    }
  }

  if (params.generate_warnings || params.warnings_id) {
    if (params.warnings_id) {
      this.warningsdiv = document.getElementById(params.warnings_id);
    } else {
      this.warningsdiv = this.div;
    }
  }

  this.parserparams = params.parser_options || {};
  this.midiparams = params.midi_options || {};
  this.onchangeCallback = params.onchange;

  this.printerparams = params.render_options || {};

  if (params.gui) {
    this.target = document.getElementById(editarea);
    this.printerparams.editable = true;
  }
  this.oldt = "";
  this.bReentry = false;
  this.parseABC();
  this.modelChanged();

  this.addClassName = function(element, className) {
    var hasClassName = function(element, className) {
      var elementClassName = element.className;
      return (elementClassName.length > 0 && (elementClassName === className ||
        new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
    };

    if (!hasClassName(element, className))
      element.className += (element.className ? ' ' : '') + className;
    return element;
  };

  this.removeClassName = function(element, className) {
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
  };

  this.setReadOnly = function(readOnly) {
	  var readonlyClass = 'abc_textarea_readonly';
	  var el = this.editarea.getElem();
    if (readOnly) {
      el.setAttribute('readonly', 'yes');
	  this.addClassName(el, readonlyClass);
	} else {
      el.removeAttribute('readonly');
	  this.removeClassName(el, readonlyClass);
    }
  };
}

ABCEditor.prototype.renderTune = function(abc, params, div) {
  var tunebook = new AbcTuneBook(abc);
  var abcParser = new AbcParse();
  abcParser.parse(tunebook.tunes[0].abc, params); //TODO handle multiple tunes
  var tune = abcParser.getTune();
  var paper = Raphael(div, 800, 400);
  var printer = new ABCPrinter(paper, {});	// TODO: handle printer params
  printer.printABC(tune);
};

ABCEditor.prototype.modelChanged = function() {
  if (this.tunes === undefined) {
    if (this.mididiv !== undefined && this.mididiv !== this.div)
		this.mididiv.innerHTML = "";
    this.div.innerHTML = "";
	return;
  }

  if (this.bReentry)
    return; // TODO is this likely? maybe, if we rewrite abc immediately w/ abc2abc
  this.bReentry = true;
  this.timerId = null;
  this.div.innerHTML = "";
  var paper = Raphael(this.div, 800, 400);
  this.printer = new ABCPrinter(paper, this.printerparams);
  this.printer.printABC(this.tunes);
  if (ABCMidiWriter && this.mididiv) {
    if (this.mididiv !== this.div)
		this.mididiv.innerHTML = "";
    var midiwriter = new ABCMidiWriter(this.mididiv,this.midiparams);
    midiwriter.addListener(this.printer);
    midiwriter.writeABC(this.tunes[0]); //TODO handle multiple tunes
  }
  if (this.warningsdiv) {
    this.warningsdiv.innerHTML = (this.warnings) ? this.warnings.join("<br />") : "No errors";
  }
  if (this.target) {
    var textprinter = new ABCTextPrinter(this.target, true);
    textprinter.printABC(this.tunes[0]); //TODO handle multiple tunes
  }
  this.printer.addSelectListener(this);
  this.updateSelection();
  this.bReentry = false;
};

// Call this to reparse in response to the printing parameters changing
ABCEditor.prototype.paramChanged = function(printerparams) {
	this.printerparams = printerparams;
	this.oldt = "";
	this.fireChanged();
};

// return true if the model has changed
ABCEditor.prototype.parseABC = function() {
  var t = this.editarea.getString();
  if (t===this.oldt) {
    this.updateSelection();
    return false;
  }

  this.oldt = t;
  if (t === "") {
	this.tunes = undefined;
	this.warnings = "";
	return true;
  }
  var tunebook = new AbcTuneBook(t);

  this.tunes = [];
  this.warnings = [];
  for (var i=0; i<tunebook.tunes.length; i++) {
    var abcParser = new AbcParse();
    abcParser.parse(tunebook.tunes[i].abc, this.parserparams); //TODO handle multiple tunes
    this.tunes[i] = abcParser.getTune();
    var warnings = abcParser.getWarnings() || [];
    for (var j=0; j<warnings.length; j++) {
      this.warnings.push(warnings[j]);
    }
  }
  return true;
};

ABCEditor.prototype.updateSelection = function() {
  var selection = this.editarea.getSelection();
  try {
    this.printer.rangeHighlight(selection.start, selection.end);
  } catch (e) {} // maybe printer isn't defined yet?
};

ABCEditor.prototype.fireSelectionChanged = function() {
  this.updateSelection();
};

ABCEditor.prototype.setDirtyStyle = function(isDirty) {
	if (this.indicate_changed === undefined)
		return;
  var addClassName = function(element, className) {
    var hasClassName = function(element, className) {
      var elementClassName = element.className;
      return (elementClassName.length > 0 && (elementClassName === className ||
        new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
    };

    if (!hasClassName(element, className))
      element.className += (element.className ? ' ' : '') + className;
    return element;
  };

  var removeClassName = function(element, className) {
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
  };

	var readonlyClass = 'abc_textarea_dirty';
	var el = this.editarea.getElem();
	if (isDirty) {
		addClassName(el, readonlyClass);
	} else {
		removeClassName(el, readonlyClass);
    }
};

// call when abc text is changed and needs re-parsing
ABCEditor.prototype.fireChanged = function() {
  if (this.bIsPaused)
    return;
  if (this.parseABC()) {
    var self = this;
    if (this.timerId)	// If the user is still typing, cancel the update
      clearTimeout(this.timerId);
    this.timerId = setTimeout(function () {
      self.modelChanged();
    }, 300);	// Is this a good comprimise between responsiveness and not redrawing too much?
	  var isDirty = this.isDirty();
	  if (this.wasDirty !== isDirty) {
		  this.wasDirty = isDirty;
		  this.setDirtyStyle(isDirty);
	  }
	  if (this.onchangeCallback)
		  this.onchangeCallback(this);
	  }
};

ABCEditor.prototype.setNotDirty = function() {
	this.editarea.initialText = this.editarea.getString();
	this.wasDirty = false;
	this.setDirtyStyle(false);
};

ABCEditor.prototype.isDirty = function() {
	if (this.indicate_changed === undefined)
		return false;
	return this.editarea.initialText !== this.editarea.getString();
};

ABCEditor.prototype.highlight = function(abcelem) {
  this.editarea.setSelection(abcelem.startChar, abcelem.endChar);
};

ABCEditor.prototype.pause = function(shouldPause) {
	this.bIsPaused = shouldPause;
	if (!shouldPause)
		this.updateRendering();
};

ABCEditor.prototype.pauseMidi = function(shouldPause) {
	if (shouldPause && this.mididiv) {
		this.mididivSave = this.mididiv;
		this.addClassName(this.mididiv, 'hidden');
		this.mididiv = null;
	} else if (!shouldPause && this.mididivSave) {
		this.mididiv = this.mididivSave;
		this.removeClassName(this.mididiv, 'hidden');
		this.mididivSave = null;
	}
};
<commit_msg>Fixes for IE8<commit_after>// abc_editor.js
// ABCEditor is the interface class for the area that contains the ABC text. It is responsible for
// holding the text of the tune and calling the parser and the rendering engines.
//
// EditArea is an example of using a textarea as the control that is shown to the user. As long as
// the same interface is used, ABCEditor can use a different type of object.
//
// EditArea:
// - constructor(textareaid)
//		This contains the id of a textarea control that will be used.
// - addSelectionListener(listener)
//		A callback class that contains the entry point fireSelectionChanged()
// - addChangeListener(listener)
//		A callback class that contains the entry point fireChanged()
// - getSelection()
//		returns the object { start: , end: } with the current selection in characters
// - setSelection(start, end)
//		start and end are the character positions that should be selected.
// - getString()
//		returns the ABC text that is currently displayed.
// - setString(str)
//		sets the ABC text that is currently displayed, and resets the initialText variable
// - getElem()
//		returns the textarea element
// - string initialText
//		Contains the starting text. This can be compared against the current text to see if anything changed.
//

/*global document, clearTimeout, setTimeout */
/*global ABCMidiWriter, AbcTuneBook, AbcParse, Raphael, ABCPrinter, ABCTextPrinter */
/*extern EditArea, ABCEditor */

function EditArea(textareaid) {
  this.textarea = document.getElementById(textareaid);
  this.initialText = this.textarea.value;
  this.isDragging = false;
}

EditArea.prototype.addSelectionListener = function(listener) {
  this.textarea.onmousemove = function(ev) {
	  if (this.isDragging)
	    listener.fireSelectionChanged();
  };
};

EditArea.prototype.addChangeListener = function(listener) {
  this.changelistener = listener;
  this.textarea.onkeyup = function() {
    listener.fireChanged();
  };
  this.textarea.onmousedown = function() {
	this.isDragging = true;
    listener.fireSelectionChanged();
  };
  this.textarea.onmouseup = function() {
	this.isDragging = false;
    listener.fireChanged();
  };
  this.textarea.onchange = function() {
    listener.fireChanged();
  };
};

//TODO won't work under IE?
EditArea.prototype.getSelection = function() {
  return {start: this.textarea.selectionStart, end: this.textarea.selectionEnd};
};

EditArea.prototype.setSelection = function(start, end) {
	if(this.textarea.setSelectionRange)
	   this.textarea.setSelectionRange(start, end);
	else if(this.textarea.createTextRange) {
		// For IE8
	   var e = this.textarea.createTextRange();
	   e.collapse(true);
	   e.moveEnd('character', end);
	   e.moveStart('character', start);
	   e.select();
	}
  this.textarea.focus();
};

EditArea.prototype.getString = function() {
  return this.textarea.value;
};

EditArea.prototype.setString = function(str) {
  this.textarea.value = str;
  this.initialText = this.getString();
  if (this.changelistener) {
    this.changelistener.fireChanged();
  }
};

EditArea.prototype.getElem = function() {
  return this.textarea;
};

//
// ABCEditor:
//
// constructor(editarea, params)
//		if editarea is a string, then it is an HTML id of a textarea control.
//		Otherwise, it should be an instantiation of an object that expresses the EditArea interface.
//
//		params is a hash of:
//		canvas_id: or paper_id: HTML id to draw in. If not present, then the drawing happens just below the editor.
//		generate_midi: if present, then midi is generated.
//		midi_id: if present, the HTML id to place the midi control. Otherwise it is placed in the same div as the paper.
//		generate_warnings: if present, then parser warnings are displayed on the page.
//		warnings_id: if present, the HTML id to place the warnings. Otherwise they are placed in the same div as the paper.
//		onchange: if present, the callback function to call whenever there has been a change.
//		gui: if present, the paper can send changes back to the editor (presumably because the user changed something directly.)
//		parser_options: options to send to the parser engine.
//		midi_options: options to send to the midi engine.
//		render_options: options to send to the render engine.
//		indicate_changed: the dirty flag is set if this is true.
//
// - setReadOnly(bool)
//		adds or removes the class abc_textarea_readonly, and adds or removes the attribute readonly=yes
// - setDirtyStyle(bool)
//		adds or removes the class abc_textarea_dirty
// - renderTune(abc, parserparams, div)
//		Immediately renders the tune. (Useful for creating the SVG output behind the scenes, if div is hidden)
//		string abc: the ABC text
//		parserparams: params to send to the parser
//		div: the HTML id to render to.
// - modelChanged()
//		Called when the model has been changed to trigger re-rendering
// - parseABC()
//		Called internally by fireChanged()
//		returns true if there has been a change since last call.
// - updateSelection()
//		Called when the user has changed the selection. This calls the printer to show the selection.
// - fireSelectionChanged()
//		Called by the textarea object when the user has changed the selection.
// - paramChanged(printerparams)
//		Called to signal that the printer params have changed, so re-rendering should occur.
// - fireChanged()
//		Called by the textarea object when the user has changed something.
// - setNotDirty()
//		Called by the client app to reset the dirty flag
// - isDirty()
//		Returns true or false, whether the textarea contains the same text that it started with.
// - highlight(abcelem)
//		Called by the printer to highlight an area.
// - pause(bool)
//		Stops the automatic rendering when the user is typing.
//

function ABCEditor(editarea, params) {
	if (params.indicate_changed)
		this.indicate_changed = true;
  if (typeof editarea === "string") {
    this.editarea = new EditArea(editarea);
  } else {
    this.editarea = editarea;
  }
  this.editarea.addSelectionListener(this);
  this.editarea.addChangeListener(this);

  if (params.canvas_id) {
    this.div = document.getElementById(params.canvas_id);
  } else if (params.paper_id) {
    this.div = document.getElementById(params.paper_id);
  } else {
    this.div = document.createElement("DIV");
    this.editarea.getElem().parentNode.insertBefore(this.div, this.editarea.getElem());
  }

  if (params.generate_midi || params.midi_id) {
    if (params.midi_id) {
      this.mididiv = document.getElementById(params.midi_id);
    } else {
      this.mididiv = this.div;
    }
  }

  if (params.generate_warnings || params.warnings_id) {
    if (params.warnings_id) {
      this.warningsdiv = document.getElementById(params.warnings_id);
    } else {
      this.warningsdiv = this.div;
    }
  }

  this.parserparams = params.parser_options || {};
  this.midiparams = params.midi_options || {};
  this.onchangeCallback = params.onchange;

  this.printerparams = params.render_options || {};

  if (params.gui) {
    this.target = document.getElementById(editarea);
    this.printerparams.editable = true;
  }
  this.oldt = "";
  this.bReentry = false;
  this.parseABC();
  this.modelChanged();

  this.addClassName = function(element, className) {
    var hasClassName = function(element, className) {
      var elementClassName = element.className;
      return (elementClassName.length > 0 && (elementClassName === className ||
        new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
    };

    if (!hasClassName(element, className))
      element.className += (element.className ? ' ' : '') + className;
    return element;
  };

  this.removeClassName = function(element, className) {
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
  };

  this.setReadOnly = function(readOnly) {
	  var readonlyClass = 'abc_textarea_readonly';
	  var el = this.editarea.getElem();
    if (readOnly) {
      el.setAttribute('readonly', 'yes');
	  this.addClassName(el, readonlyClass);
	} else {
      el.removeAttribute('readonly');
	  this.removeClassName(el, readonlyClass);
    }
  };
}

ABCEditor.prototype.renderTune = function(abc, params, div) {
  var tunebook = new AbcTuneBook(abc);
  var abcParser = new AbcParse();
  abcParser.parse(tunebook.tunes[0].abc, params); //TODO handle multiple tunes
  var tune = abcParser.getTune();
  var paper = Raphael(div, 800, 400);
  var printer = new ABCPrinter(paper, {});	// TODO: handle printer params
  printer.printABC(tune);
};

ABCEditor.prototype.modelChanged = function() {
  if (this.tunes === undefined) {
    if (this.mididiv !== undefined && this.mididiv !== this.div)
		this.mididiv.innerHTML = "";
    this.div.innerHTML = "";
	return;
  }

  if (this.bReentry)
    return; // TODO is this likely? maybe, if we rewrite abc immediately w/ abc2abc
  this.bReentry = true;
  this.timerId = null;
  this.div.innerHTML = "";
  var paper = Raphael(this.div, 800, 400);
  this.printer = new ABCPrinter(paper, this.printerparams);
  this.printer.printABC(this.tunes);
  if (ABCMidiWriter && this.mididiv) {
    if (this.mididiv !== this.div)
		this.mididiv.innerHTML = "";
    var midiwriter = new ABCMidiWriter(this.mididiv,this.midiparams);
    midiwriter.addListener(this.printer);
    midiwriter.writeABC(this.tunes[0]); //TODO handle multiple tunes
  }
  if (this.warningsdiv) {
    this.warningsdiv.innerHTML = (this.warnings) ? this.warnings.join("<br />") : "No errors";
  }
  if (this.target) {
    var textprinter = new ABCTextPrinter(this.target, true);
    textprinter.printABC(this.tunes[0]); //TODO handle multiple tunes
  }
  this.printer.addSelectListener(this);
  this.updateSelection();
  this.bReentry = false;
};

// Call this to reparse in response to the printing parameters changing
ABCEditor.prototype.paramChanged = function(printerparams) {
	this.printerparams = printerparams;
	this.oldt = "";
	this.fireChanged();
};

// return true if the model has changed
ABCEditor.prototype.parseABC = function() {
  var t = this.editarea.getString();
  if (t===this.oldt) {
    this.updateSelection();
    return false;
  }

  this.oldt = t;
  if (t === "") {
	this.tunes = undefined;
	this.warnings = "";
	return true;
  }
  var tunebook = new AbcTuneBook(t);

  this.tunes = [];
  this.warnings = [];
  for (var i=0; i<tunebook.tunes.length; i++) {
    var abcParser = new AbcParse();
    abcParser.parse(tunebook.tunes[i].abc, this.parserparams); //TODO handle multiple tunes
    this.tunes[i] = abcParser.getTune();
    var warnings = abcParser.getWarnings() || [];
    for (var j=0; j<warnings.length; j++) {
      this.warnings.push(warnings[j]);
    }
  }
  return true;
};

ABCEditor.prototype.updateSelection = function() {
  var selection = this.editarea.getSelection();
  try {
    this.printer.rangeHighlight(selection.start, selection.end);
  } catch (e) {} // maybe printer isn't defined yet?
};

ABCEditor.prototype.fireSelectionChanged = function() {
  this.updateSelection();
};

ABCEditor.prototype.setDirtyStyle = function(isDirty) {
	if (this.indicate_changed === undefined)
		return;
  var addClassName = function(element, className) {
    var hasClassName = function(element, className) {
      var elementClassName = element.className;
      return (elementClassName.length > 0 && (elementClassName === className ||
        new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
    };

    if (!hasClassName(element, className))
      element.className += (element.className ? ' ' : '') + className;
    return element;
  };

  var removeClassName = function(element, className) {
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
  };

	var readonlyClass = 'abc_textarea_dirty';
	var el = this.editarea.getElem();
	if (isDirty) {
		addClassName(el, readonlyClass);
	} else {
		removeClassName(el, readonlyClass);
    }
};

// call when abc text is changed and needs re-parsing
ABCEditor.prototype.fireChanged = function() {
  if (this.bIsPaused)
    return;
  if (this.parseABC()) {
    var self = this;
    if (this.timerId)	// If the user is still typing, cancel the update
      clearTimeout(this.timerId);
    this.timerId = setTimeout(function () {
      self.modelChanged();
    }, 300);	// Is this a good comprimise between responsiveness and not redrawing too much?
	  var isDirty = this.isDirty();
	  if (this.wasDirty !== isDirty) {
		  this.wasDirty = isDirty;
		  this.setDirtyStyle(isDirty);
	  }
	  if (this.onchangeCallback)
		  this.onchangeCallback(this);
	  }
};

ABCEditor.prototype.setNotDirty = function() {
	this.editarea.initialText = this.editarea.getString();
	this.wasDirty = false;
	this.setDirtyStyle(false);
};

ABCEditor.prototype.isDirty = function() {
	if (this.indicate_changed === undefined)
		return false;
	return this.editarea.initialText !== this.editarea.getString();
};

ABCEditor.prototype.highlight = function(abcelem) {
  this.editarea.setSelection(abcelem.startChar, abcelem.endChar);
};

ABCEditor.prototype.pause = function(shouldPause) {
	this.bIsPaused = shouldPause;
	if (!shouldPause)
		this.updateRendering();
};

ABCEditor.prototype.pauseMidi = function(shouldPause) {
	if (shouldPause && this.mididiv) {
		this.mididivSave = this.mididiv;
		this.addClassName(this.mididiv, 'hidden');
		this.mididiv = null;
	} else if (!shouldPause && this.mididivSave) {
		this.mididiv = this.mididivSave;
		this.removeClassName(this.mididiv, 'hidden');
		this.mididivSave = null;
	}
};
<|endoftext|>


<commit_before>// client-test.js
//
// Test the client module
//
// Copyright 2012, StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var assert = require('assert'),
    vows = require('vows');

vows.describe('client module interface').addBatch({
    'When we check for a test suite': {
        topic: function() {
            return false;
        },
        'there is one': function(tsExists) {
            assert.isTrue(tsExists);
        }
    }
}).export(module);

<commit_msg>client unit test<commit_after>// client-test.js
//
// Test the client module
//
// Copyright 2012, StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var assert = require('assert'),
    vows = require('vows'),
    databank = require('databank'),
    URLMaker = require('../lib/urlmaker').URLMaker,
    modelBatch = require('./lib/model').modelBatch,
    Databank = databank.Databank,
    DatabankObject = databank.DatabankObject;

// Need this to make IDs

URLMaker.hostname = "example.net";

// Dummy databank

DatabankObject.bank = Databank.get('memory', {});

var suite = vows.describe('client module interface');

var testSchema = {
    pkey: 'consumer_key',
    fields: ['title',
             'description',
             'host',
             'secret',
             'contacts',
             'logo_url',
             'redirect_uris',
             'type',
             'created',
             'updated'],
    indices: ['title']
};

var testData = {
    'create': {
        title: "MyApp",
        description: "an app I made",
        host: "example.com",
        contacts: ["evan@example.com"],
        type: "web"
    },
    'update': {
        contacts: ["evan@example.com", "jerry@example.com"]
    }
};

suite.addBatch(modelBatch('client', 'Client', testSchema, testData));

suite.addBatch({
    'When we create a new client': {
        topic: function() {
            var Client = require('../lib/model/client').Client;
            Client.create({title: "Another App",
                           description: "another app"},
                          this.callback);
        },
        'secret is automatically created': function(err, client) {
            assert.isString(client.secret);
        }
    }
});

suite.export(module);
<|endoftext|>


<commit_before>/* global __dirname */
/* global process */

var fs = require("fs");
var path = require("path");
var babel = require("babel-core");
var template = require("babel-template");
var spawn = require('child_process').spawn;

// Custom plugin to simulate macro expressions
var transformMacroExpressions = {
  visitor: {
    StringLiteral(path) {
      if (!path.node.macro)
          return;

      try {
        var buildArgs = {};
        var buildMacro = template(path.node.value);
        for (var i = 0; i < path.node.args.length; i++) {
            buildArgs["$" + i] = path.node.args[i];
        }
        path.replaceWithMultiple(buildMacro(buildArgs));
      }
      catch (err) {
          console.log("BABEL ERROR: Failed to parse macro: " + path.node.value);
          process.exit(1);
      }
    }
  }
};

// In pattern matching targets, some variables may have the same name
// but we can safely remove the duplicates after do expressions have
// been resolved and the variable declarations hoisted
var removeDuplicatedVarDeclarators = {
  visitor: {
    VariableDeclaration(path) {
      var buffer = [];
      var duplicated = [];

      for (var i = 0; i < path.node.declarations.length; i++) {
          var decl = path.node.declarations[i];
          if (typeof decl.id.name === "string") {
              if (buffer.indexOf(decl.id.name) > -1 && decl.init == null) {
                  duplicated.push(i);
              }
              else {
                  buffer.push(decl.id.name);
              }
          }
      }

      try {
        if (duplicated.length > 0) {
            var node = path.node;
            for (var j = duplicated.length - 1; j >= 0; j--) {
                node.declarations.splice(duplicated[j], 1);
            }
            path.replaceWith(node);
        }
      }
      catch (err) {
          console.log("BABEL ERROR: Failed to remove duplicated variables");
          process.exit(1);
      }
    }
  }
};

var babelPlugins = [
    transformMacroExpressions,
    // "transform-es2015-block-scoping", // This creates too many function wrappers
    "transform-do-expressions",
    removeDuplicatedVarDeclarators,
    "transform-es5-property-mutators",
    "transform-es2015-arrow-functions",
    "transform-es2015-classes",
    "transform-es2015-computed-properties",
    "transform-es2015-for-of",
    "transform-es2015-object-super",
    "transform-es2015-parameters",
    "transform-es2015-shorthand-properties",
    "transform-es2015-spread"
];

function ensureDirExists(dir, cont) {
    if (fs.existsSync(dir)) {
        if (typeof cont === "function") { cont(); }
    }
    else {
        ensureDirExists(path.dirname(dir), function() {
            fs.mkdirSync(dir);
            if (typeof cont === "function") { cont(); }
        })
    }
}

function babelifyToConsole(babelAst) {
    var opts = { plugins: babelPlugins };
    var parsed = babel.transformFromAst(babelAst, null, opts);
    console.log(parsed.code);
}

function babelifyToFile(projDir, outDir, babelAst) {
    var targetFile = path.join(outDir, path.relative(projDir, babelAst.fileName));
    targetFile = targetFile.replace(path.extname(babelAst.fileName), ".js")

    var opts = {
        sourceMaps: true,
        sourceMapTarget: path.basename(targetFile),
        sourceFileName: babelAst.fileName,
        plugins: babelPlugins
    };

    var parsed = babel.transformFromAst(babelAst, null, opts);

    ensureDirExists(path.dirname(targetFile));
    fs.writeFileSync(targetFile, parsed.code);
    fs.appendFileSync(targetFile, "\n//# sourceMappingURL=" + path.basename(targetFile)+".map");

    fs.writeFileSync(targetFile + ".map", JSON.stringify(parsed.map));
}

try {
    var opts = {
        lib: ".",
        outDir: ".",
        symbols: [],
        watch: false
    }

    for (var i=2; i < process.argv.length; i++) {
        var key = process.argv[i].substring(2);
        opts[key] = key == "watch" ? true : opts[key] = process.argv[++i];
    }

    var fableCwd = process.cwd();
    var fableCmd = process.platform === "win32" ? "cmd" : "mono";
    var fableCmdArgs = [path.resolve(__dirname, ["..", "build", "main", "Fable.exe"].join(path.sep))];
    if (process.platform === "win32") {
        fableCmdArgs.unshift("/C");
    }

    if (typeof opts.projFile === "string") {
        fableCwd = path.dirname(path.isAbsolute(opts.projFile) ? opts.projFile : path.join(fableCwd, opts.projFile));
        opts.projFile = path.basename(opts.projFile);

        var cfgFile = path.join(fableCwd, "fableconfig.json");
        if (fs.existsSync(cfgFile)) {
            var cfg = JSON.parse(fs.readFileSync(cfgFile).toString());
            for (var key in cfg) {
                opts[key] = cfg[key];
            }
        }
    }
    else if (typeof opts.code !== "string") {
        throw "No correct --projFile or --code argument provided";
    }

    // Module target
    if (opts.env === "browser") {
        babelPlugins.push("transform-es2015-modules-amd");
    }
    else if (opts.env === "node") {
        babelPlugins.push("transform-es2015-modules-commonjs");
    }
    else {
        babelPlugins.push("transform-es2015-modules-umd");
    }

    var addArg = function(k, v) {
        if (v != null) {
            fableCmdArgs.push("--" + k, v.toString());
        }
    };
    for (var k in opts) {
        if (Array.isArray(opts[k])) {
            opts[k].forEach(function (v) {
                addArg(k,v);
            });
        }
        else {
            addArg(k, opts[k]);
        }
    }
    console.log(fableCwd + ">" + fableCmd + " " + fableCmdArgs.join(" "));

    var proc = spawn(fableCmd, fableCmdArgs, { cwd: fableCwd });

    if (opts.watch) {
        proc.stdin.setEncoding('utf-8');
        fs.watch(fableCwd, { persistent: true, recursive: true }, function(ev, filename) {
            var ext = path.extname(filename).toLowerCase();
            if (ev == "change" && (ext == ".fs" || ext == ".fsx")) {
                proc.stdin.write(path.join(fableCwd, filename) + "\n");
            }
        });
    }

    proc.on('exit', function(code) {
        console.log("Finished");
        process.exit(code);
    });

    proc.stderr.on('data', function(data) {
        console.log("FABLE ERROR: " + data.toString().substring(0, 300) + "...");
    });

    var buffer = "";
    proc.stdout.on("data", function(data) {
        var txt = data.toString();
        // New lines are parsed as literals \n,
        // so this complicated RegExp isn't necessary
        // var json, closing = /\}\n(?![^$\{][\s\S]*")/.exec(txt);
        var json, closing = txt.indexOf("\n");
        if (closing == -1) {
            buffer += txt;
            return;
        }
        else {
            json = buffer + txt.substring(0, closing + 1);
            buffer = txt.substring(closing + 1);
        }

        var err = null;
        try {
            var babelAst = JSON.parse(json);
            if (babelAst.type == "Error") {
                err = babelAst.message;
            }
            else {
                if (opts.projFile) {
                    babelifyToFile(fableCwd, path.join(fableCwd, opts.outDir), babelAst);
                    console.log("Compiled " + babelAst.fileName);
                }
                else {
                    babelifyToConsole(babelAst);
                }
            }
        }
        catch (e) {
            err = e;
        }
        if (err != null) {
            console.log(err);
            if (!opts.watch) {
                process.exit(1);
            }
        }
    });
}
catch (err) {
    console.log("ARG ERROR: " + err);
    process.exit(1);
}
<commit_msg>fixing "The path is not of a legal form" on Windows if just fileName is passed as projFile (would fail in System.IO.Path.NormalizePath called from Microsoft.FSharp.Compiler.CompileOps.Resolve and Fable.Main.parseFSharpProject)<commit_after>/* global __dirname */
/* global process */

var fs = require("fs");
var path = require("path");
var babel = require("babel-core");
var template = require("babel-template");
var spawn = require('child_process').spawn;

// Custom plugin to simulate macro expressions
var transformMacroExpressions = {
  visitor: {
    StringLiteral(path) {
      if (!path.node.macro)
          return;

      try {
        var buildArgs = {};
        var buildMacro = template(path.node.value);
        for (var i = 0; i < path.node.args.length; i++) {
            buildArgs["$" + i] = path.node.args[i];
        }
        path.replaceWithMultiple(buildMacro(buildArgs));
      }
      catch (err) {
          console.log("BABEL ERROR: Failed to parse macro: " + path.node.value);
          process.exit(1);
      }
    }
  }
};

// In pattern matching targets, some variables may have the same name
// but we can safely remove the duplicates after do expressions have
// been resolved and the variable declarations hoisted
var removeDuplicatedVarDeclarators = {
  visitor: {
    VariableDeclaration(path) {
      var buffer = [];
      var duplicated = [];

      for (var i = 0; i < path.node.declarations.length; i++) {
          var decl = path.node.declarations[i];
          if (typeof decl.id.name === "string") {
              if (buffer.indexOf(decl.id.name) > -1 && decl.init == null) {
                  duplicated.push(i);
              }
              else {
                  buffer.push(decl.id.name);
              }
          }
      }

      try {
        if (duplicated.length > 0) {
            var node = path.node;
            for (var j = duplicated.length - 1; j >= 0; j--) {
                node.declarations.splice(duplicated[j], 1);
            }
            path.replaceWith(node);
        }
      }
      catch (err) {
          console.log("BABEL ERROR: Failed to remove duplicated variables");
          process.exit(1);
      }
    }
  }
};

var babelPlugins = [
    transformMacroExpressions,
    // "transform-es2015-block-scoping", // This creates too many function wrappers
    "transform-do-expressions",
    removeDuplicatedVarDeclarators,
    "transform-es5-property-mutators",
    "transform-es2015-arrow-functions",
    "transform-es2015-classes",
    "transform-es2015-computed-properties",
    "transform-es2015-for-of",
    "transform-es2015-object-super",
    "transform-es2015-parameters",
    "transform-es2015-shorthand-properties",
    "transform-es2015-spread"
];

function ensureDirExists(dir, cont) {
    if (fs.existsSync(dir)) {
        if (typeof cont === "function") { cont(); }
    }
    else {
        ensureDirExists(path.dirname(dir), function() {
            fs.mkdirSync(dir);
            if (typeof cont === "function") { cont(); }
        })
    }
}

function babelifyToConsole(babelAst) {
    var opts = { plugins: babelPlugins };
    var parsed = babel.transformFromAst(babelAst, null, opts);
    console.log(parsed.code);
}

function babelifyToFile(projDir, outDir, babelAst) {
    var targetFile = path.join(outDir, path.relative(projDir, babelAst.fileName));
    targetFile = targetFile.replace(path.extname(babelAst.fileName), ".js")

    var opts = {
        sourceMaps: true,
        sourceMapTarget: path.basename(targetFile),
        sourceFileName: babelAst.fileName,
        plugins: babelPlugins
    };

    var parsed = babel.transformFromAst(babelAst, null, opts);

    ensureDirExists(path.dirname(targetFile));
    fs.writeFileSync(targetFile, parsed.code);
    fs.appendFileSync(targetFile, "\n//# sourceMappingURL=" + path.basename(targetFile)+".map");

    fs.writeFileSync(targetFile + ".map", JSON.stringify(parsed.map));
}

try {
    var opts = {
        lib: ".",
        outDir: ".",
        symbols: [],
        watch: false
    }

    for (var i=2; i < process.argv.length; i++) {
        var key = process.argv[i].substring(2);
        opts[key] = key == "watch" ? true : opts[key] = process.argv[++i];
    }

    var fableCwd = process.cwd();
    var fableCmd = process.platform === "win32" ? "cmd" : "mono";
    var fableCmdArgs = [path.resolve(__dirname, ["..", "build", "main", "Fable.exe"].join(path.sep))];
    if (process.platform === "win32") {
        fableCmdArgs.unshift("/C");
    }

    if (typeof opts.projFile === "string") {
        fableCwd = path.dirname(path.isAbsolute(opts.projFile) ? opts.projFile : path.join(fableCwd, opts.projFile));
        opts.projFile = "./" + path.basename(opts.projFile);

        var cfgFile = path.join(fableCwd, "fableconfig.json");
        if (fs.existsSync(cfgFile)) {
            var cfg = JSON.parse(fs.readFileSync(cfgFile).toString());
            for (var key in cfg) {
                opts[key] = cfg[key];
            }
        }
    }
    else if (typeof opts.code !== "string") {
        throw "No correct --projFile or --code argument provided";
    }

    // Module target
    if (opts.env === "browser") {
        babelPlugins.push("transform-es2015-modules-amd");
    }
    else if (opts.env === "node") {
        babelPlugins.push("transform-es2015-modules-commonjs");
    }
    else {
        babelPlugins.push("transform-es2015-modules-umd");
    }

    var addArg = function(k, v) {
        if (v != null) {
            fableCmdArgs.push("--" + k, v.toString());
        }
    };
    for (var k in opts) {
        if (Array.isArray(opts[k])) {
            opts[k].forEach(function (v) {
                addArg(k,v);
            });
        }
        else {
            addArg(k, opts[k]);
        }
    }
    console.log(fableCwd + ">" + fableCmd + " " + fableCmdArgs.join(" "));

    var proc = spawn(fableCmd, fableCmdArgs, { cwd: fableCwd });

    if (opts.watch) {
        proc.stdin.setEncoding('utf-8');
        fs.watch(fableCwd, { persistent: true, recursive: true }, function(ev, filename) {
            var ext = path.extname(filename).toLowerCase();
            if (ev == "change" && (ext == ".fs" || ext == ".fsx")) {
                proc.stdin.write(path.join(fableCwd, filename) + "\n");
            }
        });
    }

    proc.on('exit', function(code) {
        console.log("Finished");
        process.exit(code);
    });

    proc.stderr.on('data', function(data) {
        console.log("FABLE ERROR: " + data.toString().substring(0, 300) + "...");
    });

    var buffer = "";
    proc.stdout.on("data", function(data) {
        var txt = data.toString();
        // New lines are parsed as literals \n,
        // so this complicated RegExp isn't necessary
        // var json, closing = /\}\n(?![^$\{][\s\S]*")/.exec(txt);
        var json, closing = txt.indexOf("\n");
        if (closing == -1) {
            buffer += txt;
            return;
        }
        else {
            json = buffer + txt.substring(0, closing + 1);
            buffer = txt.substring(closing + 1);
        }

        var err = null;
        try {
            var babelAst = JSON.parse(json);
            if (babelAst.type == "Error") {
                err = babelAst.message;
            }
            else {
                if (opts.projFile) {
                    babelifyToFile(fableCwd, path.join(fableCwd, opts.outDir), babelAst);
                    console.log("Compiled " + babelAst.fileName);
                }
                else {
                    babelifyToConsole(babelAst);
                }
            }
        }
        catch (e) {
            err = e;
        }
        if (err != null) {
            console.log(err);
            if (!opts.watch) {
                process.exit(1);
            }
        }
    });
}
catch (err) {
    console.log("ARG ERROR: " + err);
    process.exit(1);
}
<|endoftext|>


<commit_before>'use strict';

var Distributor = require('../lib/distributor').Distributor,
	Node = require('../lib/node').Node,
	expect = require('expect.js'),
	EventEmitter = require('events').EventEmitter;

describe('Distributor', function() {
	var distributor,
		project1 = {name: 'project1'};

	var createNodeMock = function(executorRun) {
		return function(params) {
			var node = new Node(params);
			node._createExecutor = function() {
				var executor = new EventEmitter();
				executor.run = executorRun;
				return executor;
			};
			return node;
		};
	};

	var expectUpdateBuild = function(distributor, build, number, conditionsHash) {
		var conditions = conditionsHash[number];
		expect(distributor.queue).length(conditions.queue.length);
		expect(build.status).equal(conditions.build.status);
		if (build.status === 'error') {
			expect(build.error.message).eql(conditions.build.error.message);
		}
	};

	describe('with sucess project', function() {
		var originalCreateNode;

		before(function() {
			originalCreateNode = Distributor.prototype._createNode;
			Distributor.prototype._createNode = createNodeMock(
				function(params, callback) {
					setTimeout(callback, 10);
				}
			);
		});

		it('instance should be created without errors', function() {
			var number = 1;
			var conditionsHash = {
				1: {queue: {length: 0}, build: {status: 'queued'}},
				2: {queue: {length: 1}, build: {status: 'in-progress'}},
				3: {queue: {length: 0}, build: {status: 'done'}},
				4: 'Should never happend'
			};
			var saveBuild = function(build, callback) {
				expectUpdateBuild(distributor, build, number, conditionsHash);
				number++;
				callback(null, build)
			};

			distributor = new Distributor({
				nodes: [{type: 'local', maxExecutorsCount: 1}],
				saveBuild: saveBuild
			});
		});

		it('should run without errors', function() {
			distributor.run(project1, {}, function(err) {
				expect(err).not.ok();
			});
		});

		it('wait for project done (should no errors)', function(done) {
			setTimeout(done, 20);
		});

		after(function() {
			Distributor.prototype._createNode = originalCreateNode;
		});
	});

	describe('with fail project', function() {
		var originalCreateNode;

		before(function() {
			originalCreateNode = Distributor.prototype._createNode;
			Distributor.prototype._createNode = createNodeMock(
				function(params, callback) {
					setTimeout(function() {
						callback(new Error('Some error'));
					}, 10);
				}
			);
		});

		it('instance should be created without errors', function() {
			var number = 1;
			var conditionsHash = {
				1: {queue: {length: 0}, build: {status: 'queued'}},
				2: {queue: {length: 1}, build: {status: 'in-progress'}},
				3: {
					queue: {length: 0},
					build: {status: 'error', error: {message: 'Some error'}}
				},
				4: 'Should never happend'
			};
			var saveBuild = function(build, callback) {
				expectUpdateBuild(distributor, build, number, conditionsHash);
				number++;
				callback(null, build)
			};

			distributor = new Distributor({
				nodes: [{type: 'local', maxExecutorsCount: 1}],
				saveBuild: saveBuild
			});
		});

		it('should run with errors', function() {
			distributor.run(project1, {}, function(err) {
				expect(err).not.ok();
			});
		});

		it('wait for project done (should no errors)', function(done) {
			setTimeout(done, 20);
		});

		after(function() {
			Distributor.prototype._createNode = originalCreateNode;
		});
	});
});
<commit_msg>fix distributor tests<commit_after>'use strict';

var Distributor = require('../lib/distributor').Distributor,
	Node = require('../lib/node').Node,
	expect = require('expect.js'),
	EventEmitter = require('events').EventEmitter;

describe('Distributor', function() {
	var distributor,
		project1 = {name: 'project1'};

	var createNodeMock = function(executorRun) {
		return function(params) {
			var node = new Node(params);
			node._createExecutor = function() {
				var executor = new EventEmitter();
				executor.run = executorRun;
				return executor;
			};
			return node;
		};
	};

	var expectUpdateBuild = function(distributor, build, number, conditionsHash) {
		var conditions = conditionsHash[number];
		expect(distributor.queue).length(conditions.queue.length);
		expect(build.status).equal(conditions.build.status);
		if (build.status === 'error') {
			expect(build.error).eql(conditions.build.error.message);
		}
	};

	describe('with sucess project', function() {
		var originalCreateNode;

		before(function() {
			originalCreateNode = Distributor.prototype._createNode;
			Distributor.prototype._createNode = createNodeMock(
				function(params, callback) {
					setTimeout(callback, 10);
				}
			);
		});

		it('instance should be created without errors', function() {
			var number = 1;
			var conditionsHash = {
				1: {queue: {length: 0}, build: {status: 'queued'}},
				2: {queue: {length: 1}, build: {status: 'in-progress'}},
				3: {queue: {length: 0}, build: {status: 'done'}},
				4: 'Should never happend'
			};
			var saveBuild = function(build, callback) {
				expectUpdateBuild(distributor, build, number, conditionsHash);
				number++;
				callback(null, build)
			};

			distributor = new Distributor({
				nodes: [{type: 'local', maxExecutorsCount: 1}],
				saveBuild: saveBuild
			});
		});

		it('should run without errors', function() {
			distributor.run(project1, {}, function(err) {
				expect(err).not.ok();
			});
		});

		it('wait for project done (should no errors)', function(done) {
			setTimeout(done, 20);
		});

		after(function() {
			Distributor.prototype._createNode = originalCreateNode;
		});
	});

	describe('with fail project', function() {
		var originalCreateNode;

		before(function() {
			originalCreateNode = Distributor.prototype._createNode;
			Distributor.prototype._createNode = createNodeMock(
				function(params, callback) {
					setTimeout(function() {
						callback(new Error('Some error'));
					}, 10);
				}
			);
		});

		it('instance should be created without errors', function() {
			var number = 1;
			var conditionsHash = {
				1: {queue: {length: 0}, build: {status: 'queued'}},
				2: {queue: {length: 1}, build: {status: 'in-progress'}},
				3: {
					queue: {length: 0},
					build: {status: 'error', error: {message: 'Some error'}}
				},
				4: 'Should never happend'
			};
			var saveBuild = function(build, callback) {
				expectUpdateBuild(distributor, build, number, conditionsHash);
				number++;
				callback(null, build)
			};

			distributor = new Distributor({
				nodes: [{type: 'local', maxExecutorsCount: 1}],
				saveBuild: saveBuild
			});
		});

		it('should run with errors', function() {
			distributor.run(project1, {}, function(err) {
				expect(err).not.ok();
			});
		});

		it('wait for project done (should no errors)', function(done) {
			setTimeout(done, 20);
		});

		after(function() {
			Distributor.prototype._createNode = originalCreateNode;
		});
	});
});
<|endoftext|>


<commit_before>var selftest = require('../tool-testing/selftest.js');
var Sandbox = selftest.Sandbox;
var utils = require('../utils/utils.js');
var net = require('net');
var Future = require('fibers/future');
var _ = require('underscore');
var files = require('../fs/files.js');

// Tests that observeChanges continues to work even over a mongo failover.
selftest.define("mongo failover", ["slow"], function () {
  var s = new Sandbox();
  s.set('METEOR_TEST_MULTIPLE_MONGOD_REPLSET', 't');
  s.createApp("failover-test", "failover-test");
  s.cd("failover-test");

  var run = s.run("--once", "--raw-logs");
  run.waitSecs(120);
  run.match("SUCCESS\n");
  run.expectEnd();
  run.expectExit(0);
});

function testMeteorMongo(appDir) {
  var s = new Sandbox();
  s.createApp(appDir, 'standard-app');
  s.cd(appDir);

  var run = s.run();
  run.match(appDir);
  run.match('proxy');
  run.waitSecs(15);
  run.match('Started MongoDB');
  run.waitSecs(15);

  var mongoRun = s.run('mongo');
  run.waitSecs(15);
  // Note: when mongo shell's input is not a tty, there is no prompt.
  mongoRun.write('db.version()\n');
  mongoRun.match(/4\.\d+\.\d+/);
  run.waitSecs(5);
  mongoRun.stop();

  run.stop();
}

selftest.define("meteor mongo", function () {
  testMeteorMongo('asdfzasdf');
});

// Regression test for #3999.  Note the Cyrillic character in the pathname.
//
// XXX This test fails on Windows for two different reasons:
// - With the Unicode directory name, `meteor run` fails to start mongod
// - If you change appDir to not have the Unicode character, the reads
//   from the mongo shell process seem to be randomly corrupted
// https://github.com/meteor/windows-preview/issues/145
selftest.define("meteor mongo in unicode dir", function () {
  testMeteorMongo('asdf\u0442asdf');
});

selftest.define("mongo with multiple --port numbers (#7563)", function () {
  var s = new Sandbox();
  s.createApp("mongo-multiple-ports", "mongo-sanity");
  s.cd("mongo-multiple-ports");

  function check(args, matches) {
    const run = s.run(...args);
    run.waitSecs(30);
    matches.forEach(m => {
      run.waitSecs(10);
      run.match(m);
    });
    run.stop();
  }

  // Make absolutely sure we're creating the database for the first time.
  check(["reset"], ["Project reset."]);

  let count = 0;
  function next() {
    return ["Started MongoDB", "count: " + (++count)];
  }

  check(["run"], next());
  check(["--port", "4321"], next());
  check(["--port", "4123"], next());
  check([], next());
});
<commit_msg>Wait until test app is running before starting Mongo shell.<commit_after>var selftest = require('../tool-testing/selftest.js');
var Sandbox = selftest.Sandbox;
var utils = require('../utils/utils.js');
var net = require('net');
var Future = require('fibers/future');
var _ = require('underscore');
var files = require('../fs/files.js');

// Tests that observeChanges continues to work even over a mongo failover.
selftest.define("mongo failover", ["slow"], function () {
  var s = new Sandbox();
  s.set('METEOR_TEST_MULTIPLE_MONGOD_REPLSET', 't');
  s.createApp("failover-test", "failover-test");
  s.cd("failover-test");

  var run = s.run("--once", "--raw-logs");
  run.waitSecs(120);
  run.match("SUCCESS\n");
  run.expectEnd();
  run.expectExit(0);
});

function testMeteorMongo(appDir) {
  var s = new Sandbox();
  s.createApp(appDir, 'standard-app');
  s.cd(appDir);

  var run = s.run();
  run.match(appDir);
  run.match('proxy');
  run.waitSecs(15);
  run.match('Started MongoDB');
  run.waitSecs(15);
  run.match('App running');
  run.waitSecs(15);

  var mongoRun = s.run('mongo');
  run.waitSecs(15);
  // Note: when mongo shell's input is not a tty, there is no prompt.
  mongoRun.write('db.version()\n');
  mongoRun.match(/4\.\d+\.\d+/);
  run.waitSecs(5);
  mongoRun.stop();

  run.stop();
}

selftest.define("meteor mongo", function () {
  testMeteorMongo('asdfzasdf');
});

// Regression test for #3999.  Note the Cyrillic character in the pathname.
//
// XXX This test fails on Windows for two different reasons:
// - With the Unicode directory name, `meteor run` fails to start mongod
// - If you change appDir to not have the Unicode character, the reads
//   from the mongo shell process seem to be randomly corrupted
// https://github.com/meteor/windows-preview/issues/145
selftest.define("meteor mongo in unicode dir", function () {
  testMeteorMongo('asdf\u0442asdf');
});

selftest.define("mongo with multiple --port numbers (#7563)", function () {
  var s = new Sandbox();
  s.createApp("mongo-multiple-ports", "mongo-sanity");
  s.cd("mongo-multiple-ports");

  function check(args, matches) {
    const run = s.run(...args);
    run.waitSecs(30);
    matches.forEach(m => {
      run.waitSecs(10);
      run.match(m);
    });
    run.stop();
  }

  // Make absolutely sure we're creating the database for the first time.
  check(["reset"], ["Project reset."]);

  let count = 0;
  function next() {
    return ["Started MongoDB", "count: " + (++count)];
  }

  check(["run"], next());
  check(["--port", "4321"], next());
  check(["--port", "4123"], next());
  check([], next());
});
<|endoftext|>


<commit_before>var assert = require('assert')
var fs = require('fs')
var path = require('path')
var testutil = require('testutil')
var fse = require('../')

var TEST_DIR = ''

describe('fs-extra', function() {
  beforeEach(function() {
    TEST_DIR = testutil.createTestDir('fs-extra')
  })

  afterEach(function(done) {
    fse.remove(TEST_DIR, done)
  })

  describe('+ ensureFile()', function() {
    describe('> when file exists', function() {
      it('should not do anything', function(done) {
        var file = path.join(TEST_DIR, 'file.txt')
        fs.writeFileSync(file, 'blah')

        assert(fs.existsSync(file))
        fse.ensureFile(file, function(err) {
          assert.ifError(err)
          assert(fs.existsSync(file))
          done()
        })
      })
    })

    describe('> when file does not exist', function() {
      it('should create the file', function(done) {
        var file = path.join(TEST_DIR, 'dir/that/does/not/exist', 'file.txt')

        assert(!fs.existsSync(file))
        fse.ensureFile(file, function(err) {
          assert.ifError(err)
          assert(fs.existsSync(file))
          done()
        })
      })
    })
  })

  describe('+ ensureFileSync()', function() {
    describe('> when file exists', function() {
      it('should not do anything', function() {
        var file = path.join(TEST_DIR, 'file.txt')
        fs.writeFileSync(file, 'blah')

        assert(fs.existsSync(file))
        fse.ensureFileSync(file)
        assert(fs.existsSync(file))
      })
    })

    describe('> when file does not exist', function() {
      it('should create the file', function() {
        var file = path.join(TEST_DIR, 'dir/that/does/not/exist', 'file.txt')

        assert(!fs.existsSync(file))
        fse.ensureFileSync(file)
        assert(fs.existsSync(file))
      })
    })
  })

  describe('+ ensureDir()', function() {
    describe('> when dir exists', function() {
      it('should not do anything', function(done) {
        var dir = path.join(TEST_DIR, 'dir/does/not/exist')
        fse.mkdirpSync(dir)

        assert(fs.existsSync(dir))
        fse.ensureDir(dir, function(err) {
          assert.ifError(err)
          assert(fs.existsSync(dir))
          done()
        })
      })
    })

    describe('> when dir does not exist', function() {
      it('should create the dir', function(done) {
        var dir = path.join(TEST_DIR, 'dir/that/does/not/exist')

        assert(!fs.existsSync(dir))
        fse.ensureDir(dir, function(err) {
          assert.ifError(err)
          assert(fs.existsSync(dir))
          done()
        })
      })
    })
  })

  describe('+ ensureDirSync()', function() {
    describe('> when dir exists', function() {
      it('should not do anything', function() {
        var dir = path.join(TEST_DIR, 'dir/does/not/exist')
        fse.mkdirpSync(dir)

        assert(fs.existsSync(dir))
        fse.ensureDirSync(dir)
        assert(fs.existsSync(dir))
      })
    })

    describe('> when dir does not exist', function() {
      it('should create the dir', function() {
        var dir = path.join(TEST_DIR, 'dir/that/does/not/exist')

        assert(!fs.existsSync(dir))
        fse.ensureDirSync(dir)
        assert(fs.existsSync(dir))
      })
    })
  })
})

<commit_msg>JavaScript Standard Style<commit_after>var assert = require('assert')
var fs = require('fs')
var path = require('path')
var testutil = require('testutil')
var fse = require('../')

/* global afterEach, beforeEach, describe, it */

var TEST_DIR = ''

describe('fs-extra', function () {
  beforeEach(function () {
    TEST_DIR = testutil.createTestDir('fs-extra')
  })

  afterEach(function (done) {
    fse.remove(TEST_DIR, done)
  })

  describe('+ ensureFile()', function () {
    describe('> when file exists', function () {
      it('should not do anything', function (done) {
        var file = path.join(TEST_DIR, 'file.txt')
        fs.writeFileSync(file, 'blah')

        assert(fs.existsSync(file))
        fse.ensureFile(file, function (err) {
          assert.ifError(err)
          assert(fs.existsSync(file))
          done()
        })
      })
    })

    describe('> when file does not exist', function () {
      it('should create the file', function (done) {
        var file = path.join(TEST_DIR, 'dir/that/does/not/exist', 'file.txt')

        assert(!fs.existsSync(file))
        fse.ensureFile(file, function (err) {
          assert.ifError(err)
          assert(fs.existsSync(file))
          done()
        })
      })
    })
  })

  describe('+ ensureFileSync()', function () {
    describe('> when file exists', function () {
      it('should not do anything', function () {
        var file = path.join(TEST_DIR, 'file.txt')
        fs.writeFileSync(file, 'blah')

        assert(fs.existsSync(file))
        fse.ensureFileSync(file)
        assert(fs.existsSync(file))
      })
    })

    describe('> when file does not exist', function () {
      it('should create the file', function () {
        var file = path.join(TEST_DIR, 'dir/that/does/not/exist', 'file.txt')

        assert(!fs.existsSync(file))
        fse.ensureFileSync(file)
        assert(fs.existsSync(file))
      })
    })
  })

  describe('+ ensureDir()', function () {
    describe('> when dir exists', function () {
      it('should not do anything', function (done) {
        var dir = path.join(TEST_DIR, 'dir/does/not/exist')
        fse.mkdirpSync(dir)

        assert(fs.existsSync(dir))
        fse.ensureDir(dir, function (err) {
          assert.ifError(err)
          assert(fs.existsSync(dir))
          done()
        })
      })
    })

    describe('> when dir does not exist', function () {
      it('should create the dir', function (done) {
        var dir = path.join(TEST_DIR, 'dir/that/does/not/exist')

        assert(!fs.existsSync(dir))
        fse.ensureDir(dir, function (err) {
          assert.ifError(err)
          assert(fs.existsSync(dir))
          done()
        })
      })
    })
  })

  describe('+ ensureDirSync()', function () {
    describe('> when dir exists', function () {
      it('should not do anything', function () {
        var dir = path.join(TEST_DIR, 'dir/does/not/exist')
        fse.mkdirpSync(dir)

        assert(fs.existsSync(dir))
        fse.ensureDirSync(dir)
        assert(fs.existsSync(dir))
      })
    })

    describe('> when dir does not exist', function () {
      it('should create the dir', function () {
        var dir = path.join(TEST_DIR, 'dir/that/does/not/exist')

        assert(!fs.existsSync(dir))
        fse.ensureDirSync(dir)
        assert(fs.existsSync(dir))
      })
    })
  })
})
<|endoftext|>


<commit_before>/**
 * JS Linting
 */

let colors = require('mocha/lib/reporters/base').colors;
colors['pass'] = '1;36';
colors['error stack'] = '1;36';
colors['fast'] = '1;36';
colors['light'] = '1;36';
colors['diff gutter'] = '1;36';

require('mocha-eslint')([
  'config',
  'models',
  'routes',
  'test',
  'app/app.js'
]);
<commit_msg>Add common folder to tests<commit_after>/**
 * JS Linting
 */

let colors = require('mocha/lib/reporters/base').colors;
colors['pass'] = '1;36';
colors['error stack'] = '1;36';
colors['fast'] = '1;36';
colors['light'] = '1;36';
colors['diff gutter'] = '1;36';

require('mocha-eslint')([
  'common',
  'config',
  'models',
  'routes',
  'test',
  'app/app.js'
]);
<|endoftext|>