<commit_before>const webpack = require('webpack');
const path = require('path');
const glob = require('glob');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const config = require('./config');

const inProduction = process.env.NODE_ENV === 'production';
const styleHash = inProduction ? 'contenthash' : 'hash';
const scriptHash = inProduction ? 'chunkhash' : 'hash';

module.exports = {
  entry: {
    app: glob.sync('./resources/assets/+(s[ac]ss|js)/main.+(s[ac]ss|js)'),
    login: glob.sync('./resources/assets/+(s[ac]ss|js)/login.+(s[ac]ss|js)'),
    vendor: ['jquery', 'vue'],
  },
  output: {
    path: path.resolve(__dirname, '../static/'),
    filename: `js/[name].[${scriptHash}].js`,
  },

  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            // Since sass-loader (weirdly) has SCSS as its default parse mode, we map
            // the "scss" and "sass" values for the lang attribute to the right configs here.
            // other preprocessors should work out of the box, no loader config like this necessary.
            scss: ['vue-style-loader', 'css-loader', 'sass-loader'],
            sass: [
              'vue-style-loader',
              'css-loader',
              'sass-loader?indentedSyntax',
            ],
          },
          // other vue-loader options go here
        },
      },
      {
        test: /\.s[ac]ss$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: [
            {
              loader: 'css-loader',
              options: { minimize: inProduction },
            },
            {
              loader: 'sass-loader',
              options: { sourceMap: true },
            },
          ],
        }),
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
      {
        test: /\.(png|jpe?g|gif|svg)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              outputPath: 'images/',
              publicPath: `${config.assetsPath}static/`,
            },
          },
          'image-webpack-loader',
        ],
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: '[name].[hash:7].[ext]',
          outputPath: 'fonts/',
          publicPath: `${config.assetsPath}static/`,
        },
      },
    ],
  },

  resolve: {
    alias: {
      vue$: 'vue/dist/vue.esm.js',
      images: path.join(__dirname, '../resources/assets/images'),
    },
    extensions: ['*', '.js', '.vue', '.json'],
  },

  plugins: [
    new ExtractTextPlugin(`css/[name].[${styleHash}].css`),

    new BrowserSyncPlugin({
      host: 'localhost',
      port: 3000,
      proxy: config.devUrl, // YOUR DEV-SERVER URL
      files: ['./*.php', './resources/views/**/*.twig', './static/*.*'],
    }),

    new CleanWebpackPlugin(['static/css/*', 'static/js/*', 'static/fonts/*'], {
      watch: true,
      root: path.resolve(__dirname, '../'),
    }),

    new ManifestPlugin(),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      filename: `js/[name].[${scriptHash}].js`,
    }),
  ],
};

if (inProduction) {
  module.exports.plugins.push(new webpack.optimize.UglifyJsPlugin());

  module.exports.plugins.push(new webpack.DefinePlugin({
    'process.env': {
      NODE_ENV: '"production"',
    },
  }));
}
<commit_msg>updated webpack config again<commit_after>const webpack = require('webpack');
const path = require('path');
const glob = require('glob');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const config = require('./config');

const inProduction = process.env.NODE_ENV === 'production';
const styleHash = inProduction ? 'contenthash' : 'hash';
const scriptHash = inProduction ? 'chunkhash' : 'hash';

module.exports = {
  entry: {
    app: glob.sync('./resources/assets/+(s[ac]ss|js)/main.+(s[ac]ss|js)'),
    login: glob.sync('./resources/assets/+(s[ac]ss|js)/login.+(s[ac]ss|js)'),
    vendor: ['jquery', 'vue'],
  },
  output: {
    path: path.resolve(__dirname, '../static/'),
    filename: `js/[name].[${scriptHash}].js`,
  },

  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            // Since sass-loader (weirdly) has SCSS as its default parse mode, we map
            // the "scss" and "sass" values for the lang attribute to the right configs here.
            // other preprocessors should work out of the box, no loader config like this necessary.
            scss: ['vue-style-loader', 'css-loader', 'sass-loader'],
            sass: [
              'vue-style-loader',
              'css-loader',
              'sass-loader?indentedSyntax',
            ],
          },
          // other vue-loader options go here
        },
      },
      {
        test: /\.s[ac]ss$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: [
            {
              loader: 'css-loader',
              options: { minimize: inProduction },
            },
            {
              loader: 'sass-loader',
              options: { sourceMap: true },
            },
          ],
        }),
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
      {
        test: /\.(png|jpe?g|gif|svg)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              outputPath: 'images/',
              publicPath: `${config.assetsPath}static/`,
            },
          },
          'image-webpack-loader',
        ],
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: '[name].[hash:7].[ext]',
          outputPath: 'fonts/',
          publicPath: `${config.assetsPath}static/`,
        },
      },
    ],
  },

  resolve: {
    alias: {
      vue$: 'vue/dist/vue.esm.js',
      images: path.join(__dirname, '../resources/assets/images'),
    },
    extensions: ['*', '.js', '.vue', '.json'],
  },

  plugins: [
    new ExtractTextPlugin(`css/[name].[${styleHash}].css`),

    new BrowserSyncPlugin({
      host: 'localhost',
      port: 3000,
      proxy: config.devUrl, // YOUR DEV-SERVER URL
      files: ['./*.php', './resources/views/**/*.twig', './static/*.*'],
    }),

    new CleanWebpackPlugin(['static/css/*', 'static/js/*'], {
      watch: true,
      root: path.resolve(__dirname, '../'),
    }),

    new ManifestPlugin(),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      filename: `js/[name].[${scriptHash}].js`,
    }),
  ],
};

if (inProduction) {
  module.exports.plugins.push(new webpack.optimize.UglifyJsPlugin());

  module.exports.plugins.push(new webpack.DefinePlugin({
    'process.env': {
      NODE_ENV: '"production"',
    },
  }));
}
<|endoftext|>


<commit_before>define(
    [ "preemptive-task" ],
    function( PreemptiveTask ) {
      return function() {

        module( "PreemptiveTask", {
          setup: function() {},
          teardown: function() {}
        });

        var schedulerApi = {
            insert: function() {},
            remove: function() {}
        };

        var invalidSchedulerApi = {
            foo: function() {},
            bar: function() {}
        };

        test( "start a task", function() {
          expect( 1 );

          var schedulerMock = sinon.mock( schedulerApi );
          schedulerMock.expects( "insert" ).once();

          function taskFunction() {};
          var task = new PreemptiveTask( schedulerApi, taskFunction );

          task.start();

          ok( schedulerMock.verify(), "scheduler invocations verified" );
        });

        test( "create a task with invalid scheduler", function() {
          expect( 1 );

          var invalidSchedulerMock = sinon.mock( invalidSchedulerApi );

          function taskFunction() {};

          raises( function() {
            var task = new PreemptiveTask( invalidSchedulerApi, taskFunction );
          }, Error, "exception thrown for invalid scheduler" );
        });

        test( "start a task that's already started", function() {
          expect( 1 );

          var schedulerMock = sinon.mock( schedulerApi );
          function taskFunction() {};
          var task = new PreemptiveTask( schedulerApi, taskFunction );

          task.start();
          raises( function() {
            task.start();
          }, Error, "exception thrown for task already started" );
        });

        test( "task run context is correct", function() {
          expect( 7 );

          var schedulerMock = sinon.mock( schedulerApi );
          function taskFunction() {
            equal( this, task, "this value is correct" );
            ok( this.isRunning, "task is running" );
          };
          var task = new PreemptiveTask( schedulerApi, taskFunction );

          ok( !task.isRunning(), "task is not running" );
          ok( !task.isStarted(), "task is not started" );
          task.start();
          ok( task.isStarted(), "task is started" );
          task.run(); // Bypass the scheduler
          ok( !task.isRunning(), "task is not running" );
          ok( task.isStarted(), "task is started" );
        });

        test( "pause a running task", function() {
          expect( 1 );

          var schedulerMock = sinon.mock( schedulerApi );
          function taskFunction() {
            this.pause();
          };
          var task = new PreemptiveTask( schedulerApi, taskFunction );

          task.start();
          task.run(); // Bypass the scheduler

          ok( task.result instanceof Error,
              "exception thrown for task not in blocked state" );
        });

        test( "cancel a running task", function() {
          expect( 1 );

          var schedulerMock = sinon.mock( schedulerApi );
          function taskFunction() {
            task.cancel();
          };
          var task = new PreemptiveTask( schedulerApi, taskFunction );

          task.start();
          task.run(); // Bypass the scheduler

          ok( task.result instanceof Error,
          "exception thrown for task not in blocked state" );
        });

      };
    }
);<commit_msg>Refactored test setup code.<commit_after>define(
    [ "preemptive-task" ],
    function( PreemptiveTask ) {
      return function() {

        module( "PreemptiveTask", {
          setup: function() {
            this.schedulerApi = {
                insert: function() {},
                remove: function() {}
            };

            this.invalidSchedulerApi = {
                foo: function() {},
                bar: function() {}
            };

            this.schedulerMock = sinon.mock( this.schedulerApi );
            this.invalidSchedulerMock = sinon.mock( this.invalidSchedulerApi );
          },
          teardown: function() {
            this.schedulerApi = undefined;
            this.schedulerMock = undefined;
          }
        });

        test( "start a task", function() {
          expect( 1 );

          var schedulerApi = this.schedulerApi;
          var schedulerMock = this.schedulerMock;
          schedulerMock.expects( "insert" ).once();

          function taskFunction() {};
          var task = new PreemptiveTask( schedulerApi, taskFunction );

          task.start();

          ok( schedulerMock.verify(), "scheduler invocations verified" );
        });

        test( "create a task with invalid scheduler", function() {
          expect( 1 );

          var invalidSchedulerApi = this.invalidSchedulerApi;
          var invalidSchedulerMock = this.invalidSchedulerMock;

          function taskFunction() {};

          raises( function() {
            var task = new PreemptiveTask( invalidSchedulerApi, taskFunction );
          }, Error, "exception thrown for invalid scheduler" );
        });

        test( "start a task that's already started", function() {
          expect( 1 );

          var schedulerApi = this.schedulerApi;
          var schedulerMock = this.schedulerMock;
          function taskFunction() {};
          var task = new PreemptiveTask( schedulerApi, taskFunction );

          task.start();
          raises( function() {
            task.start();
          }, Error, "exception thrown for task already started" );
        });

        test( "task run context is correct", function() {
          expect( 7 );

          var schedulerApi = this.schedulerApi;
          var schedulerMock = this.schedulerMock;
          function taskFunction() {
            equal( this, task, "this value is correct" );
            ok( this.isRunning, "task is running" );
          };
          var task = new PreemptiveTask( schedulerApi, taskFunction );

          ok( !task.isRunning(), "task is not running" );
          ok( !task.isStarted(), "task is not started" );
          task.start();
          ok( task.isStarted(), "task is started" );
          task.run(); // Bypass the scheduler
          ok( !task.isRunning(), "task is not running" );
          ok( task.isStarted(), "task is started" );
        });

        test( "pause a running task", function() {
          expect( 1 );

          var schedulerApi = this.schedulerApi;
          var schedulerMock = this.schedulerMock;
          function taskFunction() {
            this.pause();
          };
          var task = new PreemptiveTask( schedulerApi, taskFunction );

          task.start();
          task.run(); // Bypass the scheduler

          ok( task.result instanceof Error,
              "exception thrown for task not in blocked state" );
        });

        test( "cancel a running task", function() {
          expect( 1 );

          var schedulerApi = this.schedulerApi;
          var schedulerMock = this.schedulerMock;
          function taskFunction() {
            task.cancel();
          };
          var task = new PreemptiveTask( schedulerApi, taskFunction );

          task.start();
          task.run(); // Bypass the scheduler

          ok( task.result instanceof Error,
          "exception thrown for task not in blocked state" );
        });

      };
    }
);<|endoftext|>


<commit_before>// stuff
define('ghostwriter', ['jquery',
   'scribe',
   'scribe-plugin-blockquote-command',
   'scribe-plugin-curly-quotes',
   'scribe-plugin-formatter-plain-text-convert-new-lines-to-html',
   'scribe-plugin-heading-command',
   'scribe-plugin-intelligent-unlink-command',
   'scribe-plugin-link-prompt-command',
   'scribe-plugin-sanitizer',
   'scribe-plugin-smart-lists',
   'scribe-plugin-toolbar',
   'beautify-html'],

function($,
  Scribe,
  scribePluginBlockquoteCommand,
  scribePluginCurlyQuotes,
  scribePluginFormatterPlainTextConvertNewLinesToHtml,
  scribePluginHeadingCommand,
  scribePluginIntelligentUnlinkCommand,
  scribePluginLinkPromptCommand,
  scribePluginSanitizer,
  scribePluginSmartLists,
  scribePluginToolbar,
  html_beautify) {

  'use strict';

  var writerHTML = '
' +
      '
' +
        '
' +
          '
<button data-command-name="bold"></button>
' +
          '
<button data-command-name="italic"></button>
' +
          '
<button data-command-name="strikeThrough"></button>
' +
          '
<button data-command-name="removeFormat"></button>
' +
          '
<button data-command-name="linkPrompt"></button>
' +
          '
<button data-command-name="insertOrderedList"></button>
' +
          '
<button data-command-name="insertUnorderedList"></button>
' +
          '
<button data-command-name="indent"></button>
' +
          '
<button data-command-name="outdent"></button>
' +
          '
<button data-command-name="blockquote"></button>
' +
          '
<button data-command-name="h2"></button>
' +
          '
<button data-command-name="undo"></button>
' +
        '
' +
      '
' +
      '

' +
    '
';

  // Load HTML beautify for demo to make html pretty
  var htmlBeautify = html_beautify.html_beautify;
  /**
   * Load Scribe(s)
   */

  $('.ghost-writer-editable').each(function (i, el) {
    // add scribe class so we know scribe is on that element
    $(el).addClass('scribe');

    // add our custom toolbar html
    $('body').append(writerHTML);

    // assign toolbars unique id's for scribe
    $('.toolbar')[i].id = 'scribe-toolbar-' + i;

    // add attribute so we can figure out which toolbar belongs to which instance
    $('.ghost-writer-editable')[i].setAttribute('data-gw-instance', i);

    var scribe = new Scribe(el, { allowBlockElements: true });

    // Demo only
    scribe.on('content-changed', updateHTML);

    function updateHTML() {
      var htmlOut = $('.scribe-html code');
      htmlOut.empty();
      htmlOut.text(htmlBeautify(scribe.getHTML()));
      hljs.configure({ tabReplace: '  ' });
      hljs.highlightBlock(htmlOut[0]);
    }

   /**
    * Scribe Plugins
    */

    scribe.use(scribePluginBlockquoteCommand());
    scribe.use(scribePluginHeadingCommand(2));
    scribe.use(scribePluginIntelligentUnlinkCommand());
    scribe.use(scribePluginLinkPromptCommand());
    scribe.use(scribePluginToolbar(document.getElementById('scribe-toolbar-' + i)));
    scribe.use(scribePluginSmartLists());
    scribe.use(scribePluginCurlyQuotes());

    // Formatters
    scribe.use(scribePluginSanitizer({
      tags: {
        p: {},
        br: {},
        b: {},
        strong: {},
        i: {},
        strike: {},
        blockquote: {},
        ol: {},
        ul: {},
        li: {},
        a: { href: true },
        h2: {}
      }
    }));

    scribe.use(scribePluginFormatterPlainTextConvertNewLinesToHtml());

    if (scribe.allowsBlockElements()) {
      scribe.setContent('
Hello, World!
' +
        '

“Easy reading is damned hard writing.” — Nathaniel Hawthorne

GhostWriter is a small JS application that lets you edit content directly in your browser. The main focus is quick, easy, and hassle free composing without writing HTML. GhostWriter is a wrapper on top of The Guardian’s Scribe content editable framework. Scribe is a plugin based framework which standardized content editable elements across browsers.

Editing is a snap.

Click the ghost icon and click the edit icon.

Click on any editable sections and start writing.

In sit amet varius ipsum. Sed sit amet neque vitae eros elementum gravida non a arcu. Proin vel ligula magna. Aenean vestibulum dui nec suscipit molestie. Vestibulum a ante rutrum, volutpat mi vestibulum, scelerisque quam. Sed facilisis sed lectus eget consequat. Duis dignissim libero in suscipit pretium. Pellentesque laoreet sapien mauris, nec suscipit nunc bibendum in. Proin sem dui, mattis eget rhoncus quis, consequat sed arcu. Morbi interdum pharetra lorem id dictum.

GhostWriter was used in the making of this post.

');  }
    else {
      scribe.setContent('Hello, World!');
    }
  });

  /**
   * GhostWriter Events
   * Events mousdown, mouseup, keyup
   */
  function calculateBounds(e) {
    var gwToolbar = $('.ghost-writer').eq($(e.delegateTarget).attr('data-gw-instance')),
        top, left;

    // Handle bounds - x
    if ( e.pageX - (gwToolbar.width() / 2) < 0 ) {
      left = 0;
    }
    else if ( e.pageX + (gwToolbar.width() / 2) > $(window).width() - 25 ) {
      left = $(window).width() - (gwToolbar.width() + 12);
    }
    else {
      left = e.pageX - (gwToolbar.width() / 2);
    }

    // Handle bounds - y
    if ( e.pageY - 50 < 0 ) {
      top = 0;
    }
    else {
      top = e.pageY - 70;
    }

    return {
      top: top,
      left: left + 3 // hack
    };
  }

  function handleToolbarPosition(e) {
    var gwToolbar = $('.ghost-writer').eq($(e.delegateTarget).attr('data-gw-instance')),
        target = e.target || e.srcElement;

    if (!target.isContentEditable)
      return;

    if (window.getSelection().isCollapsed) {
      gwToolbar.addClass('hidden');
    } else {
      var limit = calculateBounds(e);

      gwToolbar.css('top', limit.top);
      gwToolbar.css('left', limit.left);
      gwToolbar.removeClass('hidden');

    }
  }

  function goAway(e) {
    $('.ghost-writer').each(function (i, el) {
      if (!$(el).hasClass('hidden')) {
        $(el).addClass('hidden');
      }
    });

  }

  $('.scribe').each(function (i, el) {
    var instance = $(el).attr('data-gw-instace');
    $(el).on({
      mousedown: goAway,
      dblclick: handleToolbarPosition,
      // use a timer on mouseup so the browser has time to deselect text
      mouseup: function (e) { setTimeout(function () { handleToolbarPosition(e); }, 100); },
      keyup: handleToolbarPosition
    });

    //$(el).attr('contenteditable', false);
  });
/*
  function enableEditing() {
    $('.ghost-writer-editable').attr('contenteditable', true);
    $el.attr('data-gw-action-name', 'save');
    $el.html('Save ');
    $('.gw-tab-menu').addClass('save');
  }

  function disableEditing() {
    $('.ghost-writer-editable').attr('contenteditable', false);
    $el.attr('data-gw-action-name', 'edit');
    $el.html('Edit ');
    $('.gw-tab-menu').removeClass('save');
  }

  $('.gw-tab-menu ul li button').each(function (i, el) {
    $(el).on({
      click: function () {
        var $el = $(el),
          action = $el.attr('data-gw-action-name');

        switch (action) {
          case 'edit':
            enableEditing();
          break;
          case 'save':
            disableEditing();

            // do ajaxy save stuff here
          break;
          default:
          break;
        }
      }
    });
  });

  $('.tryit').on('click', function() {

  })*/
});
<commit_msg>Removed ugly comments<commit_after>// stuff
define('ghostwriter', ['jquery',
   'scribe',
   'scribe-plugin-blockquote-command',
   'scribe-plugin-curly-quotes',
   'scribe-plugin-formatter-plain-text-convert-new-lines-to-html',
   'scribe-plugin-heading-command',
   'scribe-plugin-intelligent-unlink-command',
   'scribe-plugin-link-prompt-command',
   'scribe-plugin-sanitizer',
   'scribe-plugin-smart-lists',
   'scribe-plugin-toolbar',
   'beautify-html'],

function($,
  Scribe,
  scribePluginBlockquoteCommand,
  scribePluginCurlyQuotes,
  scribePluginFormatterPlainTextConvertNewLinesToHtml,
  scribePluginHeadingCommand,
  scribePluginIntelligentUnlinkCommand,
  scribePluginLinkPromptCommand,
  scribePluginSanitizer,
  scribePluginSmartLists,
  scribePluginToolbar,
  html_beautify) {

  'use strict';

  var writerHTML = '
' +
      '
' +
        '
' +
          '
<button data-command-name="bold"></button>
' +
          '
<button data-command-name="italic"></button>
' +
          '
<button data-command-name="strikeThrough"></button>
' +
          '
<button data-command-name="removeFormat"></button>
' +
          '
<button data-command-name="linkPrompt"></button>
' +
          '
<button data-command-name="insertOrderedList"></button>
' +
          '
<button data-command-name="insertUnorderedList"></button>
' +
          '
<button data-command-name="indent"></button>
' +
          '
<button data-command-name="outdent"></button>
' +
          '
<button data-command-name="blockquote"></button>
' +
          '
<button data-command-name="h2"></button>
' +
          '
<button data-command-name="undo"></button>
' +
        '
' +
      '
' +
      '

' +
    '
';

  // Load HTML beautify for demo to make html pretty
  var htmlBeautify = html_beautify.html_beautify;
  /**
   * Load Scribe(s)
   */

  $('.ghost-writer-editable').each(function (i, el) {
    // add scribe class so we know scribe is on that element
    $(el).addClass('scribe');

    // add our custom toolbar html
    $('body').append(writerHTML);

    // assign toolbars unique id's for scribe
    $('.toolbar')[i].id = 'scribe-toolbar-' + i;

    // add attribute so we can figure out which toolbar belongs to which instance
    $('.ghost-writer-editable')[i].setAttribute('data-gw-instance', i);

    var scribe = new Scribe(el, { allowBlockElements: true });

    // Demo only
    scribe.on('content-changed', updateHTML);

    function updateHTML() {
      var htmlOut = $('.scribe-html code');
      htmlOut.empty();
      htmlOut.text(htmlBeautify(scribe.getHTML()));
      hljs.configure({ tabReplace: '  ' });
      hljs.highlightBlock(htmlOut[0]);
    }

   /**
    * Scribe Plugins
    */

    scribe.use(scribePluginBlockquoteCommand());
    scribe.use(scribePluginHeadingCommand(2));
    scribe.use(scribePluginIntelligentUnlinkCommand());
    scribe.use(scribePluginLinkPromptCommand());
    scribe.use(scribePluginToolbar(document.getElementById('scribe-toolbar-' + i)));
    scribe.use(scribePluginSmartLists());
    scribe.use(scribePluginCurlyQuotes());

    // Formatters
    scribe.use(scribePluginSanitizer({
      tags: {
        p: {},
        br: {},
        b: {},
        strong: {},
        i: {},
        strike: {},
        blockquote: {},
        ol: {},
        ul: {},
        li: {},
        a: { href: true },
        h2: {}
      }
    }));

    scribe.use(scribePluginFormatterPlainTextConvertNewLinesToHtml());

    if (scribe.allowsBlockElements()) {
      scribe.setContent('
Hello, World!
' +
        '

“Easy reading is damned hard writing.” — Nathaniel Hawthorne

GhostWriter is a small JS application that lets you edit content directly in your browser. The main focus is quick, easy, and hassle free composing without writing HTML. GhostWriter is a wrapper on top of The Guardian’s Scribe content editable framework. Scribe is a plugin based framework which standardized content editable elements across browsers.

Editing is a snap.

Click the ghost icon and click the edit icon.

Click on any editable sections and start writing.

In sit amet varius ipsum. Sed sit amet neque vitae eros elementum gravida non a arcu. Proin vel ligula magna. Aenean vestibulum dui nec suscipit molestie. Vestibulum a ante rutrum, volutpat mi vestibulum, scelerisque quam. Sed facilisis sed lectus eget consequat. Duis dignissim libero in suscipit pretium. Pellentesque laoreet sapien mauris, nec suscipit nunc bibendum in. Proin sem dui, mattis eget rhoncus quis, consequat sed arcu. Morbi interdum pharetra lorem id dictum.

GhostWriter was used in the making of this post.

');  }
    else {
      scribe.setContent('Hello, World!');
    }
  });

  /**
   * GhostWriter Events
   * Events mousdown, mouseup, keyup
   */
  function calculateBounds(e) {
    var gwToolbar = $('.ghost-writer').eq($(e.delegateTarget).attr('data-gw-instance')),
        top, left;

    // Handle bounds - x
    if ( e.pageX - (gwToolbar.width() / 2) < 0 ) {
      left = 0;
    }
    else if ( e.pageX + (gwToolbar.width() / 2) > $(window).width() - 25 ) {
      left = $(window).width() - (gwToolbar.width() + 12);
    }
    else {
      left = e.pageX - (gwToolbar.width() / 2);
    }

    // Handle bounds - y
    if ( e.pageY - 50 < 0 ) {
      top = 0;
    }
    else {
      top = e.pageY - 70;
    }

    return {
      top: top,
      left: left + 3 // hack
    };
  }

  function handleToolbarPosition(e) {
    var gwToolbar = $('.ghost-writer').eq($(e.delegateTarget).attr('data-gw-instance')),
        target = e.target || e.srcElement;

    if (!target.isContentEditable)
      return;

    if (window.getSelection().isCollapsed) {
      gwToolbar.addClass('hidden');
    } else {
      var limit = calculateBounds(e);

      gwToolbar.css('top', limit.top);
      gwToolbar.css('left', limit.left);
      gwToolbar.removeClass('hidden');

    }
  }

  function goAway(e) {
    $('.ghost-writer').each(function (i, el) {
      if (!$(el).hasClass('hidden')) {
        $(el).addClass('hidden');
      }
    });

  }

  $('.scribe').each(function (i, el) {
    var instance = $(el).attr('data-gw-instace');
    $(el).on({
      mousedown: goAway,
      dblclick: handleToolbarPosition,
      // use a timer on mouseup so the browser has time to deselect text
      mouseup: function (e) { setTimeout(function () { handleToolbarPosition(e); }, 100); },
      keyup: handleToolbarPosition
    });
  });

});
<|endoftext|>


<commit_before>jest.dontMock('../index');
jest.dontMock('../hooks');

jest.setMock('react-router', {
  HashLocation: {
    getCurrentPath() { return '/foo'; },
    addChangeListener() {}
  }
});

const PluginTestUtils = require('PluginTestUtils');

let SDK = PluginTestUtils.getSDK('tracking', {enabled: true});
require('../SDK').setSDK(SDK);

const DOMUtils = SDK.get('DOMUtils');
const TrackingHooks = require('../hooks');

describe('TrackingHooks', function () {

  describe('Listeners', function () {

    beforeEach(function () {
      DOMUtils.appendScript = jasmine.createSpy();
    });

    describe('#pluginsConfigured', function () {

      it('appends scripts to the document head if plugin enabled', function () {
        global.analytics = {ready: function () {}};
        TrackingHooks.initialize();
        SDK.Hooks.doAction('pluginsConfigured');
        expect(DOMUtils.appendScript.calls.count()).toEqual(1);
      });

    });

  });

});
<commit_msg>Fix `TrackingPlugin-test` linting error<commit_after>jest.dontMock('../index');
jest.dontMock('../hooks');

jest.setMock('react-router', {
  HashLocation: {
    getCurrentPath() { return '/foo'; },
    addChangeListener() {}
  }
});

const PluginTestUtils = require('PluginTestUtils');

let SDK = PluginTestUtils.getSDK('tracking', {enabled: true});
require('../SDK').setSDK(SDK);

const DOMUtils = SDK.get('DOMUtils');
const TrackingHooks = require('../hooks');

describe('TrackingHooks', function () {

  describe('Listeners', function () {

    beforeEach(function () {
      DOMUtils.appendScript = jasmine.createSpy();
    });

    describe('#pluginsConfigured', function () {

      it('appends scripts to the document head if plugin enabled', function () {
        global.analytics = {ready() {}};
        TrackingHooks.initialize();
        SDK.Hooks.doAction('pluginsConfigured');
        expect(DOMUtils.appendScript.calls.count()).toEqual(1);
      });

    });

  });

});
<|endoftext|>


<commit_before>var Swagger = require('./swagger'),
    Importer = require('./importer'),
    UtilityFunction = require('../entities/utilityFunction'),
    Text = require('../entities/text'),
    fs = require('fs');

function StopLightX() {
  this.metadata = null;
  this.swaggerImporter = new Swagger();
}
StopLightX.prototype = new Importer();

StopLightX.prototype.loadFile = function(path, cb){
  var me = this;
  return this.swaggerImporter.loadFile(path, function(){
    me.data = me.swaggerImporter.data;
    cb();
  });
};

StopLightX.prototype.loadData = function(path, cb){
  var me = this;
  return this.swaggerImporter.loadData(path, function(err){
    if (err) {
      return cb(err);
    }
    me.data = me.swaggerImporter.data;
    cb();
  });
};

StopLightX.prototype._import = function () {

  try {
    this.project = this.swaggerImporter.import();

    var data = this.swaggerImporter.data;
    var prefix = 'x-stoplight';
    if (data.hasOwnProperty(prefix)) {
      var environment = this.project.Environment;
      environment.loadSLData(data[prefix].version);

      //property names are different from db name
      environment.GroupsOrder = data[prefix].version.groups;
      environment.MiddlewareBefore = data[prefix].version.beforeScript;
      environment.MiddlewareAfter = data[prefix].version.afterScript;

      this.project.Environment = environment;

      for (var name in data[prefix].functions) {
        var ufData = data[prefix].functions[name];
        var uf = new UtilityFunction(ufData.name);
        uf.Description = ufData.description;
        uf.Script = ufData.script;
        this.project.addUtilityFunction(uf);
      }

      for (var name in data[prefix].textSections) {
        var txtData = data[prefix].textSections[name];
        var txt = new Text(txtData.name);
        txt.Id = txtData.id;
        txt.Content = txtData.content;
        txt.Public = txtData.public;
        this.project.addText(txt);
      }

      for(var i in this.project.Endpoints) {
        var endpoint = this.project.Endpoints[i];
        var method = data.paths[endpoint.Path][endpoint.Method][prefix];
        endpoint.Before = method['beforeScript'];
        endpoint.After = method['afterScript'];
        endpoint.Mock = method['mock'];
        endpoint.Id = method['id'];
      }

      for(var i in this.project.Schemas) {
        var schema = this.project.Schemas[i];
        var schemaData = data.definitions[schema.Name][prefix];
        schema.Id = schemaData.id;
        schema.Name = schemaData.name;
        schema.Summary = schemaData.summary;
        schema.Description = schemaData.description;
        schema.Public = schemaData.public;
      }
    }
  } catch(err) {
    console.log(err);
  }
};

module.exports = StopLightX;
<commit_msg>- additional checking for stoplight extensions data.<commit_after>var Swagger = require('./swagger'),
    Importer = require('./importer'),
    UtilityFunction = require('../entities/utilityFunction'),
    Text = require('../entities/text'),
    fs = require('fs');

function StopLightX() {
  this.metadata = null;
  this.swaggerImporter = new Swagger();
}
StopLightX.prototype = new Importer();

StopLightX.prototype.loadFile = function(path, cb){
  var me = this;
  return this.swaggerImporter.loadFile(path, function(){
    me.data = me.swaggerImporter.data;
    cb();
  });
};

StopLightX.prototype.loadData = function(path, cb){
  var me = this;
  return this.swaggerImporter.loadData(path, function(err){
    if (err) {
      return cb(err);
    }
    me.data = me.swaggerImporter.data;
    cb();
  });
};

StopLightX.prototype._import = function () {

    this.project = this.swaggerImporter.import();

    var data = this.swaggerImporter.data;
    var prefix = 'x-stoplight';
    if (!data.hasOwnProperty(prefix)) {
      return;
    }
    var environment = this.project.Environment;

    var stoplightData = data[prefix];

    if (stoplightData.hasOwnProperty('version')) {
      environment.loadSLData(data[prefix].version);
      //property names are different from db name
      environment.GroupsOrder = data[prefix].version.groups;
      environment.MiddlewareBefore = data[prefix].version.beforeScript;
      environment.MiddlewareAfter = data[prefix].version.afterScript;

      this.project.Environment = environment;
    }

    for (var name in data[prefix].functions) {
      var ufData = data[prefix].functions[name];
      var uf = new UtilityFunction(ufData.name);
      uf.Description = ufData.description;
      uf.Script = ufData.script;
      this.project.addUtilityFunction(uf);
    }

    for (var name in data[prefix].textSections) {
      var txtData = data[prefix].textSections[name];
      var txt = new Text(txtData.name);
      txt.Id = txtData.id;
      txt.Content = txtData.content;
      txt.Public = txtData.public;
      this.project.addText(txt);
    }

    for(var i in this.project.Endpoints) {
      var endpoint = this.project.Endpoints[i];
      var method = data.paths[endpoint.Path][endpoint.Method][prefix];
      if (method) {
        endpoint.Before = method['beforeScript'];
        endpoint.After = method['afterScript'];
        endpoint.Mock = method['mock'];
        endpoint.Id = method['id'];
      }
    }

    for(var i in this.project.Schemas) {
      var schema = this.project.Schemas[i];
      var schemaData = data.definitions[schema.NameSpace][prefix];
      if (schemaData) {
        schema.Id = schemaData.id;
        schema.Name = schemaData.name;
        schema.Summary = schemaData.summary;
        schema.Description = schemaData.description;
        schema.Public = schemaData.public;
      }
    }
};

module.exports = StopLightX;
<|endoftext|>


<commit_before>/**
 * Setup some defaults that can be overwritten
 */
let settings = {
  name: 'cookies_accepted',
  template: 'cookie-banner.html',
  message: 'Our website uses cookies to monitor traffic on our website and ensure that we can provide our customers with the best online experience possible. Please read our cookie policy to view more details on the cookies we use.'
};

let bodyClass = 'has-cookie-banner';

/**
 * hasClass() checks whether an element has a specified class.
 *
 * @memberOf  CookieDisclaimer
 * @name      CookieDisclaimer.hasClass
 * @access    public
 * @param     {Element} [el] – the element being targetted
 * @param     {String} [className] – the class name being toggled
 * @returns   {boolean}
 *
 * ```js
 *   if(hasClass(document.querySelector('.this'), 'class-name')) {
 *     console.log('true');
 *   }
 * ```
 */
export function hasClass (el, className) {

  return el.classList ? el.classList.contains(className) : new RegExp('\\b'+ className+'\\b').test(el.className);

}

/**
 * getFileContents() returns the content of a file
 *
 * @memberOf  PowerHouse
 * @name      Powerhouse.getFileContents
 * @access    public
 * @param     {Integer} [key] – the name of the item in the query string
 * @param     {Integer} [url] – the URL containing the query string
 * @returns   {Integer}
 *
 * ```js
 *   getFileContents('/file.html', function (response) {
 *     console.log(response);
 *   }, function (response)
 *     console.log('There was an error: ' + response);
 *   });
 * ```
 */
function getFileContents (file, success, error) {

  // Detect whether XMLHttpRequest is supported
  if (!window.XMLHttpRequest) {
    return;
  }

  // Create a new request
  var request = new XMLHttpRequest();

  // Setup callbacks
  request.onreadystatechange = function () {

    // If the request is completed
    if (this.readyState === 4) {

      // If the request failed
      if (request.status !== 200) {
        if (error && typeof(error) === 'function') {
          error(request.responseText, request);
        }
        return;
      }

      // If the request was successful
      if (success && typeof(success) === 'function') {
        success(request.responseText, request);
      }
    }
  };

  // Send the HTML
  request.open('GET', file, true);
  request.send();

}

/**
 * createCookie() is used to set a cookie with the specified name, value and
 * expiration values
 *
 * @memberOf  CookieDisclaimer
 * @name      createCookie
 * @access    private
 * @param     {string} [name] – the name of the cookie
 * @param     {string} [string] – the value of the cookie
 * @param     {number} [duration] – the duration of the cookie
 *
 * ```js
 *   createCookie('cookies_accepted', 'true', 1800);
 * ```
 */
function createCookie (name, value, duration) {

  let expires,
      date;

  // 1. If a duration is set
  if (duration) {

    // 1. Create a new date instance
    date = new Date();
    date.setTime(date.getTime()+(duration*24*60*60*1000));

    // 2. Update the expires value
    expires = '; expires=' + date.toGMTString();

  } else {

    expires = '';

  }

  // 2. Create the cookie
  document.cookie = name + '=' + value + expires + '; path=/';

}

/**
 * readCookie() is used to read a cookie
 *
 * @memberOf  CookieDisclaimer
 * @name      readCookie
 * @access    private
 * @param     {string} [name] – the name of the cookie to be read
 *
 * ```js
 *   readCookie('cookies_accepted');
 * ```
 */
function readCookie (name) {

  let cookieName = name + '=',
      ca = document.cookie.split(';');

  for (var i = 0; i < ca.length; i++) {
    var cookie = ca[i];

    while (cookie.charAt(0)== ' ') {
      cookie = cookie.substring(1, cookie.length);
    }

    if (cookie.indexOf(cookieName) === 0) {
      return cookie.substring(cookieName.length, cookie.length);
    }
  }

  return null;

}

/**
 * init() is used to setup the cookie disclaimer
 *
 * @memberOf  CookieDisclaimer
 * @name      init
 * @access    private
 * @param     {string} [name] – the name of the cookie to be read
 *
 * ```js
 *   CookreadCookie('cookies_accepted');
 * ```
 */
export function init (options) {

  // 1. Override any default settings with user specified ones
  for(var prop in options) {
    if(options.hasOwnProperty(prop)){
      settings[prop] = options[prop];
    }
  }

  // 2. If the cookie hasn't already been setup
  if(!readCookie(settings.name)) {

    // 1. Grab the contents of the HTML template
    getFileContents(settings.template, function (response) {

      // 1. Create the banner
      let el = document.createElement('div');
      el.setAttribute('class', 'cookie-banner');
      el.setAttribute('id', 'cookie-banner');
      el.setAttribute('aria-role', 'alert');
      el.setAttribute('aria-live', 'assertive');
      el.innerHTML += response;

      // 2. Prepend to the body
      document.body.insertBefore(el, document.body.firstChild);

      // 3. Add message
      document.getElementById('message').innerHTML = settings.message;

      // 4. Add class to body
      if (document.body.classList) document.body.classList.add(bodyClass);
      else if (!hasClass(document.body, bodyClass)) document.body.className += ' ' + bodyClass;

      // 5. Add an event listener to the close button
      document.getElementById('close').onclick = function () {

        // 1. On click create the cookie
        createCookie(settings.name, true, 1800);

        // 2. Remove the banner
        let banner = document.getElementById('cookie-banner');
        banner.parentNode.removeChild(el);

        // 3. Remove the class from the body
        if (document.body.classList) document.body.classList.remove(bodyClass);
        else document.body.className = document.body.className.replace(new RegExp('\\b'+ bodyClass+'\\b', 'g'), '');

        // 4. Prevent the default link action
        return false;
      };

    });

  }

}
<commit_msg>Set cookie vailidity time to 30 days & allow it to be configured<commit_after>/**
 * Setup some defaults that can be overwritten
 */
let settings = {
  name: 'cookies_accepted',
  template: 'cookie-banner.html',
  message: 'Our website uses cookies to monitor traffic on our website and ensure that we can provide our customers with the best online experience possible. Please read our cookie policy to view more details on the cookies we use.',
  duration: 30
};

let bodyClass = 'has-cookie-banner';

/**
 * hasClass() checks whether an element has a specified class.
 *
 * @memberOf  CookieDisclaimer
 * @name      CookieDisclaimer.hasClass
 * @access    public
 * @param     {Element} [el] – the element being targetted
 * @param     {String} [className] – the class name being toggled
 * @returns   {boolean}
 *
 * ```js
 *   if(hasClass(document.querySelector('.this'), 'class-name')) {
 *     console.log('true');
 *   }
 * ```
 */
export function hasClass (el, className) {

  return el.classList ? el.classList.contains(className) : new RegExp('\\b'+ className+'\\b').test(el.className);

}

/**
 * getFileContents() returns the content of a file
 *
 * @memberOf  PowerHouse
 * @name      Powerhouse.getFileContents
 * @access    public
 * @param     {Integer} [key] – the name of the item in the query string
 * @param     {Integer} [url] – the URL containing the query string
 * @returns   {Integer}
 *
 * ```js
 *   getFileContents('/file.html', function (response) {
 *     console.log(response);
 *   }, function (response)
 *     console.log('There was an error: ' + response);
 *   });
 * ```
 */
function getFileContents (file, success, error) {

  // Detect whether XMLHttpRequest is supported
  if (!window.XMLHttpRequest) {
    return;
  }

  // Create a new request
  var request = new XMLHttpRequest();

  // Setup callbacks
  request.onreadystatechange = function () {

    // If the request is completed
    if (this.readyState === 4) {

      // If the request failed
      if (request.status !== 200) {
        if (error && typeof(error) === 'function') {
          error(request.responseText, request);
        }
        return;
      }

      // If the request was successful
      if (success && typeof(success) === 'function') {
        success(request.responseText, request);
      }
    }
  };

  // Send the HTML
  request.open('GET', file, true);
  request.send();

}

/**
 * createCookie() is used to set a cookie with the specified name, value and
 * expiration values
 *
 * @memberOf  CookieDisclaimer
 * @name      createCookie
 * @access    private
 * @param     {string} [name] – the name of the cookie
 * @param     {string} [string] – the value of the cookie
 * @param     {number} [duration] – the duration of the cookie
 *
 * ```js
 *   createCookie('cookies_accepted', 'true', 30);
 * ```
 */
function createCookie (name, value, duration) {

  let expires,
      date;

  // 1. If a duration is set
  if (duration) {

    // 1. Create a new date instance
    date = new Date();
    date.setTime(date.getTime()+(duration*24*60*60*1000));

    // 2. Update the expires value
    expires = '; expires=' + date.toGMTString();

  } else {

    expires = '';

  }

  // 2. Create the cookie
  document.cookie = name + '=' + value + expires + '; path=/';

}

/**
 * readCookie() is used to read a cookie
 *
 * @memberOf  CookieDisclaimer
 * @name      readCookie
 * @access    private
 * @param     {string} [name] – the name of the cookie to be read
 *
 * ```js
 *   readCookie('cookies_accepted');
 * ```
 */
function readCookie (name) {

  let cookieName = name + '=',
      ca = document.cookie.split(';');

  for (var i = 0; i < ca.length; i++) {
    var cookie = ca[i];

    while (cookie.charAt(0)== ' ') {
      cookie = cookie.substring(1, cookie.length);
    }

    if (cookie.indexOf(cookieName) === 0) {
      return cookie.substring(cookieName.length, cookie.length);
    }
  }

  return null;

}

/**
 * init() is used to setup the cookie disclaimer
 *
 * @memberOf  CookieDisclaimer
 * @name      init
 * @access    private
 * @param     {string} [name] – the name of the cookie to be read
 *
 * ```js
 *   CookreadCookie('cookies_accepted');
 * ```
 */
export function init (options) {

  // 1. Override any default settings with user specified ones
  for(var prop in options) {
    if(options.hasOwnProperty(prop)){
      settings[prop] = options[prop];
    }
  }

  // 2. If the cookie hasn't already been setup
  if(!readCookie(settings.name)) {

    // 1. Grab the contents of the HTML template
    getFileContents(settings.template, function (response) {

      // 1. Create the banner
      let el = document.createElement('div');
      el.setAttribute('class', 'cookie-banner');
      el.setAttribute('id', 'cookie-banner');
      el.setAttribute('aria-role', 'alert');
      el.setAttribute('aria-live', 'assertive');
      el.innerHTML += response;

      // 2. Prepend to the body
      document.body.insertBefore(el, document.body.firstChild);

      // 3. Add message
      document.getElementById('message').innerHTML = settings.message;

      // 4. Add class to body
      if (document.body.classList) document.body.classList.add(bodyClass);
      else if (!hasClass(document.body, bodyClass)) document.body.className += ' ' + bodyClass;

      // 5. Add an event listener to the close button
      document.getElementById('close').onclick = function () {

        // 1. On click create the cookie
        createCookie(settings.name, true, settings.duration);

        // 2. Remove the banner
        let banner = document.getElementById('cookie-banner');
        banner.parentNode.removeChild(el);

        // 3. Remove the class from the body
        if (document.body.classList) document.body.classList.remove(bodyClass);
        else document.body.className = document.body.className.replace(new RegExp('\\b'+ bodyClass+'\\b', 'g'), '');

        // 4. Prevent the default link action
        return false;
      };

    });

  }

}
<|endoftext|>


<commit_before>#!/usr/bin/env node

var childProcess = require('child_process');

var action = process.argv[2];

var scope = process.env.npm_config_global ? 'global' : 'local';

if (action === 'install') {
    childProcess.exec("git config --"+scope+" alias.users '!"+__dirname+"/users.js'");
    childProcess.exec("git config --"+scope+" git-pair.scope "+scope);
}

if (action === 'uninstall') {
    childProcess.exec("git config --"+scope+" --unset alias.users");
    childProcess.exec("git config --"+scope+" --unset git-pair.scope");
}
<commit_msg>Improved uninstall cleanup<commit_after>#!/usr/bin/env node

var childProcess = require('child_process');

var action = process.argv[2];

var scope = process.env.npm_config_global ? 'global' : 'local';

if (action === 'install') {
    childProcess.exec("git config --"+scope+" alias.users '!"+__dirname+"/users.js'");
    childProcess.exec("git config --"+scope+" git-pair.scope "+scope);
}

if (action === 'uninstall') {
    childProcess.exec("git config --"+scope+" --unset alias.users");
    childProcess.exec("git config --"+scope+" --remove-section git-pair");
}
<|endoftext|>


<commit_before>var findTabbable = require('../helpers/tabbable');
var modalElement = null;
var focusLaterElement = null;
var needToFocus = false;

function handleBlur(event) {
  needToFocus = true;
}

function handleFocus(event) {
  if (needToFocus) {
    needToFocus = false;
    if (!modalElement) {
      return;
    }
    // need to see how jQuery shims document.on('focusin') so we don't need the
    // setTimeout, firefox doesn't support focusin, if it did, we could focus
    // the the element outisde of a setTimeout. Side-effect of this
    // implementation is that the document.body gets focus, and then we focus
    // our element right after, seems fine.
    setTimeout(function() {
      if (modalElement.contains(document.activeElement))
        return;
      var el = (findTabbable(modalElement)[0] || modalElement);
      el.focus();
    }, 0);
  }
}

exports.markForFocusLater = function() {
  focusLaterElement = document.activeElement;
};

exports.returnFocus = function() {
  try {
    focusLaterElement.focus();
  }
  catch (e) {
    console.warn('You tried to return focus to '+focusLaterElement+' but it is not in the DOM anymore');
  }
  focusLaterElement = null;
};

exports.setupScopedFocus = function(element) {
  modalElement = element;

  if (window.addEventListener) {
    window.addEventListener('blur', handleBlur, false);
    document.addEventListener('focus', handleFocus, true);
  } else {
    window.attachEvent('onBlur', handleBlur);
    document.attachEvent('onFocus', handleFocus);
  }
};

exports.teardownScopedFocus = function() {
  modalElement = null;

  if (window.addEventListener) {
    window.removeEventListener('blur', handleBlur);
    document.removeEventListener('focus', handleFocus);
  } else {
    window.detachEvent('onBlur', handleBlur);
    document.detachEvent('onFocus', handleFocus);
  }
};

<commit_msg>S-54545 sometimes modalElement is null and the setTimeouts throws and exception<commit_after>var findTabbable = require('../helpers/tabbable');
var modalElement = null;
var focusLaterElement = null;
var needToFocus = false;

function handleBlur(event) {
  needToFocus = true;
}

function handleFocus(event) {
  if (needToFocus) {
    needToFocus = false;
    if (!modalElement) {
      return;
    }
    // need to see how jQuery shims document.on('focusin') so we don't need the
    // setTimeout, firefox doesn't support focusin, if it did, we could focus
    // the the element outisde of a setTimeout. Side-effect of this
    // implementation is that the document.body gets focus, and then we focus
    // our element right after, seems fine.
    setTimeout(function() {
      if (!modalElement) {
        return;
      }
      if (modalElement.contains(document.activeElement))
        return;
      var el = (findTabbable(modalElement)[0] || modalElement);
      el.focus();
    }, 0);
  }
}

exports.markForFocusLater = function() {
  focusLaterElement = document.activeElement;
};

exports.returnFocus = function() {
  try {
    focusLaterElement.focus();
  }
  catch (e) {
    console.warn('You tried to return focus to '+focusLaterElement+' but it is not in the DOM anymore');
  }
  focusLaterElement = null;
};

exports.setupScopedFocus = function(element) {
  modalElement = element;

  if (window.addEventListener) {
    window.addEventListener('blur', handleBlur, false);
    document.addEventListener('focus', handleFocus, true);
  } else {
    window.attachEvent('onBlur', handleBlur);
    document.attachEvent('onFocus', handleFocus);
  }
};

exports.teardownScopedFocus = function() {
  modalElement = null;

  if (window.addEventListener) {
    window.removeEventListener('blur', handleBlur);
    document.removeEventListener('focus', handleFocus);
  } else {
    window.detachEvent('onBlur', handleBlur);
    document.detachEvent('onFocus', handleFocus);
  }
};

<|endoftext|>


<commit_before>describe('Protractor Demo App', function() {
  it('should have a title', function() {
    browser.get('http://juliemr.github.io/protractor-demo/');

    expect(browser.getTitle()).toEqual('Super Calculator');
  });
  it('should add one and two', function() {
    browser.get('http://juliemr.github.io/protractor-demo/');
    element(by.model('first')).sendKeys(1);
    element(by.model('second')).sendKeys(2);

    element(by.id('gobutton')).click();

    expect(element(by.binding('latest')).getText()).
        toEqual('5'); // This is wrong!
  });
});<commit_msg>test(e2e) fix e2e test<commit_after>describe('Protractor Demo App', function() {
  it('should have a title', function() {
    browser.get('http://juliemr.github.io/protractor-demo/');

    expect(browser.getTitle()).toEqual('Super Calculator');
  });
  it('should add one and two', function() {
    browser.get('http://juliemr.github.io/protractor-demo/');
    element(by.model('first')).sendKeys(1);
    element(by.model('second')).sendKeys(2);

    element(by.id('gobutton')).click();

    expect(element(by.binding('latest')).getText()).
        toEqual('3'); // This is wrong!
  });
});<|endoftext|>


<commit_before>const { expect } = require('chai');
const normalize = require('path').normalize;

const beard = require('../beard');

describe('Beard Rendering', function() {
  it('renders content', function() {
    const engine = beard({
      templates: {
        '/content': 'some content'
      }
    });
    expect(engine.render('content')).to.equal('some content');
  });

  it('handles errors', function() {
    const engine = beard({
      templates: {
        '/content': '{{foo}}}'
      }
    });
    expect(() => engine.render('content', {foo: 'value'})).to.throw(/\"\{\{foo\}\}\}\" in \/content on line 1/);
  });

  it('includes templates', function() {
    const engine = beard({
      templates: {
        '/content': 'some content',
        '/view': `header {{include 'content'}} footer`
      }
    });
    expect(engine.render('view')).to.equal('header some content footer');
  });

  it('renders blocks', function() {
    const engine = beard({
      templates: {
        '/block': '{{block footer}}a footer{{endblock}}some info - {{footer}}'
      }
    });
    expect(engine.render('block')).to.equal('some info - a footer');
  });

  it('extends layouts', function() {
    const engine = beard({
      templates: {
        '/view': `
          {{extends 'layout'}}
          page content
          {{block nav}}
            main navigation
          {{endblock}}
        `,
        '/layout': `
          header
          {{nav}}
          -
          {{content}}
          footer
        `
      }
    });
    expect(engine.render('view').replace(/\s+/g, ' ')) // replacing excessive whitespace for readability
      .to.equal(` header main navigation - page content footer `);
  });

  it('extends layouts and renders the content with put', function() {
    const engine = beard({
      templates: {
        '/view': `{{extends 'layout'}}page content`,
        '/layout': `header {{put content}} footer`
      }
    });
    expect(engine.render('view')).to.equal(`header page content footer`);
  });

  it('handles for loops', function() {
    const engine = beard({ templates: {
      '/with-index': 'names = {{for name, index in names}} {{name}} - {{index}}{{end}}',
      '/no-index': 'names = {{for name in names}} {{name}}{{end}}'
    }});
    expect(engine.render('with-index', {names: ['Bill', 'John', 'Dave']})).
      to.equal('names =  Bill - 0 John - 1 Dave - 2');
    expect(engine.render('no-index', {names: ['Bill', 'John', 'Dave']})).
      to.equal('names =  Bill John Dave');
  });

  it('handles for loops with embedded values', function() {
      const engine = beard({ templates: {
        '/loops': `
        {{for name, index in ['Charles', 'John', 'Martin']}}
          {{index}} - {{name}}
        {{end}}

        {{for sort in [{label: 'Up', val: 'asc'}, {label: 'Down', val: 'desc'}]}}
          {{sort.label}} - {{sort.val}}
        {{end}}
        `
      }});
      expect(engine.render('/loops').trim()).to.equal('0 - Charles  1 - John  2 - Martin   Up - asc  Down - desc');
  });

  it('handles each loops', function() {
    const engine = beard({
      templates: {
        '/with-index': 'people = {{each person, index in people}}{{index}} - {{person.name.first}} {{person.name.last}}! {{end}}',
        '/no-index': 'people = {{each person in people}}{{person.name.first}} {{person.name.last}}! {{end}}'
      }
    });
    const people = [
      {
        name: {
          first: 'Charles',
          last: 'Spurgeon'
        }
      },
      {
        name: {
          first: 'John',
          last: 'Calvin'
        }
      }
    ];
    expect(engine.render('with-index', {people: people})).
      to.equal('people = 0 - Charles Spurgeon! 1 - John Calvin! ');
    expect(engine.render('no-index', {people: people})).
      to.equal('people = Charles Spurgeon! John Calvin! ');
  });

  it('handles each loops with embedded values', function() {
    const engine = beard({
      templates: {
        '/loops': `
        {{each sort in [{label: 'Up', val: 'asc'}, {label: 'Down', val: 'desc'}]}}
          {{sort.label}} - {{sort.val}}
        {{end}}

        {{each count in ['Uno','Dos','Tres']}}
          {{count}}
        {{end}}
        `
      }
    });
    expect(engine.render('/loops').trim()).to.equal('Up - asc  Down - desc   Uno  Dos  Tres');
  });

  it('handles conditionals', function() {
    const engine = beard({
      templates: {
        '/with': `
          {{extends 'layout'}}
          {{if navigation === 'full'}}
            {{block nav}}full navigation{{endblock}}
          {{else if navigation === 'sub'}}
            {{block nav}}subnavigation{{endblock}}
          {{else}}
            {{block nav}}no nav{{endblock}}
          {{end}}
          {{nav}}
        `,
        '/layout': `
          header
          {{nav}}
          -
          {{put content}}
          footer
        `
      }
    });
    expect(engine.render('with', {navigation: 'full'})).to.include('full navigation');
    expect(engine.render('with', {navigation: 'sub'})).to.include('subnavigation');
    expect(engine.render('with', {navigation: 'none'})).to.include('no nav');
  });

  it('handles strings', function() {
    const engine = beard({
      templates: {
        '/content': '{{content}}'
      }
    });
    expect(engine.render('content', {content: 'some content'})).to.equal('some content');
  });

  it('handles numbers', function() {
    const engine = beard({
      templates: {
        '/value': '{{value}}'
      }
    });
    expect(engine.render('value', {value: 36})).to.equal('36');
  });

  it('handles arrays', function() {
    const engine = beard({
      templates: {
        '/arrays': '{{each name in names}}{{name}} {{end}}'
      }
    });
    expect(engine.render('arrays', {names: ['John Calvin', 'Charles Spurgeon']})).to.equal('John Calvin Charles Spurgeon ');
  });

  it('handles arrays of objects', function() {
    const engine = beard({
      templates: {
        '/arrays': '{{each person in people}}{{person.name}} {{end}}'
      }
    });
    const data = {
      people: [
        {
          name: 'John Knox'
        },
        {
          name: 'Charles Spurgeon'
        },
        {
          name: 'John Owen'
        }
      ]
    };
    expect(engine.render('arrays', data)).to.equal('John Knox Charles Spurgeon John Owen ');
  });

  it('handles functions', function() {
    const engine = beard({
      templates: {
        '/functions': 'add = {{math.add(3, 10)}}, subtract = {{math.subtract(10, 5)}}'
      }
    });
    expect(engine.render('functions', {math: {add: (x, y) => x + y, subtract: (x, y) => x - y }}))
      .to.equal('add = 13, subtract = 5');
  });

  it('handles objects', function() {
    const engine = beard({
      templates: {
        '/object': '{{resource.slug}}'
      }
    });
    const data = {
      resource: {
        slug: 'the-most-interesting-article'
      }
    };
    expect(engine.render('object', data)).to.include('the-most-interesting-article');
  });

  it('handles null values', function() {
    const engine = beard({
      templates: {
        '/null_value': '{{value}}'
      }
    });
    expect(engine.render('null_value', {value: null})).to.equal('null');
  });

  it('handles undefined values', function() {
    const engine = beard({
      templates: {
        '/undefined_value': '{{value}}'
      }
    });
    expect(engine.render('undefined_value', {value: undefined})).to.equal('undefined');
  });

  it('handles sublayouts', function() {
    const engine = beard({
      templates: {
        '/layout': 'header | {{content}} | footer',
        '/sublayout': "{{extends 'layout'}}{{sidebar}} | {{content}} | {{main}}",
        '/view': "{{include 'partial'}}",
        '/partial': "{{extends 'sublayout'}}{{block main}}main{{endblock}}{{block sidebar}}sidebar{{endblock}}hi im view"
      }
    });
    expect(engine.render('view')).to.equal('header | sidebar | hi im view | main | footer');
  });

  it('handles passing data to includes', function() {
    const engine = beard({
      templates: {
        '/view': `{{include 'item', {title: '1st', item: 1}}}, {{include 'item', {title: '2nd', item: 2}}}`,
        '/item': '{{title}} | {{item}}',
        '/multiline': `{{include 'item', {
          title: 'multi',
          item: 'line'
        }}}`
      }
    });
    expect(engine.render('view')).to.equal('1st | 1, 2nd | 2');
    expect(engine.render('multiline')).to.equal('multi | line');
  });

  it('ignores inline css and js', function() {
    const engine = beard({
      templates: {
      '/template': `

greetings

      `
      }
    });
    expect(engine.render('template')).to.equal(`

greetings

      `.replace(/\s+/g, ' '));
  });

  it('handles nested template data', function() {
    const engine = beard({
      templates: {
        '/layout': `
          im inside layout
          {{each name in names}}
            {{name}}
          {{end}}
          {{insidePartialBlock}}
          {{content}}
        `,
        '/sublayout': `
          {{extends 'layout'}}
          im in sublayout
          {{content}}
          {{foo}}
        `,
        '/view': `
          {{extends 'sublayout'}}
          im the view
          {{block foo}}
          im in foo block
          {{include 'partial', {key: value}}}
          {{endblock}}
        `,
        '/partial': `
          {{if key == 'a'}}
            {{block insidePartialBlock}}
              first partialblock
            {{endblock}}
          {{else if key == 'b'}}
            {{block insidePartialBlock}}
              second partialblock
            {{endblock}}
          {{else}}
            {{block insidePartialBlock}}
              third partialblock
            {{endblock}}
          {{end}}
        `
      }
    });
    expect(engine.render('view', {names: ['Jack', 'Black', 'John'], value: 'b'}).replace(/\s+/g, ' ')).to
      .equal(' im inside layout Jack Black John second partialblock im in sublayout im the view im in foo block ');
  });

  it('encodes html', function() {
    const engine = beard({
      templates: {
        '/encode': '{{:value}}'
      }
    });
    expect(engine.render('encode', {value: 'result&script'}))
      .to.equal('result&script<script>alert("hi'");</script>');
  });

  it('checks if undefined var exists', function() {
    const engine = beard({
      templates: {
        '/content': `{{exists jack}}jack does exist{{else}}jack does not exist{{end}}`,
      }
    });
    expect(engine.render('content')).to.equal('jack does not exist');
  });

  it('checks if assigned var exists', function() {
    const engine = beard({
      templates: {
        '/content': `{{block jack}}im jack{{endblock}}{{exists jack}}jack block exists{{else}}jack does not exist{{end}}`,
      }
    });
    expect(engine.render('content')).to.equal('jack block exists');
  });

  it('puts assigned var', function() {
    const engine = beard({
      templates: {
        '/content': `{{block jack}}im jack{{endblock}}{{put jack}}`,
      }
    });
    expect(engine.render('content')).to.equal('im jack');
  });

  it('puts undefined var without throwing error', function() {
    const engine = beard({
      templates: {
        '/content': `{{put jack}}`,
      }
    });
    expect(engine.render('content')).to.equal('');
  });

  it('gets asset path from relative path', function() {
    const engine = beard({
      templates: {
        '/content': `{{asset '../path/to/file/jack.jpg'}}`,
      }
    });
    expect(engine.render('content')).to.equal('/path/to/file/jack.jpg');
  });

  it('gets asset path from absolute path', function() {
    const engine = beard({
      templates: {
        '/content': `{{asset '/to/file/jack.jpg'}}`,
      }
    });
    expect(engine.render('content')).to.equal('/to/file/jack.jpg');
  });

  it('does not render comments', function() {
    const engine = beard({
      templates: {
        '/comments': 'some {{* a comment *}}content{{*another one*}}'
      }
    });
    expect(engine.render('comments')).to.equal('some content');
  });
});

describe('File Traversing', function() {
  it('renders files from the file system', function() {
    const engine = beard({
      root: __dirname
    });
    expect(engine.render('view').replace(/\s+/g, ' ')).to.equal('header | the view click | footer');
  });
});
<commit_msg>Add callbacks tests section<commit_after>const { expect } = require('chai');
const normalize = require('path').normalize;

const beard = require('../beard');

describe('Beard Rendering', function() {
  it('renders content', function() {
    const engine = beard({
      templates: {
        '/content': 'some content'
      }
    });
    expect(engine.render('content')).to.equal('some content');
  });

  it('handles errors', function() {
    const engine = beard({
      templates: {
        '/content': '{{foo}}}'
      }
    });
    expect(() => engine.render('content', {foo: 'value'})).to.throw(/\"\{\{foo\}\}\}\" in \/content on line 1/);
  });

  it('includes templates', function() {
    const engine = beard({
      templates: {
        '/content': 'some content',
        '/view': `header {{include 'content'}} footer`
      }
    });
    expect(engine.render('view')).to.equal('header some content footer');
  });

  it('renders blocks', function() {
    const engine = beard({
      templates: {
        '/block': '{{block footer}}a footer{{endblock}}some info - {{footer}}'
      }
    });
    expect(engine.render('block')).to.equal('some info - a footer');
  });

  it('extends layouts', function() {
    const engine = beard({
      templates: {
        '/view': `
          {{extends 'layout'}}
          page content
          {{block nav}}
            main navigation
          {{endblock}}
        `,
        '/layout': `
          header
          {{nav}}
          -
          {{content}}
          footer
        `
      }
    });
    expect(engine.render('view').replace(/\s+/g, ' ')) // replacing excessive whitespace for readability
      .to.equal(` header main navigation - page content footer `);
  });

  it('extends layouts and renders the content with put', function() {
    const engine = beard({
      templates: {
        '/view': `{{extends 'layout'}}page content`,
        '/layout': `header {{put content}} footer`
      }
    });
    expect(engine.render('view')).to.equal(`header page content footer`);
  });

  it('handles for loops', function() {
    const engine = beard({ templates: {
      '/with-index': 'names = {{for name, index in names}} {{name}} - {{index}}{{end}}',
      '/no-index': 'names = {{for name in names}} {{name}}{{end}}'
    }});
    expect(engine.render('with-index', {names: ['Bill', 'John', 'Dave']})).
      to.equal('names =  Bill - 0 John - 1 Dave - 2');
    expect(engine.render('no-index', {names: ['Bill', 'John', 'Dave']})).
      to.equal('names =  Bill John Dave');
  });

  it('handles for loops with embedded values', function() {
      const engine = beard({ templates: {
        '/loops': `
        {{for name, index in ['Charles', 'John', 'Martin']}}
          {{index}} - {{name}}
        {{end}}

        {{for sort in [{label: 'Up', val: 'asc'}, {label: 'Down', val: 'desc'}]}}
          {{sort.label}} - {{sort.val}}
        {{end}}
        `
      }});
      expect(engine.render('/loops').trim()).to.equal('0 - Charles  1 - John  2 - Martin   Up - asc  Down - desc');
  });

  it('handles each loops', function() {
    const engine = beard({
      templates: {
        '/with-index': 'people = {{each person, index in people}}{{index}} - {{person.name.first}} {{person.name.last}}! {{end}}',
        '/no-index': 'people = {{each person in people}}{{person.name.first}} {{person.name.last}}! {{end}}'
      }
    });
    const people = [
      {
        name: {
          first: 'Charles',
          last: 'Spurgeon'
        }
      },
      {
        name: {
          first: 'John',
          last: 'Calvin'
        }
      }
    ];
    expect(engine.render('with-index', {people: people})).
      to.equal('people = 0 - Charles Spurgeon! 1 - John Calvin! ');
    expect(engine.render('no-index', {people: people})).
      to.equal('people = Charles Spurgeon! John Calvin! ');
  });

  it('handles each loops with embedded values', function() {
    const engine = beard({
      templates: {
        '/loops': `
        {{each sort in [{label: 'Up', val: 'asc'}, {label: 'Down', val: 'desc'}]}}
          {{sort.label}} - {{sort.val}}
        {{end}}

        {{each count in ['Uno','Dos','Tres']}}
          {{count}}
        {{end}}
        `
      }
    });
    expect(engine.render('/loops').trim()).to.equal('Up - asc  Down - desc   Uno  Dos  Tres');
  });

  it('handles conditionals', function() {
    const engine = beard({
      templates: {
        '/with': `
          {{extends 'layout'}}
          {{if navigation === 'full'}}
            {{block nav}}full navigation{{endblock}}
          {{else if navigation === 'sub'}}
            {{block nav}}subnavigation{{endblock}}
          {{else}}
            {{block nav}}no nav{{endblock}}
          {{end}}
          {{nav}}
        `,
        '/layout': `
          header
          {{nav}}
          -
          {{put content}}
          footer
        `
      }
    });
    expect(engine.render('with', {navigation: 'full'})).to.include('full navigation');
    expect(engine.render('with', {navigation: 'sub'})).to.include('subnavigation');
    expect(engine.render('with', {navigation: 'none'})).to.include('no nav');
  });

  it('handles strings', function() {
    const engine = beard({
      templates: {
        '/content': '{{content}}'
      }
    });
    expect(engine.render('content', {content: 'some content'})).to.equal('some content');
  });

  it('handles numbers', function() {
    const engine = beard({
      templates: {
        '/value': '{{value}}'
      }
    });
    expect(engine.render('value', {value: 36})).to.equal('36');
  });

  it('handles arrays', function() {
    const engine = beard({
      templates: {
        '/arrays': '{{each name in names}}{{name}} {{end}}'
      }
    });
    expect(engine.render('arrays', {names: ['John Calvin', 'Charles Spurgeon']})).to.equal('John Calvin Charles Spurgeon ');
  });

  it('handles arrays of objects', function() {
    const engine = beard({
      templates: {
        '/arrays': '{{each person in people}}{{person.name}} {{end}}'
      }
    });
    const data = {
      people: [
        {
          name: 'John Knox'
        },
        {
          name: 'Charles Spurgeon'
        },
        {
          name: 'John Owen'
        }
      ]
    };
    expect(engine.render('arrays', data)).to.equal('John Knox Charles Spurgeon John Owen ');
  });

  it('handles functions', function() {
    const engine = beard({
      templates: {
        '/functions': 'add = {{math.add(3, 10)}}, subtract = {{math.subtract(10, 5)}}'
      }
    });
    expect(engine.render('functions', {math: {add: (x, y) => x + y, subtract: (x, y) => x - y }}))
      .to.equal('add = 13, subtract = 5');
  });

  it('handles objects', function() {
    const engine = beard({
      templates: {
        '/object': '{{resource.slug}}'
      }
    });
    const data = {
      resource: {
        slug: 'the-most-interesting-article'
      }
    };
    expect(engine.render('object', data)).to.include('the-most-interesting-article');
  });

  it('handles null values', function() {
    const engine = beard({
      templates: {
        '/null_value': '{{value}}'
      }
    });
    expect(engine.render('null_value', {value: null})).to.equal('null');
  });

  it('handles undefined values', function() {
    const engine = beard({
      templates: {
        '/undefined_value': '{{value}}'
      }
    });
    expect(engine.render('undefined_value', {value: undefined})).to.equal('undefined');
  });

  it('handles sublayouts', function() {
    const engine = beard({
      templates: {
        '/layout': 'header | {{content}} | footer',
        '/sublayout': "{{extends 'layout'}}{{sidebar}} | {{content}} | {{main}}",
        '/view': "{{include 'partial'}}",
        '/partial': "{{extends 'sublayout'}}{{block main}}main{{endblock}}{{block sidebar}}sidebar{{endblock}}hi im view"
      }
    });
    expect(engine.render('view')).to.equal('header | sidebar | hi im view | main | footer');
  });

  it('handles passing data to includes', function() {
    const engine = beard({
      templates: {
        '/view': `{{include 'item', {title: '1st', item: 1}}}, {{include 'item', {title: '2nd', item: 2}}}`,
        '/item': '{{title}} | {{item}}',
        '/multiline': `{{include 'item', {
          title: 'multi',
          item: 'line'
        }}}`
      }
    });
    expect(engine.render('view')).to.equal('1st | 1, 2nd | 2');
    expect(engine.render('multiline')).to.equal('multi | line');
  });

  it('ignores inline css and js', function() {
    const engine = beard({
      templates: {
      '/template': `

greetings

      `
      }
    });
    expect(engine.render('template')).to.equal(`

greetings

      `.replace(/\s+/g, ' '));
  });

  it('handles nested template data', function() {
    const engine = beard({
      templates: {
        '/layout': `
          im inside layout
          {{each name in names}}
            {{name}}
          {{end}}
          {{insidePartialBlock}}
          {{content}}
        `,
        '/sublayout': `
          {{extends 'layout'}}
          im in sublayout
          {{content}}
          {{foo}}
        `,
        '/view': `
          {{extends 'sublayout'}}
          im the view
          {{block foo}}
          im in foo block
          {{include 'partial', {key: value}}}
          {{endblock}}
        `,
        '/partial': `
          {{if key == 'a'}}
            {{block insidePartialBlock}}
              first partialblock
            {{endblock}}
          {{else if key == 'b'}}
            {{block insidePartialBlock}}
              second partialblock
            {{endblock}}
          {{else}}
            {{block insidePartialBlock}}
              third partialblock
            {{endblock}}
          {{end}}
        `
      }
    });
    expect(engine.render('view', {names: ['Jack', 'Black', 'John'], value: 'b'}).replace(/\s+/g, ' ')).to
      .equal(' im inside layout Jack Black John second partialblock im in sublayout im the view im in foo block ');
  });

  it('encodes html', function() {
    const engine = beard({
      templates: {
        '/encode': '{{:value}}'
      }
    });
    expect(engine.render('encode', {value: 'result&script'}))
      .to.equal('result&script<script>alert("hi'");</script>');
  });

  it('checks if undefined var exists', function() {
    const engine = beard({
      templates: {
        '/content': `{{exists jack}}jack does exist{{else}}jack does not exist{{end}}`,
      }
    });
    expect(engine.render('content')).to.equal('jack does not exist');
  });

  it('checks if assigned var exists', function() {
    const engine = beard({
      templates: {
        '/content': `{{block jack}}im jack{{endblock}}{{exists jack}}jack block exists{{else}}jack does not exist{{end}}`,
      }
    });
    expect(engine.render('content')).to.equal('jack block exists');
  });

  it('puts assigned var', function() {
    const engine = beard({
      templates: {
        '/content': `{{block jack}}im jack{{endblock}}{{put jack}}`,
      }
    });
    expect(engine.render('content')).to.equal('im jack');
  });

  it('puts undefined var without throwing error', function() {
    const engine = beard({
      templates: {
        '/content': `{{put jack}}`,
      }
    });
    expect(engine.render('content')).to.equal('');
  });

  it('gets asset path from relative path', function() {
    const engine = beard({
      templates: {
        '/views/content': `{{asset '../assets/jack.jpg'}}`,
      }
    });
    expect(engine.render('/views/content')).to.equal('/assets/jack.jpg');
  });

  it('gets asset path from absolute path', function() {
    const engine = beard({
      templates: {
        '/content': `{{asset '/to/file/jack.jpg'}}`,
      }
    });
    expect(engine.render('content')).to.equal('/to/file/jack.jpg');
  });

  it('does not render comments', function() {
    const engine = beard({
      templates: {
        '/comments': 'some {{* a comment *}}content{{*another one*}}'
      }
    });
    expect(engine.render('comments')).to.equal('some content');
  });
});

describe('File Traversing', function() {
  it('renders files from the file system', function() {
    const engine = beard({
      root: __dirname
    });
    expect(engine.render('view').replace(/\s+/g, ' ')).to.equal('header | the view click | footer');
  });
});

describe('Callbacks', function() {
  it('allows a callback on the asset function', function() {
    const engine = beard({
      templates: {
        '/views/content': `{{asset '../images/calvin.png'}}`,
      },
      asset: (path) => `/dist${path}`
    });
    expect(engine.render('/views/content')).to.equal('/dist/images/calvin.png');
  });
});
<|endoftext|>


<commit_before>/**
 * @author Rob Taylor [manix84@gmail.com]
 */
define('utils/object/length', function () {

    /**
     * Checks the length of the passed object
     * @param {object} object - Object to be checked
     * @example object.length({key1: 'var', key2: 'var'}); // RETURNS: 2
     * @returns {number}
     * @todo  Add depth.
     */
    var length = function (object) {
        var prop,
            size = 0;
        for (prop in object) {
            if (object.hasOwnProperty(prop)) {
                size++;
            }
        }
        return size;
    };

    return length;
});
<commit_msg>Relocating object/length references.<commit_after>/**
 * @author Rob Taylor [manix84@gmail.com]
 */
define('object/length', function () {

    /**
     * Checks the length of the passed object
     * @exports object/length
     *
     * @param {object} object - Object to be checked
     * @example object.length({key1: 'var', key2: 'var'}); // RETURNS: 2
     * @returns {number}
     * @todo  Add depth.
     */
    var length = function (object) {
        var prop,
            size = 0;
        for (prop in object) {
            if (object.hasOwnProperty(prop)) {
                size++;
            }
        }
        return size;
    };

    return length;
});
<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const requireDir = require('require-directory');
const paramHelper = require('./param-helper');
const errorHelper = require('./error-helper');
const middlewareHelper = require('./middleware-helper');

class RouteHelper {
  constructor() {
    // holds controller classes
    this._controllerClasses = [];
    // holds controller instances
    this._controllers = {};
  }

  collectRoutes(rawRoutes, options) {
    // init routes
    this.routes = {};

    this._controllerClasses = this._loadControllers(options.basePath);
    this._preprocess(rawRoutes);
    return this.routes;
  }

  _preprocess(rawRoutes, parent) {
    for (let i = 0, l = rawRoutes.length; i < l; i++) {
      // method is mandatory for a route.
      // if no method is specified, path will be considered prefix for children
      if (rawRoutes[i].method) {
        // route validation
        this._validateRoute(rawRoutes[i]);

        const processedRoute = this._processRoute(rawRoutes[i], parent);
        this._pushRoute(processedRoute);
      }

      if (Array.isArray(rawRoutes[i].children)) {
        this._preprocess(rawRoutes[i].children, rawRoutes[i]);
      }
    }
  }

  _validateRoute(route) {
    // path
    // REQUIRED
    if (!route.path) {
      errorHelper.throwError({
        error: `missing path.`,
        line : `Route { path: ${route.path}, method: ${route.method} }`,
        file : `routes.js`,
        hint : `add a 'path'.`
      });
    }

    // handler
    // REQUIRED
    if (!route.handler) {
      errorHelper.throwError({
        error: `missing handler.`,
        line : `Route { path: ${route.path}, method: ${route.method} }`,
        file : `routes.js`,
        hint : `add a 'handler' (or) remove 'method' property, if the 'path' is prefix for child routes.`
      });
    }
    // FORMAT
    const index = route.handler.indexOf('@');
    if (index === -1 || index === route.handler.length-1) {
      errorHelper.throwError({
        error: `invalid format for 'handler'.`,
        line : `Route: { path: ${route.path}, method: ${route.method}, handler: ${route.handler} }`,
        file : `routes.js`,
        hint : `valid format - SomeController@method`
      });
    }
  }

  _pushRoute(route) {
    const method = route.method.toLowerCase();

    if (!Array.isArray(this.routes[method])) {
      this.routes[method] = [];
    }

    // check if similar route exists
    for (let _route of this.routes[method]) {
      if (_route.footprint === route.footprint) {
        errorHelper.throwError({
          error: `route conflict.`,
          line : [
            `following routes appears either same or conflicting.`,
            `Route 1: { path: ${route.path}, method: ${route.method} }`,
            `Route 2: { path: ${_route.path}, method: ${_route.method} }`
          ],
          file : `routes.js`,
          hint : `a route must be unique by its 'path' and 'method'`
        });
      }
    }

    this.routes[method].push(route);
  }

  _processRoute(route, parent) {
    const handler = this._splitHandler(route.handler);
    const controllerKey = handler.controllerPath + '/' + handler.controllerName;

    // save the controller objects
    if (!this._controllers[controllerKey]) {
      let ctrlObj = null;

      if (handler.controllerPath) {
        for (let key of handler.controllerPath.split(path.delimiter)) {
          ctrlObj = this._controllerClasses[key];

          // path not found
          if (!ctrlObj) {
            errorHelper.throwError({
              error: `Controller '${handler.controllerName}' not found in the path app/controllers/${handler.controllerPath}.`,
              line : `Route: { path: ${route.path}, method: ${route.method}, handler: ${route.handler} }`,
              file : `routes.js`,
              hint : `make sure Controller path is correct (case sensitive) and is present inside app/controllers`
            });
          }
        }
      } else {
        ctrlObj = this._controllerClasses;
      }

      const Controller = ctrlObj[handler.controllerName];

      if (!Controller) {
        errorHelper.throwError({
          error: `Controller '${handler.controllerName}' not found.`,
          line : `Route: { path: ${route.path}, method: ${route.method}, handler: ${route.handler} }`,
          file : `routes.js`,
          hint : `make sure Controller filename is same as given in handler; and is present inside app/controllers`
        });
      }
      this._controllers[controllerKey] = new Controller();
    }

    const handlerMethod = this._controllers[controllerKey][handler.methodName];

    if (!handlerMethod) {
      errorHelper.throwError({
        error: `handler method '${handler.methodName}' not found. [in ${handler.controllerName}]`,
        line : `Route: { path: ${route.path}, method: ${route.method}, handler: ${route.handler} }`,
        file : `routes.js`,
        hint : `add the handler method in controller`
      });
    }

    const params = paramHelper.parseParams(route.path);
    const routePath = parent ? parent.path + route.path : route.path;

    return {
      method: route.method.toUpperCase(),
      path: this._trimTrailingSlash(routePath),
      segments: this._getSegments(routePath),
      footprint: this._getRouteFootprint(routePath),
      params: params,
      hasParams: !!params.length,
      handlerController: this._controllers[handler.controllerName],
      handler: handlerMethod,
      middleware: middlewareHelper.getRouteMiddleware(route),
    };
  }

  _trimTrailingSlash(routePath) {
    if (routePath[routePath.length-1] === '/') {
      return routePath.slice(0, -1);
    }
    return routePath;
  }

  _getRouteFootprint(routePath) {
    const segments = this._getSegments(routePath);
    return '/' + segments.map((s) => this._hasParams(s) ? '?' : s).join('/');
  }

  _getSegments(routePath) {
    return this._trimTrailingSlash(routePath).split('/').slice(1);
  }

  _hasParams(routePath) {
    return routePath.indexOf('{') > -1;
  }

  _splitHandler(handlerString) {
    const hs = handlerString.split('@');
    const controllerName = path.basename(hs[0]);
    let controllerPath = path.dirname(hs[0]) || '';
    if (controllerPath[0] === '.') {
      controllerPath = '';
    }

    return {
      controllerName: controllerName,
      controllerPath: controllerPath,
      methodName: hs.length > 1 ? hs[1] : null
    };
  }

  _loadControllers(basePath) {
    return requireDir(module, basePath);
  }
}

// singleton
module.exports = new RouteHelper();
<commit_msg>#14 fixed the issue with child routes<commit_after>'use strict';

const path = require('path');
const requireDir = require('require-directory');
const paramHelper = require('./param-helper');
const errorHelper = require('./error-helper');
const middlewareHelper = require('./middleware-helper');

class RouteHelper {
  constructor() {
    // holds controller classes
    this._controllerClasses = [];
    // holds controller instances
    this._controllers = {};
  }

  collectRoutes(rawRoutes, options) {
    // init routes
    this.routes = {};

    this._controllerClasses = this._loadControllers(options.basePath);
    this._preprocess(rawRoutes);
    return this.routes;
  }

  _preprocess(rawRoutes, parent) {
    for (let i = 0, l = rawRoutes.length; i < l; i++) {
      // method is mandatory for a route.
      // if no method is specified, path will be considered prefix for children
      if (rawRoutes[i].method) {
        // route validation
        this._validateRoute(rawRoutes[i]);

        // attach parent
        rawRoutes[i].parent = parent;

        const processedRoute = this._processRoute(rawRoutes[i]);
        this._pushRoute(processedRoute);
      }

      if (Array.isArray(rawRoutes[i].children)) {
        this._preprocess(rawRoutes[i].children, rawRoutes[i]);
      }
    }
  }

  _validateRoute(route) {
    // path
    // REQUIRED
    if (!route.path) {
      errorHelper.throwError({
        error: `missing path.`,
        line : `Route { path: ${route.path}, method: ${route.method} }`,
        file : `routes.js`,
        hint : `add a 'path'.`
      });
    }

    // handler
    // REQUIRED
    if (!route.handler) {
      errorHelper.throwError({
        error: `missing handler.`,
        line : `Route { path: ${route.path}, method: ${route.method} }`,
        file : `routes.js`,
        hint : `add a 'handler' (or) remove 'method' property, if the 'path' is prefix for child routes.`
      });
    }
    // FORMAT
    const index = route.handler.indexOf('@');
    if (index === -1 || index === route.handler.length-1) {
      errorHelper.throwError({
        error: `invalid format for 'handler'.`,
        line : `Route: { path: ${route.path}, method: ${route.method}, handler: ${route.handler} }`,
        file : `routes.js`,
        hint : `valid format - SomeController@method`
      });
    }
  }

  _pushRoute(route) {
    const method = route.method.toLowerCase();

    if (!Array.isArray(this.routes[method])) {
      this.routes[method] = [];
    }

    // check if similar route exists
    for (let _route of this.routes[method]) {
      if (_route.footprint === route.footprint) {
        errorHelper.throwError({
          error: `route conflict.`,
          line : [
            `following routes appears either same or conflicting.`,
            `Route 1: { path: ${route.path}, method: ${route.method} }`,
            `Route 2: { path: ${_route.path}, method: ${_route.method} }`
          ],
          file : `routes.js`,
          hint : `a route must be unique by its 'path' and 'method'`
        });
      }
    }

    this.routes[method].push(route);
  }

  _processRoute(route) {
    const handler = this._splitHandler(route.handler);
    const controllerKey = handler.controllerPath + '/' + handler.controllerName;

    // save the controller objects
    if (!this._controllers[controllerKey]) {
      let ctrlObj = null;

      if (handler.controllerPath) {
        for (let key of handler.controllerPath.split(path.delimiter)) {
          ctrlObj = this._controllerClasses[key];

          // path not found
          if (!ctrlObj) {
            errorHelper.throwError({
              error: `Controller '${handler.controllerName}' not found in the path app/controllers/${handler.controllerPath}.`,
              line : `Route: { path: ${route.path}, method: ${route.method}, handler: ${route.handler} }`,
              file : `routes.js`,
              hint : `make sure Controller path is correct (case sensitive) and is present inside app/controllers`
            });
          }
        }
      } else {
        ctrlObj = this._controllerClasses;
      }

      const Controller = ctrlObj[handler.controllerName];

      if (!Controller) {
        errorHelper.throwError({
          error: `Controller '${handler.controllerName}' not found.`,
          line : `Route: { path: ${route.path}, method: ${route.method}, handler: ${route.handler} }`,
          file : `routes.js`,
          hint : `make sure Controller filename is same as given in handler; and is present inside app/controllers`
        });
      }
      this._controllers[controllerKey] = new Controller();
    }

    const handlerMethod = this._controllers[controllerKey][handler.methodName];

    if (!handlerMethod) {
      errorHelper.throwError({
        error: `handler method '${handler.methodName}' not found. [in ${handler.controllerName}]`,
        line : `Route: { path: ${route.path}, method: ${route.method}, handler: ${route.handler} }`,
        file : `routes.js`,
        hint : `add the handler method in controller`
      });
    }

    const params = paramHelper.parseParams(route.path);
    const routePath = this._getFullRoutePath(route);

    return {
      method: route.method.toUpperCase(),
      path: this._trimTrailingSlash(routePath),
      segments: this._getSegments(routePath),
      footprint: this._getRouteFootprint(routePath),
      params: params,
      hasParams: !!params.length,
      handlerController: this._controllers[handler.controllerName],
      handler: handlerMethod,
      middleware: middlewareHelper.getRouteMiddleware(route),
    };
  }

  _getFullRoutePath(route) {
    if (!route) return '';

    if (route.parent) {
      return this._getFullRoutePath(route.parent) + route.path;
    }

    return route.path;
  }

  _trimTrailingSlash(routePath) {
    if (routePath[routePath.length-1] === '/') {
      return routePath.slice(0, -1);
    }
    return routePath;
  }

  _getRouteFootprint(routePath) {
    const segments = this._getSegments(routePath);
    return '/' + segments.map((s) => this._hasParams(s) ? '?' : s).join('/');
  }

  _getSegments(routePath) {
    return this._trimTrailingSlash(routePath).split('/').slice(1);
  }

  _hasParams(routePath) {
    return routePath.indexOf('{') > -1;
  }

  _splitHandler(handlerString) {
    const hs = handlerString.split('@');
    const controllerName = path.basename(hs[0]);
    let controllerPath = path.dirname(hs[0]) || '';
    if (controllerPath[0] === '.') {
      controllerPath = '';
    }

    return {
      controllerName: controllerName,
      controllerPath: controllerPath,
      methodName: hs.length > 1 ? hs[1] : null
    };
  }

  _loadControllers(basePath) {
    return requireDir(module, basePath);
  }
}

// singleton
module.exports = new RouteHelper();
<|endoftext|>


<commit_before>// generateJSON

var bella = require('bellajs');

var debug = require('debug');
var info = debug('pps:info');
var error = debug('pps:error');

var removeAccents = require('./removeAccents');
var readFile = require('./readFile');
var writeFile = require('./writeFile');

const THIS_YEAR = (new Date()).getFullYear();

var standalizeName = (person) => {
  let name = removeAccents(person.name);
  let arr = bella.stabilize(name.split(' '));
  person.name = arr.last() + ' ' + arr.first();
  return person;
};

var getYoE = (begin) => {
  let y = THIS_YEAR - begin;
  if (y > 1) {
    return `${y} years of experience`;
  }
  if (y === 1) {
    return `${y} year of experience`;
  }
  return 'Several months';
};

var mapSkillsToPeople = (person, mapper) => {
  let skills = person.skills.map((sk) => {
    return mapper.filter((item) => {
      return item.id === sk;
    }).map((skll) => {
      return [skll.name, getYoE(skll.since)];
    });
  }).reduce((prev, curr) => {
    return prev.concat(curr);
  }, []);
  person.skills = skills;
  return person;
};

var mapSkillsToProjects = (project, mapper) => {
  let stacks = project.stacks.map((sk) => {
    return mapper.filter((item) => {
      return item.id === sk;
    }).map((skll) => {
      return skll.name;
    });
  }).reduce((prev, curr) => {
    return prev.concat(curr);
  }, []);
  project.stacks = stacks;
  return project;
};

var generateJSON = async () => {

  let file = './dist/widget/ppsloop.widget.json';

  let ofile = './src/data.json';
  let dataText = await readFile(ofile);
  if (!dataText) {
    info(`Couldl not generate. ${ofile} not found.`);
    return false;
  }

  try {
    let json = JSON.parse(dataText);
    let {
      people,
      projects,
      skills,
      peopleSkills,
      projectSkills
    } = json;

    let arrPeople = people.map(standalizeName).map((item) => {
      return mapSkillsToPeople(item, peopleSkills);
    });

    let arrProjects = projects.map((item) => {
      return mapSkillsToProjects(item, projectSkills);
    });

    let output = {
      people: arrPeople,
      projects: arrProjects,
      techstacks: skills.map((item) => {
        return [item.name, item.image];
      })
    };

    await writeFile(file, JSON.stringify(output));
    info(`JSON data has been generated`);
    return true;
  } catch (err) {
    error('Error while generating data file');
    return error(err);
  }
};

module.exports = generateJSON;
<commit_msg>Update json generator<commit_after>// generateJSON

var bella = require('bellajs');

var debug = require('debug');
var info = debug('pps:info');
var error = debug('pps:error');

var removeAccents = require('./removeAccents');
var readFile = require('./readFile');
var writeFile = require('./writeFile');

const THIS_YEAR = (new Date()).getFullYear();

var standalizeName = (person) => {
  let name = removeAccents(person.name);
  let arr = bella.stabilize(name.split(' '));
  person.name = arr.last() + ' ' + arr.first();
  return person;
};

var getYoE = (begin) => {
  let y = THIS_YEAR - begin;
  if (y > 1) {
    return `${y} years of experience`;
  }
  if (y === 1) {
    return `${y} year of experience`;
  }
  return 'Several months';
};

var mapSkillsToPeople = (person, mapper) => {
  let skills = person.skills.map((sk) => {
    return mapper.filter((item) => {
      return item.id === sk;
    }).map((skll) => {
      return [skll.name, getYoE(skll.since)];
    });
  }).reduce((prev, curr) => {
    return prev.concat(curr);
  }, []);
  person.skills = skills;
  return person;
};

var mapSkillsToProjects = (project, mapper) => {
  let stacks = project.stacks.map((sk) => {
    return mapper.filter((item) => {
      return item.id === sk;
    }).map((skll) => {
      return skll.name;
    });
  }).reduce((prev, curr) => {
    return prev.concat(curr);
  }, []);
  project.stacks = stacks;
  return project;
};

var generateJSON = async () => {

  let file = './dist/widget/ppsloop.widget.json';

  let ofile = './src/data.json';
  let dataText = await readFile(ofile);
  if (!dataText) {
    info(`Couldl not generate. ${ofile} not found.`);
    return false;
  }

  try {
    let json = JSON.parse(dataText);
    let {
      people,
      projects,
      skills,
      peopleSkills,
      projectSkills
    } = json;

    let arrPeople = people.map(standalizeName).map((item) => {
      return mapSkillsToPeople(item, peopleSkills);
    });

    let arrProjects = projects.map((item) => {
      return mapSkillsToProjects(item, projectSkills);
    });

    let counter = {};
    peopleSkills.forEach((item) => {
      let {
        name
      } = item;
      let curr = counter[name] || 0;
      counter[name] = curr + 1;
    });

    let output = {
      people: arrPeople,
      projects: arrProjects,
      techstacks: skills.map((item) => {
        let {
          name,
          image
        } = item;
        return [name, image, counter[name] || 0];
      })
    };

    await writeFile(file, JSON.stringify(output));
    info(`JSON data has been generated`);
    return true;
  } catch (err) {
    error('Error while generating data file');
    return error(err);
  }
};

module.exports = generateJSON;
<|endoftext|>