<commit_before>const Engine = imports.search.engine;
const EosShard = imports.gi.EosShard;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const QueryObject = imports.search.queryObject;
const System = imports.system;

const USAGE = [
    'usage: kermit grep <shard path> <pattern>',
    '       kermit list <shard path>',
    '       kermit dump <shard path> <ekn id> <data|metadata|another blob name>',
    '       kermit query <app_id> "<querystring>"',
    '       kermit stat <shard path>',
    '',
    'kermit is a shard inspection utility for Knowledge Apps.',
].join('\n');

// For those interested in kermit's etymology, it goes roughly like this:
// EosShard -> Dark Shard -> Dark Crystal -> Jim Hensen -> Kermit

// size of xapian-bridge result batches
const BATCH_SIZE = 10;

function main () {
    let argv = ARGV.slice();
    let action = argv.shift();

    if (action === 'grep' && argv.length === 2)
        grep(argv[0], argv[1]);
    else if (action === 'list' && argv.length === 1)
        grep(argv[0], '');
    else if (action === 'dump' && argv.length === 3)
        dump(argv[0], argv[1], argv[2]);
    else if (action === 'query' && argv.length === 2)
        query(argv[0], argv[1]);
    else if (action === 'stat' && argv.length === 1)
        stat(argv[0]);
    else
        fail_with_message(USAGE);
}

function grep (path, pattern) {
    let shard = get_shard_for_path(path);
    let records = shard.list_records();
    records.forEach(function (record, i) {
        let regex = new RegExp(pattern);
        let id = record.get_hex_name();
        let offset, content_type;

        if (record.data) {
            offset = record.data.get_offset();
            content_type = record.data.get_content_type();
        } else {
            offset = undefined;
            content_type = "Unknown - no data";
        }

        if (!record.metadata) {
            print_result(id, content_type, "Unknown - no metadata", offset);
            return;
        }

        let metadata_text = record.metadata.load_contents().get_data().toString();

        if (metadata_text.match(regex) !== null) {
            let metadata = JSON.parse(metadata_text);
            let title = metadata.title;
            print_result(id, content_type, title, offset);
        }

        // Unfortunately, Spidermonkey isn't able to effectively track the
        // memory footprint of native objects like GBytes very well, so we
        // have to nudge it in the right direction every now and then.
        if (i%1000 === 0)
            imports.system.gc();
    });
}

function fmt_size (size_bytes) {
    if (size_bytes < 1e3) {
        return size_bytes.toPrecision(4) + ' Bytes';
    } else if (size_bytes >= 1e3 && size_bytes < 1e6) {
        let size_kbytes = size_bytes / 1e3;
        return size_kbytes.toPrecision(4) + ' KB';
    } else {
        let size_mbytes = size_bytes / 1e6;
        return size_mbytes.toPrecision(4) + ' MB';
    }
}

function stat (path) {
    let shard = get_shard_for_path(path);
    let records = shard.list_records();

    /**
     * To calculate the sum of all spaces between blobs (i.e. wasted space),
     * we only keep track of the first and last offsets (along with the total
     * blob length), effectively squashing all blobs but the last one together.
     * In general, this shard
     *
     *   [ block 1 ]      // offset 1
     *   empty space 1
     *   [ block 2 ]      // offset 2
     *   empty space 2
     *   ...
     *   [ block N-1 ]    // offset N-1
     *   empty space N-1
     *   [ block N ]      // offset N
     *
     * would become
     *
     *   [ block 1 ]      // offset 1
     *   [ block 2 ]
     *   ...
     *   [ block N-1 ]
     *   empty space 1
     *   empty space 2
     *   ...
     *   empty space N-1
     *   [ block N ]      // offset N
     *
     * Then we just subtract the megablock's offset and length from the last
     * block's offset to get the total empty space.
     */
    let first_offset = Infinity, last_offset = 0;
    let length_sum = 0, last_length = 0;
    let content_type_sizes = {};
    function tallyBlob (blob) {
        let offset = blob.get_offset();
        let length = blob.get_packed_size();
        let content_type = blob.get_content_type();

        if (offset > last_offset) {
            last_offset = offset;
            last_length = length;
        }
        if (offset < first_offset) {
            first_offset = offset;
        }

        length_sum += length;

        if (!content_type_sizes.hasOwnProperty(content_type)) {
            content_type_sizes[content_type] = [];
        }
        content_type_sizes[content_type].push(length);
    }

    records.forEach(function (record, i) {
        let data = record.data;
        let metadata = record.metadata;

        if (data) {
            tallyBlob(data);
        }
        if (metadata) {
            tallyBlob(metadata);
        }

        // Unfortunately, Spidermonkey isn't able to effectively track the
        // memory footprint of native objects like GBytes very well, so we
        // have to nudge it in the right direction every now and then.
        if (i%1000 === 0)
            imports.system.gc();
    });

    let empty_bytes = last_offset - (first_offset + length_sum - last_length);
    print('Wasted space between blobs:', fmt_size(empty_bytes));

    for (let content_type in content_type_sizes) {
        let max_bytes = 0, avg_bytes = 0;
        for (let size_bytes of content_type_sizes[content_type]) {
            avg_bytes += size_bytes;
            if (size_bytes > max_bytes)
                max_bytes = size_bytes;
        }
        avg_bytes /= content_type_sizes[content_type].length;

        let str = 'Average size for ' + content_type + ': ' + fmt_size(avg_bytes);
        str += ' (max ' + fmt_size(max_bytes) + ')';
        print(str);
    }
}

function query (app_id, query_string) {
    let engine = new Engine.Engine();
    let query_obj = new QueryObject.QueryObject({
        query: query_string,
        limit: BATCH_SIZE,
        app_id: app_id,
    });
    perform_query(engine, query_obj);

    // run the mainloop so we don't exit before our callback fires
    imports.mainloop.run();
}

function perform_query (engine, query_obj) {
    engine.get_objects_by_query(query_obj, null, (engine, task) => {
        try {
            let [results, info] = engine.get_objects_by_query_finish(task);
            results.forEach(function (result) {
                let id = result.ekn_id.split('/').pop();
                print_result(id, result.content_type, result.title);
            });

            // if there were fewer than the requested number of results, that
            // means we're done.
            if (results.length < BATCH_SIZE) {
                System.exit(0);
            } else {
                perform_query(engine, info.more_results);
            }
        } catch (e) {
            fail_with_error(e);
        }
    });
}

function print_result (id, content_type, title, offset) {
    let output = [
        id,
        content_type,
        // use JSON.stringify to escape title and wrap in quotes
        JSON.stringify(title),
    ];
    if (typeof offset !== 'undefined')
        output.push('Offset ' + offset);
    print(output.join(' - '));
}

function dump (path, id, blob_name) {
    let shard = get_shard_for_path(path);
    let record = shard.find_record_by_hex_name(id);

    if (record === null) {
        let sha1 = GLib.compute_checksum_for_string(GLib.ChecksumType.SHA1, id, -1);
        record = shard.find_record_by_hex_name(sha1);
    }

    if (record === null) {
        fail_with_message('Could not find shard entry for id', id);
    }

    function find_blob() {
        if (blob_name === 'data')
            return record.data;
        else if (blob_name === 'metadata')
            return record.metadata;
        else
            return record.lookup_blob(blob_name);
    }

    let stdout = Gio.UnixOutputStream.new(1, false);
    let blob = find_blob();
    stdout.splice(blob.get_stream(), Gio.OutputStreamSpliceFlags.NONE, null);
}

function get_shard_for_path (path) {
    let shard;
    try {
        shard = new EosShard.ShardFile({
            path: path,
        });
        shard.init(null);
    } catch (e) {
        fail_with_error(e, 'Could not open shard at path', path);
    }
    return shard;
}

function fail_with_message () {
    // join args with space, a la print/console.log
    var args = Array.prototype.slice.call(arguments);
    printerr(args.join(' '));
    System.exit(1);
}

function fail_with_error (e) {
    var args = Array.prototype.slice.call(arguments, 1);
    logError(e, e + ' ' + args.join(' '));
    System.exit(1);
}
<commit_msg>Revert "kermit: add stat function"<commit_after>const Engine = imports.search.engine;
const EosShard = imports.gi.EosShard;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const QueryObject = imports.search.queryObject;
const System = imports.system;

const USAGE = [
    'usage: kermit grep <shard path> <pattern>',
    '       kermit list <shard path>',
    '       kermit dump <shard path> <ekn id> <data|metadata|another blob name>',
    '       kermit query <app_id> "<querystring>"',
    '',
    'kermit is a shard inspection utility for Knowledge Apps.',
].join('\n');

// For those interested in kermit's etymology, it goes roughly like this:
// EosShard -> Dark Shard -> Dark Crystal -> Jim Hensen -> Kermit

// size of xapian-bridge result batches
const BATCH_SIZE = 10;

function main () {
    let argv = ARGV.slice();
    let action = argv.shift();

    if (action === 'grep' && argv.length === 2)
        grep(argv[0], argv[1]);
    else if (action === 'list' && argv.length === 1)
        grep(argv[0], '');
    else if (action === 'dump' && argv.length === 3)
        dump(argv[0], argv[1], argv[2]);
    else if (action === 'query' && argv.length === 2)
        query(argv[0], argv[1]);
    else
        fail_with_message(USAGE);
}

function grep (path, pattern) {
    let shard = get_shard_for_path(path);
    let records = shard.list_records();
    records.forEach(function (record, i) {
        let regex = new RegExp(pattern);
        let id = record.get_hex_name();
        let offset, content_type;

        if (record.data) {
            offset = record.data.get_offset();
            content_type = record.data.get_content_type();
        } else {
            offset = undefined;
            content_type = "Unknown - no data";
        }

        if (!record.metadata) {
            print_result(id, content_type, "Unknown - no metadata", offset);
            return;
        }

        let metadata_text = record.metadata.load_contents().get_data().toString();

        if (metadata_text.match(regex) !== null) {
            let metadata = JSON.parse(metadata_text);
            let title = metadata.title;
            print_result(id, content_type, title, offset);
        }

        // Unfortunately, Spidermonkey isn't able to effectively track the
        // memory footprint of native objects like GBytes very well, so we
        // have to nudge it in the right direction every now and then.
        if (i%1000 === 0)
            imports.system.gc();
    });
}

function query (app_id, query_string) {
    let engine = new Engine.Engine();
    let query_obj = new QueryObject.QueryObject({
        query: query_string,
        limit: BATCH_SIZE,
        app_id: app_id,
    });
    perform_query(engine, query_obj);

    // run the mainloop so we don't exit before our callback fires
    imports.mainloop.run();
}

function perform_query (engine, query_obj) {
    engine.get_objects_by_query(query_obj, null, (engine, task) => {
        try {
            let [results, info] = engine.get_objects_by_query_finish(task);
            results.forEach(function (result) {
                let id = result.ekn_id.split('/').pop();
                print_result(id, result.content_type, result.title);
            });

            // if there were fewer than the requested number of results, that
            // means we're done.
            if (results.length < BATCH_SIZE) {
                System.exit(0);
            } else {
                perform_query(engine, info.more_results);
            }
        } catch (e) {
            fail_with_error(e);
        }
    });
}

function print_result (id, content_type, title, offset) {
    let output = [
        id,
        content_type,
        // use JSON.stringify to escape title and wrap in quotes
        JSON.stringify(title),
    ];
    if (typeof offset !== 'undefined')
        output.push('Offset ' + offset);
    print(output.join(' - '));
}

function dump (path, id, blob_name) {
    let shard = get_shard_for_path(path);
    let record = shard.find_record_by_hex_name(id);

    if (record === null) {
        let sha1 = GLib.compute_checksum_for_string(GLib.ChecksumType.SHA1, id, -1);
        record = shard.find_record_by_hex_name(sha1);
    }

    if (record === null) {
        fail_with_message('Could not find shard entry for id', id);
    }

    function find_blob() {
        if (blob_name === 'data')
            return record.data;
        else if (blob_name === 'metadata')
            return record.metadata;
        else
            return record.lookup_blob(blob_name);
    }

    let stdout = Gio.UnixOutputStream.new(1, false);
    let blob = find_blob();
    stdout.splice(blob.get_stream(), Gio.OutputStreamSpliceFlags.NONE, null);
}

function get_shard_for_path (path) {
    let shard;
    try {
        shard = new EosShard.ShardFile({
            path: path,
        });
        shard.init(null);
    } catch (e) {
        fail_with_error(e, 'Could not open shard at path', path);
    }
    return shard;
}

function fail_with_message () {
    // join args with space, a la print/console.log
    var args = Array.prototype.slice.call(arguments);
    printerr(args.join(' '));
    System.exit(1);
}

function fail_with_error (e) {
    var args = Array.prototype.slice.call(arguments, 1);
    logError(e, e + ' ' + args.join(' '));
    System.exit(1);
}
<|endoftext|>


<commit_before>module.exports = function (broccoli) {
  var filterCoffeeScript = require('broccoli-coffee')
  var filterTemplates = require('broccoli-template')
  var uglifyJavaScript = require('broccoli-uglify-js')
  var compileES6 = require('broccoli-es6-concatenator')
  var pickFiles = require('broccoli-static-compiler')
  var env = require('broccoli-env').getEnv()

  function preprocess (tree) {
    tree = filterTemplates(tree, {
      extensions: ['hbs', 'handlebars'],
      compileFunction: 'Ember.Handlebars.compile'
    })
    tree = filterCoffeeScript(tree, {
      bare: true
    })
    return tree
  }

  var app = broccoli.makeTree('app')
  app = pickFiles(app, {
    srcDir: '/',
    destDir: 'appkit' // move under appkit namespace
  })
  app = preprocess(app)

  var tests = broccoli.makeTree('tests')
  tests = pickFiles(tests, {
    srcDir: '/',
    destDir: 'appkit/tests'
  })
  tests = preprocess(tests)

  var vendor = broccoli.makeTree('vendor')

  var sourceTrees = [app, vendor]
  if (env !== 'production') {
    sourceTrees.push(tests)
  }
  sourceTrees = sourceTrees.concat(broccoli.bowerTrees())

  var appAndDependencies = new broccoli.MergedTree(sourceTrees)

  applicationJs = compileES6(appAndDependencies, {
    loaderFile: 'loader.js',
    ignoredModules: [
      'resolver'
    ],
    inputFiles: [
      'appkit/**/*.js'
    ],
    legacyFilesToAppend: [
      'jquery.js',
      'handlebars.js',
      'ember.js',
      'ember-data.js',
      'ember-resolver.js'
    ],
    wrapInEval: env !== 'production',
    outputFile: '/assets/app.js'
  })

  if (env === 'production') {
    applicationJs = uglifyJavaScript(applicationJs, {
      // mangle: false,
      // compress: false
    })
  }

  var publicFiles = broccoli.makeTree('public')

  return [applicationJs, publicFiles]
}
<commit_msg>Add missing var<commit_after>module.exports = function (broccoli) {
  var filterCoffeeScript = require('broccoli-coffee')
  var filterTemplates = require('broccoli-template')
  var uglifyJavaScript = require('broccoli-uglify-js')
  var compileES6 = require('broccoli-es6-concatenator')
  var pickFiles = require('broccoli-static-compiler')
  var env = require('broccoli-env').getEnv()

  function preprocess (tree) {
    tree = filterTemplates(tree, {
      extensions: ['hbs', 'handlebars'],
      compileFunction: 'Ember.Handlebars.compile'
    })
    tree = filterCoffeeScript(tree, {
      bare: true
    })
    return tree
  }

  var app = broccoli.makeTree('app')
  app = pickFiles(app, {
    srcDir: '/',
    destDir: 'appkit' // move under appkit namespace
  })
  app = preprocess(app)

  var tests = broccoli.makeTree('tests')
  tests = pickFiles(tests, {
    srcDir: '/',
    destDir: 'appkit/tests'
  })
  tests = preprocess(tests)

  var vendor = broccoli.makeTree('vendor')

  var sourceTrees = [app, vendor]
  if (env !== 'production') {
    sourceTrees.push(tests)
  }
  sourceTrees = sourceTrees.concat(broccoli.bowerTrees())

  var appAndDependencies = new broccoli.MergedTree(sourceTrees)

  var applicationJs = compileES6(appAndDependencies, {
    loaderFile: 'loader.js',
    ignoredModules: [
      'resolver'
    ],
    inputFiles: [
      'appkit/**/*.js'
    ],
    legacyFilesToAppend: [
      'jquery.js',
      'handlebars.js',
      'ember.js',
      'ember-data.js',
      'ember-resolver.js'
    ],
    wrapInEval: env !== 'production',
    outputFile: '/assets/app.js'
  })

  if (env === 'production') {
    applicationJs = uglifyJavaScript(applicationJs, {
      // mangle: false,
      // compress: false
    })
  }

  var publicFiles = broccoli.makeTree('public')

  return [applicationJs, publicFiles]
}
<|endoftext|>


<commit_before>import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import {
  addFormInput,
  removeFormInput,
  updateFormInput,
  moveFormInput,
  mapArrayToObject,
  mapSnapToArray,
  getFullRecipeDataObject,
  getSearchDataObject,
  getFirebaseNewRecipeObject,
  addRecipeToStore,
  showTransition,
  hideTransition,
  showNotification,
  hideNotification,
  notify,
  setSearchFilter,
  setCurSeason,
  setSearchTerm,
  setCurUser,
  setHasRecipesData
} from './';

// this mock redux store will be used to test async action creators
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);

// this will mock native timer functions for the following tests
jest.useFakeTimers();

describe('mapArrayToObject', () => {
  test('should convert the passed array to an obj with indexes as keys', () => {
    const actual = mapArrayToObject(['woof', 'meow', 'gibber', 'grunt']);
    const expected = {
      0: 'woof',
      1: 'meow',
      2: 'gibber',
      3: 'grunt'
    };
    expect(actual).toEqual(expected);
  });
});

describe('mapSnapToArray', () => {
  test('should convert the passed object to an array of object, with keys as id & index', () => {
    const actual = mapSnapToArray({
      hoho: {
        type: '1',
        season: '0'
      },
      haha: {
        type: '2',
        season: '2'
      }
    });
    const expected = [
      {
        id: 'hoho',
        type: '1',
        season: '0',
        index: 0
      },
      {
        id: 'haha',
        season: '2',
        type: '2',
        index: 1
      }
    ];
    expect(actual).toEqual(expected);
  });

  test('should still work on an empty object', () => {
    const actual = mapSnapToArray({});
    const expected = [];
    expect(actual).toEqual(expected);
  });
});

// this function takes a recipe with 'ui' data and merges it
// with additional info needed in the database :
// author id, timestamp, mapped ingredients & steps
describe('getFullRecipeDataObject', () => {
  test('should merge the passed recipe with additionnal passed data', () => {
    const recipe = {
      a: 'b',
      ingredients: 'hi',
      steps: 'boo',
      c: 'd'
    };
    const author = 'hihuz';
    const stamp = new Date().getTime();
    const ingredients = ['hey', 'ho'];
    const steps = ['close eyes', 'sleep'];
    const actual = getFullRecipeDataObject({ recipe, author, stamp, ingredients, steps });
    const expected = {
      a: 'b',
      ingredients,
      steps,
      c: 'd',
      author,
      created: stamp,
      updated: stamp,
      rating: null
    };
    expect(actual).toEqual(expected);
  });
});

describe('getSearchDataObject', () => {
  test('should return an object with only search fields', () => {
    const recipe = {
      a: 'b',
      desc: 'hey',
      img: false,
      name: 'blah',
      season: '0',
      type: '1',
      ingredients: 'hi',
      c: 'd'
    };
    const stamp = new Date().getTime();
    const ingredients = ['hey', 'ho'];
    const actual = getSearchDataObject({ recipe, stamp, ingredients });
    const expected = {
      desc: 'hey',
      img: false,
      name: 'blah',
      season: '0',
      type: '1',
      ingredients,
      updated: stamp
    };
    expect(actual).toEqual(expected);
  });
});

describe('getFirebaseNewRecipeObject', () => {
  test('should return an object formatted for firebase updates', () => {
    const input = {
      key: 'hi',
      recipeData: 'test1',
      searchData: 'test2',
      userid: 'hihuz'
    };
    const actual = getFirebaseNewRecipeObject(input);
    const expected = {
      '/recipes/hi': 'test1',
      '/recipesSearch/hi': 'test2',
      '/recipeVotes/hi': null,
      '/userRecipes/hihuz/hi': true
    };
    expect(actual).toEqual(expected);
  });
});

describe('addRecipeToStore', () => {
  test('should return an ADD_RECIPE action with param as recipe prop', () => {
    const action = { type: 'ADD_RECIPE', recipe: 'test' };
    expect(addRecipeToStore('test')).toEqual(action);
  });

  test('should pass object param as prop too', () => {
    const action = { type: 'ADD_RECIPE', recipe: { a: 'b', c: 0 } };
    expect(addRecipeToStore({ a: 'b', c: 0 })).toEqual(action);
  });
});

describe('showTransition', () => {
  test('should return an SHOW_TRANSITION action with param as config prop', () => {
    const action = { type: 'SHOW_TRANSITION', config: 'boo' };
    expect(showTransition('boo')).toEqual(action);
  });

  test('should pass object param as prop too', () => {
    const action = { type: 'SHOW_TRANSITION', config: { a: 1, c: 'd' } };
    expect(showTransition({ a: 1, c: 'd' })).toEqual(action);
  });
});

describe('hideTransition', () => {
  test('should return a plain HIDE_TRANSITION action', () => {
    const action = { type: 'HIDE_TRANSITION' };
    expect(hideTransition()).toEqual(action);
  });
});

describe('showNotification', () => {
  test('should return an SHOW_NOTIFICATION action with msg and id params as props', () => {
    const action = { type: 'SHOW_NOTIFICATION', id: 123, msg: 'boo' };
    expect(showNotification({ id: 123, msg: 'boo' })).toEqual(action);
  });
});

describe('hideNotification', () => {
  test('should return a HIDE_NOTIFICATION action with id param as props', () => {
    const action = { type: 'HIDE_NOTIFICATION', id: 456 };
    expect(hideNotification(456)).toEqual(action);
  });
});

describe('notify', () => {
  test('should dispatch showNotification and hideNotification (after delay) actions', () => {
    const id = new Date().getTime();
    const expectedActions = [
      { type: 'SHOW_NOTIFICATION', msg: 'hello', id },
      { type: 'HIDE_NOTIFICATION', id }
    ];
    const store = mockStore({});
    store.dispatch(notify('hello', id));
    expect(store.getActions()).toEqual(expectedActions);
  });

  test('should call a setTimeout of 4000 on hideNotification', () => {
    notify('hello', new Date().getTime());
    expect(setTimeout.mock.calls.length).toBe(1);
    expect(setTimeout.mock.calls[0][1]).toBe(4000);
  });
});

describe('setSearchFilter', () => {
  test('should return a SET_*_FILTER action based on passed name and value 1', () => {
    const action = { type: 'SET_BOO_FILTER', value: 1 };
    expect(setSearchFilter({ name: 'boo', value: 1 })).toEqual(action);
  });

  test('should return a SET_*_FILTER action based on passed name and value 2', () => {
    const action = { type: 'SET_HI_FILTER', value: 'hu' };
    expect(setSearchFilter({ name: 'hi', value: 'hu' })).toEqual(action);
  });
});

describe('setCurSeason', () => {
  test('should return a SET_CUR_SEASON action with estimated season', () => {
    const curMonth = new Date().getMonth() + 1;
    let season;
    if (curMonth <= 3) {
      season = 1;
    } else if (curMonth >= 4 && curMonth <= 6) {
      season = 2;
    } else if (curMonth >= 7 && curMonth <= 9) {
      season = 3;
    } else if (curMonth >= 10) {
      season = 4;
    }
    const action = { type: 'SET_CUR_SEASON', season };
    expect(setCurSeason()).toEqual(action);
  });
});

describe('setSearchTerm', () => {
  test('should return an SET_SEARCH_TERM action with value params passed as searchTerm', () => {
    const action = { type: 'SET_SEARCH_TERM', searchTerm: 'test' };
    expect(setSearchTerm('test')).toEqual(action);
  });
});

describe('setCurUser', () => {
  test('should return an SET_CUR_USER action with params obj passed as id/sn', () => {
    const action = { type: 'SET_CUR_USER', id: 789, sn: 'hihuz' };
    expect(setCurUser({ id: 789, sn: 'hihuz' })).toEqual(action);
  });
});

describe('setHasRecipesData', () => {
  test('should return a plain SET_HAS_RECIPES_DATA action', () => {
    const action = { type: 'SET_HAS_RECIPES_DATA' };
    expect(setHasRecipesData()).toEqual(action);
  });
});

describe('addFormInput', () => {
  test('should return an ADD_ADDPAGE_INPUT action passing the name arg', () => {
    const name = 'boo';
    const action = { type: 'ADD_ADDPAGE_INPUT', name };
    expect(addFormInput({ name, type: 'add' })).toEqual(action);
  });

  test('should return an ADD_EDITPAGE_INPUT action passing the name arg', () => {
    const name = 'buu';
    const action = { type: 'ADD_EDITPAGE_INPUT', name };
    expect(addFormInput({ name, type: 'edit' })).toEqual(action);
  });

  test('should pass a recipeIndex param if provided', () => {
    const name = 'baa';
    const recipeIndex = 7;
    const action = { type: 'ADD_EDITPAGE_INPUT', name, recipeIndex };
    expect(addFormInput({ name, type: 'edit', recipeIndex })).toEqual(action);
  });
});

describe('removeFormInput', () => {
  test('should return a REMOVE_ADDPAGE_INPUT action with the correct index and name 1', () => {
    const index = 0;
    const name = 'kewkew';
    const action = { type: 'REMOVE_ADDPAGE_INPUT', index, name };
    expect(removeFormInput({ index, name, type: 'add' })).toEqual(action);
  });
  test('should return a REMOVE_ADDPAGE_INPUT action with the correct index and name 2', () => {
    const index = 2;
    const name = 'booboo';
    const action = { type: 'REMOVE_ADDPAGE_INPUT', index, name };
    expect(removeFormInput({ index, name, type: 'add' })).toEqual(action);
  });
  test('should convert the string provided as index to a number', () => {
    const name = 'hohoho';
    const action = { type: 'REMOVE_ADDPAGE_INPUT', index: 2, name };
    expect(removeFormInput({ index: '2', name, type: 'add' })).toEqual(action);
  });
  test('should return a REMOVE_EDITPAGE_INPUT action with the correct index and name', () => {
    const index = 8;
    const name = 'booboo';
    const action = { type: 'REMOVE_EDITPAGE_INPUT', index, name };
    expect(removeFormInput({ index, name, type: 'edit' })).toEqual(action);
  });
  test('should pass a recipeIndex param if provided', () => {
    const index = 8;
    const name = 'booboo';
    const recipeIndex = 95;
    const action = { type: 'REMOVE_EDITPAGE_INPUT', index, name, recipeIndex };
    expect(removeFormInput({ index, name, type: 'edit', recipeIndex })).toEqual(action);
  });
});

describe('updateFormInput', () => {
  test('should return an UPDATE_ADDPAGE_INPUT action with correct index/value/name 1', () => {
    const index = 0;
    const value = 'boo';
    const name = 'heyyy';
    const action = { type: 'UPDATE_ADDPAGE_INPUT', index, value, name };
    expect(updateFormInput({ index, value, name, type: 'add' })).toEqual(action);
  });
  test('should return an UPDATE_ADDPAGE_INPUT action with correct index/value/name 2', () => {
    const index = 3;
    const value = true;
    const name = 'hiii';
    const action = { type: 'UPDATE_ADDPAGE_INPUT', index, value, name };
    expect(updateFormInput({ index, value, name, type: 'add' })).toEqual(action);
  });
  test('should convert the string provided as index to a number', () => {
    const value = 'baa';
    const name = 'boo';
    const action = { type: 'UPDATE_ADDPAGE_INPUT', index: 3, value, name };
    expect(updateFormInput({ index: '3', value, name, type: 'add' })).toEqual(action);
  });
  test('should return an UPDATE_EDITPAGE_INPUT action with correct index/value/name 2', () => {
    const index = 3;
    const value = 123;
    const name = 'hooo';
    const action = { type: 'UPDATE_EDITPAGE_INPUT', index, value, name };
    expect(updateFormInput({ index, value, name, type: 'edit' })).toEqual(action);
  });
  test('should pass a recipeIndex param if provided', () => {
    const index = 1;
    const value = 45;
    const name = 'santa';
    const recipeIndex = 5;
    const action = { type: 'UPDATE_EDITPAGE_INPUT', index, value, name, recipeIndex };
    expect(updateFormInput({ index, value, name, type: 'edit', recipeIndex })).toEqual(action);
  });
});

describe('moveFormInput', () => {
  test('should return an MOVE_ADDPAGE_INPUT action with correct index/name/dir 1', () => {
    const index = 1;
    const name = 'ingredients';
    const dir = 'up';
    const action = { type: 'MOVE_ADDPAGE_INPUT', index, name, dir };
    expect(moveFormInput({ index, name, dir, type: 'add' })).toEqual(action);
  });
  test('should return an MOVE_ADDPAGE_INPUT action with correct index/name/dir 2', () => {
    const index = 2;
    const name = 'steps';
    const dir = 'down';
    const action = { type: 'MOVE_ADDPAGE_INPUT', index, name, dir };
    expect(moveFormInput({ index, name, dir, type: 'add' })).toEqual(action);
  });
  test('should return an MOVE_EDITPAGE_INPUT action with correct index/name/dir', () => {
    const index = 3;
    const name = 'steps';
    const dir = 'down';
    const action = { type: 'MOVE_EDITPAGE_INPUT', index, name, dir };
    expect(moveFormInput({ index, name, dir, type: 'edit' })).toEqual(action);
  });
  test('should pass a recipeIndex param if provided', () => {
    const index = 4;
    const name = 'steps';
    const dir = 'up';
    const recipeIndex = 9;
    const action = { type: 'MOVE_EDITPAGE_INPUT', index, name, dir, recipeIndex };
    expect(moveFormInput({ index, name, dir, type: 'edit', recipeIndex })).toEqual(action);
  });
  test('should convert the string provided as index to a number', () => {
    const action = { type: 'MOVE_ADDPAGE_INPUT', index: 3, name: 'hi', dir: 'up' };
    expect(moveFormInput({ index: '3', name: 'hi', dir: 'up', type: 'add' })).toEqual(action);
  });
});
<commit_msg>Update notification action tests<commit_after>import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import {
  addFormInput,
  removeFormInput,
  updateFormInput,
  moveFormInput,
  mapArrayToObject,
  mapSnapToArray,
  getFullRecipeDataObject,
  getSearchDataObject,
  getFirebaseNewRecipeObject,
  addRecipeToStore,
  showTransition,
  hideTransition,
  showNotification,
  hideNotification,
  notify,
  setSearchFilter,
  setCurSeason,
  setSearchTerm,
  setCurUser,
  setHasRecipesData
} from './';

// this mock redux store will be used to test async action creators
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);

// this will mock native timer functions for the following tests
jest.useFakeTimers();

describe('mapArrayToObject', () => {
  test('should convert the passed array to an obj with indexes as keys', () => {
    const actual = mapArrayToObject(['woof', 'meow', 'gibber', 'grunt']);
    const expected = {
      0: 'woof',
      1: 'meow',
      2: 'gibber',
      3: 'grunt'
    };
    expect(actual).toEqual(expected);
  });
});

describe('mapSnapToArray', () => {
  test('should convert the passed object to an array of object, with keys as id & index', () => {
    const actual = mapSnapToArray({
      hoho: {
        type: '1',
        season: '0'
      },
      haha: {
        type: '2',
        season: '2'
      }
    });
    const expected = [
      {
        id: 'hoho',
        type: '1',
        season: '0',
        index: 0
      },
      {
        id: 'haha',
        season: '2',
        type: '2',
        index: 1
      }
    ];
    expect(actual).toEqual(expected);
  });

  test('should still work on an empty object', () => {
    const actual = mapSnapToArray({});
    const expected = [];
    expect(actual).toEqual(expected);
  });
});

// this function takes a recipe with 'ui' data and merges it
// with additional info needed in the database :
// author id, timestamp, mapped ingredients & steps
describe('getFullRecipeDataObject', () => {
  test('should merge the passed recipe with additionnal passed data', () => {
    const recipe = {
      a: 'b',
      ingredients: 'hi',
      steps: 'boo',
      c: 'd'
    };
    const author = 'hihuz';
    const stamp = new Date().getTime();
    const ingredients = ['hey', 'ho'];
    const steps = ['close eyes', 'sleep'];
    const actual = getFullRecipeDataObject({ recipe, author, stamp, ingredients, steps });
    const expected = {
      a: 'b',
      ingredients,
      steps,
      c: 'd',
      author,
      created: stamp,
      updated: stamp,
      rating: null
    };
    expect(actual).toEqual(expected);
  });
});

describe('getSearchDataObject', () => {
  test('should return an object with only search fields', () => {
    const recipe = {
      a: 'b',
      desc: 'hey',
      img: false,
      name: 'blah',
      season: '0',
      type: '1',
      ingredients: 'hi',
      c: 'd'
    };
    const stamp = new Date().getTime();
    const ingredients = ['hey', 'ho'];
    const actual = getSearchDataObject({ recipe, stamp, ingredients });
    const expected = {
      desc: 'hey',
      img: false,
      name: 'blah',
      season: '0',
      type: '1',
      ingredients,
      updated: stamp
    };
    expect(actual).toEqual(expected);
  });
});

describe('getFirebaseNewRecipeObject', () => {
  test('should return an object formatted for firebase updates', () => {
    const input = {
      key: 'hi',
      recipeData: 'test1',
      searchData: 'test2',
      userid: 'hihuz'
    };
    const actual = getFirebaseNewRecipeObject(input);
    const expected = {
      '/recipes/hi': 'test1',
      '/recipesSearch/hi': 'test2',
      '/recipeVotes/hi': null,
      '/userRecipes/hihuz/hi': true
    };
    expect(actual).toEqual(expected);
  });
});

describe('addRecipeToStore', () => {
  test('should return an ADD_RECIPE action with param as recipe prop', () => {
    const action = { type: 'ADD_RECIPE', recipe: 'test' };
    expect(addRecipeToStore('test')).toEqual(action);
  });

  test('should pass object param as prop too', () => {
    const action = { type: 'ADD_RECIPE', recipe: { a: 'b', c: 0 } };
    expect(addRecipeToStore({ a: 'b', c: 0 })).toEqual(action);
  });
});

describe('showTransition', () => {
  test('should return an SHOW_TRANSITION action with param as config prop', () => {
    const action = { type: 'SHOW_TRANSITION', config: 'boo' };
    expect(showTransition('boo')).toEqual(action);
  });

  test('should pass object param as prop too', () => {
    const action = { type: 'SHOW_TRANSITION', config: { a: 1, c: 'd' } };
    expect(showTransition({ a: 1, c: 'd' })).toEqual(action);
  });
});

describe('hideTransition', () => {
  test('should return a plain HIDE_TRANSITION action', () => {
    const action = { type: 'HIDE_TRANSITION' };
    expect(hideTransition()).toEqual(action);
  });
});

describe('showNotification', () => {
  test('should return an SHOW_NOTIFICATION action with msg/id/notifType params as props', () => {
    const action = { type: 'SHOW_NOTIFICATION', id: 123, msg: 'boo', notifType: 'success' };
    expect(showNotification({ id: 123, msg: 'boo', notifType: 'success' })).toEqual(action);
  });
});

describe('hideNotification', () => {
  test('should return a HIDE_NOTIFICATION action with id param as props', () => {
    const action = { type: 'HIDE_NOTIFICATION', id: 456 };
    expect(hideNotification(456)).toEqual(action);
  });
});

describe('notify', () => {
  test('should call a setTimeout of 4000 on hideNotification', () => {
    notify(() => {}, {
      msg: 'hello',
      id: new Date().getTime(),
      notifType: 'warn'
    });
    expect(setTimeout.mock.calls.length).toBe(1);
    expect(setTimeout.mock.calls[0][1]).toBe(4000);
  });

  test('should dispatch showNotification and hideNotification (after delay) actions', () => {
    const id = new Date().getTime();
    const msg = 'hello';
    const notifType = 'error';
    const expectedActions = [
      { type: 'SHOW_NOTIFICATION', msg, id, notifType },
      { type: 'HIDE_NOTIFICATION', id }
    ];
    const store = mockStore({});
    notify(store.dispatch, { msg, id, notifType });
    setTimeout(() => {
      expect(store.getActions()).toEqual(expectedActions)
    },10);
  });
});

describe('setSearchFilter', () => {
  test('should return a SET_*_FILTER action based on passed name and value 1', () => {
    const action = { type: 'SET_BOO_FILTER', value: 1 };
    expect(setSearchFilter({ name: 'boo', value: 1 })).toEqual(action);
  });

  test('should return a SET_*_FILTER action based on passed name and value 2', () => {
    const action = { type: 'SET_HI_FILTER', value: 'hu' };
    expect(setSearchFilter({ name: 'hi', value: 'hu' })).toEqual(action);
  });
});

describe('setCurSeason', () => {
  test('should return a SET_CUR_SEASON action with estimated season', () => {
    const curMonth = new Date().getMonth() + 1;
    let season;
    if (curMonth <= 3) {
      season = 1;
    } else if (curMonth >= 4 && curMonth <= 6) {
      season = 2;
    } else if (curMonth >= 7 && curMonth <= 9) {
      season = 3;
    } else if (curMonth >= 10) {
      season = 4;
    }
    const action = { type: 'SET_CUR_SEASON', season };
    expect(setCurSeason()).toEqual(action);
  });
});

describe('setSearchTerm', () => {
  test('should return an SET_SEARCH_TERM action with value params passed as searchTerm', () => {
    const action = { type: 'SET_SEARCH_TERM', searchTerm: 'test' };
    expect(setSearchTerm('test')).toEqual(action);
  });
});

describe('setCurUser', () => {
  test('should return an SET_CUR_USER action with params obj passed as id/sn', () => {
    const action = { type: 'SET_CUR_USER', id: 789, sn: 'hihuz' };
    expect(setCurUser({ id: 789, sn: 'hihuz' })).toEqual(action);
  });
});

describe('setHasRecipesData', () => {
  test('should return a plain SET_HAS_RECIPES_DATA action', () => {
    const action = { type: 'SET_HAS_RECIPES_DATA' };
    expect(setHasRecipesData()).toEqual(action);
  });
});

describe('addFormInput', () => {
  test('should return an ADD_ADDPAGE_INPUT action passing the name arg', () => {
    const name = 'boo';
    const action = { type: 'ADD_ADDPAGE_INPUT', name };
    expect(addFormInput({ name, type: 'add' })).toEqual(action);
  });

  test('should return an ADD_EDITPAGE_INPUT action passing the name arg', () => {
    const name = 'buu';
    const action = { type: 'ADD_EDITPAGE_INPUT', name };
    expect(addFormInput({ name, type: 'edit' })).toEqual(action);
  });

  test('should pass a recipeIndex param if provided', () => {
    const name = 'baa';
    const recipeIndex = 7;
    const action = { type: 'ADD_EDITPAGE_INPUT', name, recipeIndex };
    expect(addFormInput({ name, type: 'edit', recipeIndex })).toEqual(action);
  });
});

describe('removeFormInput', () => {
  test('should return a REMOVE_ADDPAGE_INPUT action with the correct index and name 1', () => {
    const index = 0;
    const name = 'kewkew';
    const action = { type: 'REMOVE_ADDPAGE_INPUT', index, name };
    expect(removeFormInput({ index, name, type: 'add' })).toEqual(action);
  });
  test('should return a REMOVE_ADDPAGE_INPUT action with the correct index and name 2', () => {
    const index = 2;
    const name = 'booboo';
    const action = { type: 'REMOVE_ADDPAGE_INPUT', index, name };
    expect(removeFormInput({ index, name, type: 'add' })).toEqual(action);
  });
  test('should convert the string provided as index to a number', () => {
    const name = 'hohoho';
    const action = { type: 'REMOVE_ADDPAGE_INPUT', index: 2, name };
    expect(removeFormInput({ index: '2', name, type: 'add' })).toEqual(action);
  });
  test('should return a REMOVE_EDITPAGE_INPUT action with the correct index and name', () => {
    const index = 8;
    const name = 'booboo';
    const action = { type: 'REMOVE_EDITPAGE_INPUT', index, name };
    expect(removeFormInput({ index, name, type: 'edit' })).toEqual(action);
  });
  test('should pass a recipeIndex param if provided', () => {
    const index = 8;
    const name = 'booboo';
    const recipeIndex = 95;
    const action = { type: 'REMOVE_EDITPAGE_INPUT', index, name, recipeIndex };
    expect(removeFormInput({ index, name, type: 'edit', recipeIndex })).toEqual(action);
  });
});

describe('updateFormInput', () => {
  test('should return an UPDATE_ADDPAGE_INPUT action with correct index/value/name 1', () => {
    const index = 0;
    const value = 'boo';
    const name = 'heyyy';
    const action = { type: 'UPDATE_ADDPAGE_INPUT', index, value, name };
    expect(updateFormInput({ index, value, name, type: 'add' })).toEqual(action);
  });
  test('should return an UPDATE_ADDPAGE_INPUT action with correct index/value/name 2', () => {
    const index = 3;
    const value = true;
    const name = 'hiii';
    const action = { type: 'UPDATE_ADDPAGE_INPUT', index, value, name };
    expect(updateFormInput({ index, value, name, type: 'add' })).toEqual(action);
  });
  test('should convert the string provided as index to a number', () => {
    const value = 'baa';
    const name = 'boo';
    const action = { type: 'UPDATE_ADDPAGE_INPUT', index: 3, value, name };
    expect(updateFormInput({ index: '3', value, name, type: 'add' })).toEqual(action);
  });
  test('should return an UPDATE_EDITPAGE_INPUT action with correct index/value/name 2', () => {
    const index = 3;
    const value = 123;
    const name = 'hooo';
    const action = { type: 'UPDATE_EDITPAGE_INPUT', index, value, name };
    expect(updateFormInput({ index, value, name, type: 'edit' })).toEqual(action);
  });
  test('should pass a recipeIndex param if provided', () => {
    const index = 1;
    const value = 45;
    const name = 'santa';
    const recipeIndex = 5;
    const action = { type: 'UPDATE_EDITPAGE_INPUT', index, value, name, recipeIndex };
    expect(updateFormInput({ index, value, name, type: 'edit', recipeIndex })).toEqual(action);
  });
});

describe('moveFormInput', () => {
  test('should return an MOVE_ADDPAGE_INPUT action with correct index/name/dir 1', () => {
    const index = 1;
    const name = 'ingredients';
    const dir = 'up';
    const action = { type: 'MOVE_ADDPAGE_INPUT', index, name, dir };
    expect(moveFormInput({ index, name, dir, type: 'add' })).toEqual(action);
  });
  test('should return an MOVE_ADDPAGE_INPUT action with correct index/name/dir 2', () => {
    const index = 2;
    const name = 'steps';
    const dir = 'down';
    const action = { type: 'MOVE_ADDPAGE_INPUT', index, name, dir };
    expect(moveFormInput({ index, name, dir, type: 'add' })).toEqual(action);
  });
  test('should return an MOVE_EDITPAGE_INPUT action with correct index/name/dir', () => {
    const index = 3;
    const name = 'steps';
    const dir = 'down';
    const action = { type: 'MOVE_EDITPAGE_INPUT', index, name, dir };
    expect(moveFormInput({ index, name, dir, type: 'edit' })).toEqual(action);
  });
  test('should pass a recipeIndex param if provided', () => {
    const index = 4;
    const name = 'steps';
    const dir = 'up';
    const recipeIndex = 9;
    const action = { type: 'MOVE_EDITPAGE_INPUT', index, name, dir, recipeIndex };
    expect(moveFormInput({ index, name, dir, type: 'edit', recipeIndex })).toEqual(action);
  });
  test('should convert the string provided as index to a number', () => {
    const action = { type: 'MOVE_ADDPAGE_INPUT', index: 3, name: 'hi', dir: 'up' };
    expect(moveFormInput({ index: '3', name: 'hi', dir: 'up', type: 'add' })).toEqual(action);
  });
});
<|endoftext|>


<commit_before>// # Surrounds given text with Markdown syntax

/*global $, window, CodeMirror, Showdown, moment */
(function () {
    "use strict";
    var Markdown = {
        init : function (options, elem) {
            var self = this;
            self.elem = elem;

            self.style = (typeof options === 'string') ? options : options.style;

            self.options = $.extend({}, CodeMirror.prototype.addMarkdown.options, options);

            self.replace();
        },
        replace: function () {
            var text = this.elem.getSelection(), pass = true, md, cursor, line, word, letterCount, converter;
            switch (this.style) {
            case "h1":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "# " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 2);
                pass = false;
                break;
            case "h2":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "## " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 3);
                pass = false;
                break;
            case "h3":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "### " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 4);
                pass = false;
                break;
            case "h4":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "#### " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 5);
                pass = false;
                break;
            case "h5":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "##### " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 6);
                pass = false;
                break;
            case "h6":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "###### " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 7);
                pass = false;
                break;
            case "link":
                md = this.options.syntax.link.replace('$1', text);
                this.elem.replaceSelection(md, "end");
                cursor = this.elem.getCursor();
                this.elem.setSelection({line: cursor.line, ch: cursor.ch - 8}, {line: cursor.line, ch: cursor.ch - 1});
                pass = false;
                break;
            case "image":
                md = this.options.syntax.image.replace('$1', text);
                this.elem.replaceSelection(md, "end");
                cursor = this.elem.getCursor();
                this.elem.setSelection({line: cursor.line, ch: cursor.ch - 8}, {line: cursor.line, ch: cursor.ch - 1});
                pass = false;
                break;
            case "uppercase":
                md = text.toLocaleUpperCase();
                break;
            case "lowercase":
                md = text.toLocaleLowerCase();
                break;
            case "titlecase":
                md = text.toTitleCase();
                break;
            case "selectword":
                cursor = this.elem.getCursor();
                word = this.elem.getTokenAt(cursor);
                if (!/\w$/g.test(word.string)) {
                    this.elem.setSelection({line: cursor.line, ch: word.start}, {line: cursor.line, ch: word.end - 1});
                } else {
                    this.elem.setSelection({line: cursor.line, ch: word.start}, {line: cursor.line, ch: word.end});
                }
                break;
            case "copyHTML":
                converter = new Showdown.converter();
                if (text) {
                    md = converter.makeHtml(text);
                } else {
                    md = converter.makeHtml(this.elem.getValue());
                }

                $(".modal-copyToHTML-content").text(md).selectText();
                $(".js-modal").center();
                pass = false;
                break;
            case "list":
                md = text.replace(/^(\s*)(\w\W*)/gm, "$1* $2");
                this.elem.replaceSelection(md, "end");
                pass = false;
                break;
            case "currentDate":
                md = moment(new Date()).format("D MMMM YYYY");
                break;
            default:
                if (this.options.syntax[this.style]) {
                    md = this.options.syntax[this.style].replace('$1', text);
                }
            }
            if (pass && md) {
                this.elem.replaceSelection(md, "end");
                if (!text) {
                    letterCount = md.length;
                    cursor = this.elem.getCursor();
                    this.elem.setCursor({line: cursor.line, ch: cursor.ch - (letterCount / 2)});
                }
            }
        }
    };

    CodeMirror.prototype.addMarkdown = function (options) {
        var markdown = Object.create(Markdown);
        markdown.init(options, this);
    };

    CodeMirror.prototype.addMarkdown.options = {
        style: null,
        syntax: {
            bold: "**$1**",
            italic: "*$1*",
            strike: "~~$1~~",
            code: "`$1`",
            link: "[$1](http://)",
            image: "![$1](http://)",
            blockquote: "> $1"
        }
    };

}());<commit_msg>Improved Image keyboard shortcut<commit_after>// # Surrounds given text with Markdown syntax

/*global $, window, CodeMirror, Showdown, moment */
(function () {
    "use strict";
    var Markdown = {
        init : function (options, elem) {
            var self = this;
            self.elem = elem;

            self.style = (typeof options === 'string') ? options : options.style;

            self.options = $.extend({}, CodeMirror.prototype.addMarkdown.options, options);

            self.replace();
        },
        replace: function () {
            var text = this.elem.getSelection(), pass = true, md, cursor, line, word, letterCount, converter;
            switch (this.style) {
            case "h1":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "# " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 2);
                pass = false;
                break;
            case "h2":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "## " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 3);
                pass = false;
                break;
            case "h3":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "### " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 4);
                pass = false;
                break;
            case "h4":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "#### " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 5);
                pass = false;
                break;
            case "h5":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "##### " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 6);
                pass = false;
                break;
            case "h6":
                cursor = this.elem.getCursor();
                line = this.elem.getLine(cursor.line);
                this.elem.setLine(cursor.line, "###### " + line);
                this.elem.setCursor(cursor.line, cursor.ch + 7);
                pass = false;
                break;
            case "link":
                md = this.options.syntax.link.replace('$1', text);
                this.elem.replaceSelection(md, "end");
                cursor = this.elem.getCursor();
                this.elem.setSelection({line: cursor.line, ch: cursor.ch - 8}, {line: cursor.line, ch: cursor.ch - 1});
                pass = false;
                break;
            case "image":
                cursor = this.elem.getCursor();
                md = this.options.syntax.image.replace('$1', text);
                if (this.elem.getLine(cursor.line) !== "") {
                    md = "\n\n" + md;
                }
                this.elem.replaceSelection(md, "end");
                cursor = this.elem.getCursor();
                this.elem.setSelection({line: cursor.line, ch: cursor.ch - 8}, {line: cursor.line, ch: cursor.ch - 1});
                pass = false;
                break;
            case "uppercase":
                md = text.toLocaleUpperCase();
                break;
            case "lowercase":
                md = text.toLocaleLowerCase();
                break;
            case "titlecase":
                md = text.toTitleCase();
                break;
            case "selectword":
                cursor = this.elem.getCursor();
                word = this.elem.getTokenAt(cursor);
                if (!/\w$/g.test(word.string)) {
                    this.elem.setSelection({line: cursor.line, ch: word.start}, {line: cursor.line, ch: word.end - 1});
                } else {
                    this.elem.setSelection({line: cursor.line, ch: word.start}, {line: cursor.line, ch: word.end});
                }
                break;
            case "copyHTML":
                converter = new Showdown.converter();
                if (text) {
                    md = converter.makeHtml(text);
                } else {
                    md = converter.makeHtml(this.elem.getValue());
                }

                $(".modal-copyToHTML-content").text(md).selectText();
                $(".js-modal").center();
                pass = false;
                break;
            case "list":
                md = text.replace(/^(\s*)(\w\W*)/gm, "$1* $2");
                this.elem.replaceSelection(md, "end");
                pass = false;
                break;
            case "currentDate":
                md = moment(new Date()).format("D MMMM YYYY");
                break;
            default:
                if (this.options.syntax[this.style]) {
                    md = this.options.syntax[this.style].replace('$1', text);
                }
            }
            if (pass && md) {
                this.elem.replaceSelection(md, "end");
                if (!text) {
                    letterCount = md.length;
                    cursor = this.elem.getCursor();
                    this.elem.setCursor({line: cursor.line, ch: cursor.ch - (letterCount / 2)});
                }
            }
        }
    };

    CodeMirror.prototype.addMarkdown = function (options) {
        var markdown = Object.create(Markdown);
        markdown.init(options, this);
    };

    CodeMirror.prototype.addMarkdown.options = {
        style: null,
        syntax: {
            bold: "**$1**",
            italic: "*$1*",
            strike: "~~$1~~",
            code: "`$1`",
            link: "[$1](http://)",
            image: "![$1](http://)",
            blockquote: "> $1"
        }
    };

}());<|endoftext|>


<commit_before>if (PDFAnnotate.default) {
  PDFAnnotate = PDFAnnotate.default;
}

let currentTool;

let documentId = '';
let PAGE_HEIGHT;
window.RENDER_OPTIONS = {
    documentId,
    userId: "",
    pdfDocument: null,
    scale: parseFloat(localStorage.getItem('scale')) || 1,
    rotate: parseInt(localStorage.getItem('rotate')) || 0
};
window.GENERAL_INFORMATION = {
    grader_id: "",
    user_id: "",
    gradeable_id: "",
    file_name: "",
}

pdfjsLib.GlobalWorkerOptions.workerSrc = 'vendor/pdfjs/pdf.worker.min.js';

let NUM_PAGES = 0;

//For the student popup window, buildURL doesn't work because the context switched. Therefore, we need to pass in the url
//as a parameter.
function render_student(gradeable_id, user_id, file_name, pdf_url){
    render(gradeable_id, user_id, "", file_name, 1, pdf_url)
}

function render(gradeable_id, user_id, grader_id, file_name, page_num, url = "") {
    window.GENERAL_INFORMATION = {
        grader_id: grader_id,
        user_id: user_id,
        gradeable_id: gradeable_id,
        file_name: file_name
    }
    window.RENDER_OPTIONS.documentId = file_name;
    //TODO: Duplicate user_id in both RENDER_OPTIONS and GENERAL_INFORMATION, also grader_id = user_id in this context.
    window.RENDER_OPTIONS.userId = grader_id;
    if(url === ""){
        url = buildUrl({'component': 'misc', 'page': 'base64_encode_pdf'});
    }
    $.ajax({
        type: 'POST',
        url: url,
        data: {
            gradeable_id: gradeable_id,
            user_id: user_id,
            filename: file_name
        },
        success: (data) => {
            PDFAnnotate.setStoreAdapter(new PDFAnnotate.LocalUserStoreAdapter(GENERAL_INFORMATION.grader_id));
            // documentId = file_name;

            let pdfData;
            try {
                pdfData = JSON.parse(data);
                pdfData = atob(pdfData);
            } catch (err){
                alert("Something went wrong, please try again later.");
            }
            pdfjsLib.getDocument({data: pdfData}).then((pdf) => {
                window.RENDER_OPTIONS.pdfDocument = pdf;
                let viewer = document.getElementById('viewer');
                $(viewer).on('touchstart touchmove', function(e){
                    //Let touchscreen work
                    if(currentTool == "pen" || currentTool == "text"){
                        e.preventDefault();
                    }
                });
                $("a[value='zoomcustom']").text(parseInt(window.RENDER_OPTIONS.scale * 100) + "%");
                viewer.innerHTML = '';
                NUM_PAGES = pdf.numPages;
                for (let i=0; i<NUM_PAGES; i++) {
                    let page = PDFAnnotate.UI.createPage(i+1);
                    viewer.appendChild(page);
                    let page_id = i+1;
                    PDFAnnotate.UI.renderPage(page_id, window.RENDER_OPTIONS).then(([pdfPage, annotations]) => {
                        let viewport = pdfPage.getViewport(window.RENDER_OPTIONS.scale, window.RENDER_OPTIONS.rotate);
                        PAGE_HEIGHT = viewport.height;
                        if(i == page_num) {
                            $('#file_content').animate({scrollTop: page_num * PAGE_HEIGHT}, 500);
                        }
                    }).then(function(){
                        document.getElementById('pageContainer'+page_id).addEventListener('mousedown', function(){
                            //Makes sure the panel don't move when writing on it.
                            $("#submission_browser").draggable('disable');
                            let selected = $(".tool-selected");
                            if(selected.length != 0 && $(selected[0]).attr('value') != 'cursor'){
                                $("#save_status").text("Changes not saved");
                                $("#save_status").css("color", "red");
                            }
                        });
                        document.getElementById('pageContainer'+page_id).addEventListener('mouseup', function(){
                            $("#submission_browser").draggable('enable');
                        });
                    });
                }
            });
        }
    });
}

// TODO: Stretch goal, find a better solution to load/unload
// annotation. Maybe use session storage?
$(window).on('unload', () => {
  for (let i = 0; i < localStorage.length; i++) {
    if (localStorage.key(i).includes('annotations')) {
      localStorage.removeItem(localStorage.key(i));
    }
  }
});
<commit_msg>[Bugfix] Initial PDF scroll in TA manual grading (#3825)<commit_after>if (PDFAnnotate.default) {
  PDFAnnotate = PDFAnnotate.default;
}

let currentTool;

let documentId = '';
let PAGE_HEIGHT;
window.RENDER_OPTIONS = {
    documentId,
    userId: "",
    pdfDocument: null,
    scale: parseFloat(localStorage.getItem('scale')) || 1,
    rotate: parseInt(localStorage.getItem('rotate')) || 0
};
window.GENERAL_INFORMATION = {
    grader_id: "",
    user_id: "",
    gradeable_id: "",
    file_name: "",
}

pdfjsLib.GlobalWorkerOptions.workerSrc = 'vendor/pdfjs/pdf.worker.min.js';

let NUM_PAGES = 0;

//For the student popup window, buildURL doesn't work because the context switched. Therefore, we need to pass in the url
//as a parameter.
function render_student(gradeable_id, user_id, file_name, pdf_url){
    render(gradeable_id, user_id, "", file_name, 1, pdf_url)
}

function render(gradeable_id, user_id, grader_id, file_name, page_num, url = "") {
    window.GENERAL_INFORMATION = {
        grader_id: grader_id,
        user_id: user_id,
        gradeable_id: gradeable_id,
        file_name: file_name
    }
    window.RENDER_OPTIONS.documentId = file_name;
    //TODO: Duplicate user_id in both RENDER_OPTIONS and GENERAL_INFORMATION, also grader_id = user_id in this context.
    window.RENDER_OPTIONS.userId = grader_id;
    if(url === ""){
        url = buildUrl({'component': 'misc', 'page': 'base64_encode_pdf'});
    }
    $.ajax({
        type: 'POST',
        url: url,
        data: {
            gradeable_id: gradeable_id,
            user_id: user_id,
            filename: file_name
        },
        success: (data) => {
            PDFAnnotate.setStoreAdapter(new PDFAnnotate.LocalUserStoreAdapter(GENERAL_INFORMATION.grader_id));
            // documentId = file_name;

            let pdfData;
            try {
                pdfData = JSON.parse(data);
                pdfData = atob(pdfData);
            } catch (err){
                alert("Something went wrong, please try again later.");
            }
            pdfjsLib.getDocument({data: pdfData}).then((pdf) => {
                window.RENDER_OPTIONS.pdfDocument = pdf;
                let viewer = document.getElementById('viewer');
                $(viewer).on('touchstart touchmove', function(e){
                    //Let touchscreen work
                    if(currentTool == "pen" || currentTool == "text"){
                        e.preventDefault();
                    }
                });
                $("a[value='zoomcustom']").text(parseInt(window.RENDER_OPTIONS.scale * 100) + "%");
                viewer.innerHTML = '';
                NUM_PAGES = pdf.numPages;
                for (let i=0; i<NUM_PAGES; i++) {
                    let page = PDFAnnotate.UI.createPage(i+1);
                    viewer.appendChild(page);
                    let page_id = i+1;
                    PDFAnnotate.UI.renderPage(page_id, window.RENDER_OPTIONS).then(function(){
                        if (i == page_num) {
                            // scroll to page on load
                            let zoom = parseInt(localStorage.getItem('scale')) || 1;
                            let page1 = $(".page").filter(":first");
                            //get css attr, remove 'px' :
                            let page_height = parseInt(page1.css("height").slice(0, -2));
                            let page_margin_top = parseInt(page1.css("margin-top").slice(0, -2));
                            let page_margin_bot = parseInt(page1.css("margin-bottom").slice(0, -2));
                            // assuming margin-top < margin-bot: it overlaps on all pages but 1st so we add it once
                            let scrollY = zoom*(page_num)*(page_height+page_margin_bot)+page_margin_top;
                            $('#file_content').animate({scrollTop: scrollY}, 500);
                        }
                        document.getElementById('pageContainer'+page_id).addEventListener('mousedown', function(){
                            //Makes sure the panel don't move when writing on it.
                            $("#submission_browser").draggable('disable');
                            let selected = $(".tool-selected");
                            if(selected.length != 0 && $(selected[0]).attr('value') != 'cursor'){
                                $("#save_status").text("Changes not saved");
                                $("#save_status").css("color", "red");
                            }
                        });
                        document.getElementById('pageContainer'+page_id).addEventListener('mouseup', function(){
                            $("#submission_browser").draggable('enable');
                        });
                    });
                }
            });
        }
    });
}

// TODO: Stretch goal, find a better solution to load/unload
// annotation. Maybe use session storage?
$(window).on('unload', () => {
  for (let i = 0; i < localStorage.length; i++) {
    if (localStorage.key(i).includes('annotations')) {
      localStorage.removeItem(localStorage.key(i));
    }
  }
});
<|endoftext|>


<commit_before>var cfg = require('./config.json')
var exec = require('child_process').exec;

function runcmd(cmd, msg, bot) {
    var nick = msg.nick;
    var cloak = msg.host;
    var chan = msg.args[0];
    var text = msg.args[1].split(" ");
    var cmd = text[1].toLowerCase();

    if(chan == cfg.nick) {
        chan = nick;
    }
    switch(cmd) {
    case "help":
        bot.say(chan, "Normal Commands: [{0}], Trusted Commands: [{1}], Op Commands: [{2}], Onwer Commands: [{3}]".format(cfg.cmd.join(", "), cfg.trusted.cmd.join(", "), cfg.op.cmd.join(", "), cfg.owner.cmd.join(", ")));
        break;
    case "list":
        bot.say(chan, "Normal Commands: [{0}], Trusted Commands: [{1}], Op Commands: [{2}], Onwer Commands: [{3}]".format(cfg.cmd.join(", "), cfg.trusted.cmd.join(", "), cfg.op.cmd.join(", "), cfg.owner.cmd.join(", ")));
        break;
    case "trustedcheck":
        bot.say(chan, nick + ": Yes! You are trusted!");
        break;
    case "tcheck":
        bot.say(chan, nick + ": Yes! You are trusted!");
        break;
    case "opcheck":
        bot.say(chan, nick + ": Yes! You are an op in {0}!".format(chan));
        break;
    case "ocheck":
        bot.say(chan, nick + ": Yes! You are my owner!");
        break;
    case "ownercheck":
        bot.say(chan, nick + ": Yes! You are my owner!");
        break;
    case "msg":
        if(text[2].split(",").length == 1 && text[2][0] == "#") {
            var send = msg.args[1].replace("{0}: msg {1} ".format(cfg.nick, text[2]), "");
            bot.say(text[2], send);
        }
        break;
    case "act":
        if(text[2].split(",").length == 1 && text[2][0] == "#") {
            var send = msg.args[1].replace("{0}: act {1} ".format(cfg.nick, text[2]), "");
            bot.action(text[2], send);
        }
        break;
    case "op":
        bot.send('MODE', chan, '+o', nick);
        break;
    case "deop":
        bot.send('MODE', chan, '-o', nick);
        break;
    case "mode":
        var send = msg.args[1].replace("{0}: mode".format(cfg.nick) , "");
        bot.conn.write("MODE " + chan + send + "\n");
        break;
    case "do":
        var send = msg.args[1].replace("{0}: do".format(cfg.nick) , "");
        console.log(send);
        bot.say(cfg.logchan, send)
        bot.conn.write(send + "\n");
        break;
    case "ddate":
        exec("ddate", function(error, stdout, stderr) { bot.say(chan, stdout); });
        break;
    case "memo":
        var memo = msg.args[1].replace(cfg.nick + ": memo " + text[2] + " ", "");
        if(text[2].split(",").length == 1) {
            bot.say(chan, "okay, sending your memo");
            exec("echo {0},{1},{2} >> memo.txt".format(text[2], nick, memo),
                function(error, stdout, stderr) {
                    var send = "[{0}] {1} sends a memo to {2}: {3}".format(chan, nick, text[2], memo);
                    console.log(send);
                    bot.say(cfg.logchan, send);
                }
            );
        }
        break;
    case "oracle":
        var morgan = fs.readFileSync('./morganstarot.txt').toString().split("\n");
        bot.say(chan, morgan[Math.floor(Math.random() * morgan.length)]);
        break;
    case "coffee":
        if(text.length == 2) { bot.action(chan, "hands {0} a steaming cup of delicious coffee".format(nick)); }
        else { bot.action(chan, "hands {0} a steaming cup of delicious coffee".format(text[2])); }
        break;
    case "tea":
        if(text.length == 2) { bot.action(chan, "hands {0} a nice cup of tea.".format(nick)); }
        else { bot.action(chan, "hands {0} a nice cup of tea".format(text[2])); }
        break;
    case "cookie":
        if(text.length >= 3) { bot.action(chan, "gets {0} a plate of cookies and a glass of milk".format(text[2])); }
        break;
    case "goat":
        if(text.length >= 3) { bot.say(chan, "{0}'s goat walks by and kicks {1}".format(nick, text[2])); }
        break;
    case "wolf":
        if(text.length >= 3) { bot.say(chan, "{0}'s wolf walks by and noms {1}".format(nick, text[2])); }
            break;
    case "bear":
        if(text.length >= 3) { bot.say(chan, "{0}'s bear walks by and gives {1} a huge bear hug".format(nick, text[2])); }
        break;
    case "kitty":
       if(text.length >= 3) { bot.say(chan, "{0}'s kitty climbs into {1}'s lap, curls up, and falls asleep".format(nick, text[2])); }
        break;
    case "penguin":
        if(text.length >= 3) { bot.say(chan, "{0}'s penguin waddles by and slaps {1}!".format(nick, text[2])); }
          break;
    case "kekse":
        if(text.length >= 3) { bot.action(chan, "holt {0}  einen Teller Kekse und ein Glas Milch".format(text[2])); }
        break;
    case "ping":
        bot.say(chan, "Pong!");
        break;
    case "quit":
        bot.quit();
        break;
    case "nick":
        cfg.nick = text[2]
        bot.send("NICK", text[2]);
        break;
    case "botsnack":
        bot.action(chan, "noms happily :3");
    break;
    }
}

module.exports = runcmd;<commit_msg>minor fix (thanks to @Cydrobolt for finding this)<commit_after>var cfg = require('./config.json')
var exec = require('child_process').exec;
var vs = require('fs');

function runcmd(cmd, msg, bot) {
    var nick = msg.nick;
    var cloak = msg.host;
    var chan = msg.args[0];
    var text = msg.args[1].split(" ");
    var cmd = text[1].toLowerCase();

    if(chan == cfg.nick) {
        chan = nick;
    }
    switch(cmd) {
    case "help":
        bot.say(chan, "Normal Commands: [{0}], Trusted Commands: [{1}], Op Commands: [{2}], Onwer Commands: [{3}]".format(cfg.cmd.join(", "), cfg.trusted.cmd.join(", "), cfg.op.cmd.join(", "), cfg.owner.cmd.join(", ")));
        break;
    case "list":
        bot.say(chan, "Normal Commands: [{0}], Trusted Commands: [{1}], Op Commands: [{2}], Onwer Commands: [{3}]".format(cfg.cmd.join(", "), cfg.trusted.cmd.join(", "), cfg.op.cmd.join(", "), cfg.owner.cmd.join(", ")));
        break;
    case "trustedcheck":
        bot.say(chan, nick + ": Yes! You are trusted!");
        break;
    case "tcheck":
        bot.say(chan, nick + ": Yes! You are trusted!");
        break;
    case "opcheck":
        bot.say(chan, nick + ": Yes! You are an op in {0}!".format(chan));
        break;
    case "ocheck":
        bot.say(chan, nick + ": Yes! You are my owner!");
        break;
    case "ownercheck":
        bot.say(chan, nick + ": Yes! You are my owner!");
        break;
    case "msg":
        if(text[2].split(",").length == 1 && text[2][0] == "#") {
            var send = msg.args[1].replace("{0}: msg {1} ".format(cfg.nick, text[2]), "");
            bot.say(text[2], send);
        }
        break;
    case "act":
        if(text[2].split(",").length == 1 && text[2][0] == "#") {
            var send = msg.args[1].replace("{0}: act {1} ".format(cfg.nick, text[2]), "");
            bot.action(text[2], send);
        }
        break;
    case "op":
        bot.send('MODE', chan, '+o', nick);
        break;
    case "deop":
        bot.send('MODE', chan, '-o', nick);
        break;
    case "mode":
        var send = msg.args[1].replace("{0}: mode".format(cfg.nick) , "");
        bot.conn.write("MODE " + chan + send + "\n");
        break;
    case "do":
        var send = msg.args[1].replace("{0}: do".format(cfg.nick) , "");
        console.log(send);
        bot.say(cfg.logchan, send)
        bot.conn.write(send + "\n");
        break;
    case "ddate":
        exec("ddate", function(error, stdout, stderr) { bot.say(chan, stdout); });
        break;
    case "memo":
        var memo = msg.args[1].replace(cfg.nick + ": memo " + text[2] + " ", "");
        if(text[2].split(",").length == 1) {
            bot.say(chan, "okay, sending your memo");
            exec("echo {0},{1},{2} >> memo.txt".format(text[2], nick, memo),
                function(error, stdout, stderr) {
                    var send = "[{0}] {1} sends a memo to {2}: {3}".format(chan, nick, text[2], memo);
                    console.log(send);
                    bot.say(cfg.logchan, send);
                }
            );
        }
        break;
    case "oracle":
        var morgan = fs.readFileSync('./morganstarot.txt').toString().split("\n");
        bot.say(chan, morgan[Math.floor(Math.random() * morgan.length)]);
        break;
    case "coffee":
        if(text.length == 2) { bot.action(chan, "hands {0} a steaming cup of delicious coffee".format(nick)); }
        else { bot.action(chan, "hands {0} a steaming cup of delicious coffee".format(text[2])); }
        break;
    case "tea":
        if(text.length == 2) { bot.action(chan, "hands {0} a nice cup of tea.".format(nick)); }
        else { bot.action(chan, "hands {0} a nice cup of tea".format(text[2])); }
        break;
    case "cookie":
        if(text.length >= 3) { bot.action(chan, "gets {0} a plate of cookies and a glass of milk".format(text[2])); }
        break;
    case "goat":
        if(text.length >= 3) { bot.say(chan, "{0}'s goat walks by and kicks {1}".format(nick, text[2])); }
        break;
    case "wolf":
        if(text.length >= 3) { bot.say(chan, "{0}'s wolf walks by and noms {1}".format(nick, text[2])); }
            break;
    case "bear":
        if(text.length >= 3) { bot.say(chan, "{0}'s bear walks by and gives {1} a huge bear hug".format(nick, text[2])); }
        break;
    case "kitty":
       if(text.length >= 3) { bot.say(chan, "{0}'s kitty climbs into {1}'s lap, curls up, and falls asleep".format(nick, text[2])); }
        break;
    case "penguin":
        if(text.length >= 3) { bot.say(chan, "{0}'s penguin waddles by and slaps {1}!".format(nick, text[2])); }
          break;
    case "kekse":
        if(text.length >= 3) { bot.action(chan, "holt {0}  einen Teller Kekse und ein Glas Milch".format(text[2])); }
        break;
    case "ping":
        bot.say(chan, "Pong!");
        break;
    case "quit":
        bot.quit();
        break;
    case "nick":
        cfg.nick = text[2]
        bot.send("NICK", text[2]);
        break;
    case "botsnack":
        bot.action(chan, "noms happily :3");
    break;
    }
}

module.exports = runcmd;
<|endoftext|>


<commit_before>/* eslint-disable */
import express from 'express';
import bodyParser from 'body-parser';
import config from './server/config';
import db from './server/models/index';
import path from 'path';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import webpackConfig from './webpack.config';
/* eslint-enable */

require('dotenv').config();

let app = express();

app = config(app);

if (process.env.NODE_ENV !== 'production') {
  const compiler = webpack(webpackConfig);
  app.use(webpackDevMiddleware(compiler, {
    noInfo: true,
    publicPath: webpackConfig.output.publicPath
  }));
  app.use(webpackHotMiddleware(compiler));
}

app.use(express.static(path.join(__dirname, 'client/dist')));

app.get('/app/*', (req, res) => {
  res.sendFile(`${__dirname}/client/dist/index.html`);
});

if (process.env.NODE_ENV !== 'test') {
  db.sequelize.sync().done(() => {
    app.listen(process.env.PORT, () => {
      /* eslint-disable */
      console.log(`server started on port ${process.env.PORT}`);
      /* eslint-enable */
    });
  });
}

module.exports = app;
<commit_msg>feature(add-landing-page): allow weboack to complile during development in server.js [143493101]<commit_after>/* eslint-disable */
import express from 'express';
import bodyParser from 'body-parser';
import config from './server/config';
import db from './server/models/index';
import path from 'path';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import webpackConfig from './webpack.config';
/* eslint-enable */

require('dotenv').config();

let app = express();

app = config(app);

if (process.env.NODE_ENV !== 'test') {
  const compiler = webpack(webpackConfig);
  app.use(webpackDevMiddleware(compiler, {
    noInfo: true,
    publicPath: webpackConfig.output.publicPath
  }));
  app.use(webpackHotMiddleware(compiler));
}

app.use(express.static(path.join(__dirname, 'client/dist')));

app.get('/app/*', (req, res) => {
  res.sendFile(`${__dirname}/client/dist/index.html`);
});

if (process.env.NODE_ENV !== 'test') {
  db.sequelize.sync().done(() => {
    app.listen(process.env.PORT, () => {
      /* eslint-disable */
      console.log(`server started on port ${process.env.PORT}`);
      /* eslint-enable */
    });
  });
}

module.exports = app;
<|endoftext|>


<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict
 * @format
 */

// flowlint ambiguous-object-type:error

'use strict';

type FeatureFlags = {|
  ENABLE_VARIABLE_CONNECTION_KEY: boolean,
  ENABLE_PARTIAL_RENDERING_DEFAULT: boolean,
  ENABLE_RELAY_CONTAINERS_SUSPENSE: boolean,
  ENABLE_UNIQUE_MUTATION_ROOT: boolean,
|};

const RelayFeatureFlags: FeatureFlags = {
  // T45504512: new connection model
  ENABLE_VARIABLE_CONNECTION_KEY: false,
  ENABLE_PARTIAL_RENDERING_DEFAULT: false,
  ENABLE_RELAY_CONTAINERS_SUSPENSE: true,
  ENABLE_UNIQUE_MUTATION_ROOT: true,
};

module.exports = RelayFeatureFlags;
<commit_msg>Feature flag for containers suspending should be off by default<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict
 * @format
 */

// flowlint ambiguous-object-type:error

'use strict';

type FeatureFlags = {|
  ENABLE_VARIABLE_CONNECTION_KEY: boolean,
  ENABLE_PARTIAL_RENDERING_DEFAULT: boolean,
  ENABLE_RELAY_CONTAINERS_SUSPENSE: boolean,
  ENABLE_UNIQUE_MUTATION_ROOT: boolean,
|};

const RelayFeatureFlags: FeatureFlags = {
  // T45504512: new connection model
  ENABLE_VARIABLE_CONNECTION_KEY: false,
  ENABLE_PARTIAL_RENDERING_DEFAULT: false,
  ENABLE_RELAY_CONTAINERS_SUSPENSE: false,
  ENABLE_UNIQUE_MUTATION_ROOT: true,
};

module.exports = RelayFeatureFlags;
<|endoftext|>


<commit_before>var _ = require('underscore');
var cdb = require('cartodb.js');
var CoreView = require('backbone/core-view');
var AssetsListView = require('../../../../../../../../../javascripts/cartodb3/components/form-components/editors/fill/input-color/assets-picker/assets-list-view');

describe('components/form-components/editors/fill/input-color/asset-picker/assets-list-view', function () {
  function createViewFn (options) {
    var icons = [
      { name: 'icon1', icon: 'icon1' },
      { name: 'icon2', icon: 'icon2' },
      { name: 'icon3', icon: 'icon3' }
    ];

    this.view = new AssetsListView(_.extend({
      selectedAsset: new cdb.core.Model(),
      title: 'Title',
      icons: icons,
      disclaimer: 'Disclaimer',
      folder: 'folder',
      kind: 'marker',
      size: '10'
    }, options));
  }

  beforeEach(function () {
    this.createView = createViewFn.bind(this);
  });

  describe('.initialize', function () {
    it('should call ._setupAssets and get assets with default options if no options provided', function () {
      this.createView();

      var itemNames = _.map(this.view._assets.models, function (model) {
        return model.attributes.name;
      });

      expect(itemNames.length).toBe(3);
      expect(_.contains(itemNames, 'icon1'));
      expect(_.contains(itemNames, 'icon2'));
      expect(_.contains(itemNames, 'icon3'));
    });

    it('should call ._setupAssets and get assets with provided options', function () {
      this.createView({
        folder: 'a folder',
        size: 48,
        host: 'a host',
        ext: 'an extension'
      });

      _.each(this.view._assets.models, function (model) {
        expect(model.attributes.ext).toBe('an extension');
        expect(model.attributes.folder).toBe('a folder');
        expect(model.attributes.host).toBe('a host');
        expect(model.attributes.size).toBe(48);
      });
    });

    it('should limit asset collection length if limit option is present', function () {
      this.createView({
        limit: 2
      });

      var itemNames = _.map(this.view._assets.models, function (model) {
        return model.attributes.name;
      });

      expect(itemNames.length).toBe(2);
      expect(_.contains(itemNames, 'icon1'));
      expect(_.contains(itemNames, 'icon2'));
    });

    it('should add related models', function () {
      spyOn(CoreView.prototype, 'add_related_model').and.callThrough();
      this.createView();

      expect(CoreView.prototype.add_related_model).toHaveBeenCalledWith(this.view._assets);
    });
  });

  describe('render', function () {
    it('should render icons according to the template', function () {
      this.createView();

      this.view.render();

      var expectedIcons = ['icon1', 'icon2', 'icon3'];
      _.each(expectedIcons, function (icon) {
        var $icon = this.view.$("img[alt='" + icon + "']");
        expect($icon.length).toBe(1);
        expect($icon.attr('crossorigin')).toBe('anonymous');
        expect($icon.attr('src').indexOf(icon)).not.toBe(0);
      }, this);
    });
  });

  describe('._selectItem', function () {
    it('should select given item and deselect the others', function () {
      var selectedAsset = new cdb.core.Model();

      this.createView({
        selectedAsset: selectedAsset
      });

      this.view.render();

      var url = this.view.$('.AssetsList-item:nth-child(1) img').attr('src');
      this.view.$('.AssetsList-item:nth-child(1) .js-asset').click();

      expect(selectedAsset.get('url')).toBe(url);
      expect(selectedAsset.get('kind')).toBe('marker');
    });
  });
});
<commit_msg>Updates spec<commit_after>var _ = require('underscore');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var AssetsListView = require('../../../../../../../../../javascripts/cartodb3/components/form-components/editors/fill/input-color/assets-picker/assets-list-view');

describe('components/form-components/editors/fill/input-color/asset-picker/assets-list-view', function () {
  function createViewFn (options) {
    var icons = [
      { name: 'icon1', icon: 'icon1' },
      { name: 'icon2', icon: 'icon2' },
      { name: 'icon3', icon: 'icon3' }
    ];

    this.view = new AssetsListView(_.extend({
      selectedAsset: new Backbone.Model(),
      title: 'Title',
      icons: icons,
      disclaimer: 'Disclaimer',
      folder: 'folder',
      kind: 'marker',
      size: '10'
    }, options));
  }

  beforeEach(function () {
    this.createView = createViewFn.bind(this);
  });

  describe('.initialize', function () {
    it('should call ._setupAssets and get assets with default options if no options provided', function () {
      this.createView();

      var itemNames = _.map(this.view._assetsCollection.models, function (model) {
        return model.attributes.name;
      });

      expect(itemNames.length).toBe(3);
      expect(_.contains(itemNames, 'icon1'));
      expect(_.contains(itemNames, 'icon2'));
      expect(_.contains(itemNames, 'icon3'));
    });

    it('should call ._setupAssets and get assets with provided options', function () {
      this.createView({
        folder: 'a folder',
        size: 48,
        host: 'a host',
        ext: 'an extension'
      });

      _.each(this.view._assetsCollection.models, function (model) {
        expect(model.attributes.ext).toBe('an extension');
        expect(model.attributes.folder).toBe('a folder');
        expect(model.attributes.host).toBe('a host');
        expect(model.attributes.size).toBe(48);
      });
    });

    it('should limit asset collection length if limit option is present', function () {
      this.createView({
        limit: 2
      });

      var itemNames = _.map(this.view._assetsCollection.models, function (model) {
        return model.attributes.name;
      });

      expect(itemNames.length).toBe(2);
      expect(_.contains(itemNames, 'icon1'));
      expect(_.contains(itemNames, 'icon2'));
    });

    it('should add related models', function () {
      spyOn(CoreView.prototype, 'add_related_model').and.callThrough();
      this.createView();

      expect(CoreView.prototype.add_related_model).toHaveBeenCalledWith(this.view._assetsCollection);
    });
  });

  describe('render', function () {
    it('should render icons according to the template', function () {
      this.createView();

      this.view.render();

      var expectedIcons = ['icon1', 'icon2', 'icon3'];
      _.each(expectedIcons, function (icon) {
        var $icon = this.view.$("img[alt='" + icon + "']");
        expect($icon.length).toBe(1);
        expect($icon.attr('crossorigin')).toBe('anonymous');
        expect($icon.attr('src').indexOf(icon)).not.toBe(0);
      }, this);
    });
  });

  describe('._selectItem', function () {
    it('should select given item and deselect the others', function () {
      var selectedAsset = new Backbone.Model();

      this.createView({
        selectedAsset: selectedAsset
      });

      this.view.render();

      var url = this.view.$('.AssetsList-item:nth-child(1) img').attr('src');
      this.view.$('.AssetsList-item:nth-child(1) .js-asset').click();

      expect(selectedAsset.get('url')).toBe(url);
      expect(selectedAsset.get('kind')).toBe('marker');
    });
  });
});
<|endoftext|>


<commit_before>import * as _ from 'lodash'
import * as React from 'react'
import * as marked from 'marked'

import {
	BooleanRequirement, SomeArrayRequirement, BooleanArrayRequirement, NumberObjectRequirement
} from 'elements/requirement'

let RequirementSet = React.createClass({
	toggleDescription() {
		this.setState({showDescription: !this.state.showDescription})
	},

	getInitialState() {
		return {
			showDescription: false
		}
	},

	componentWillReceiveProps(nextProps) {
		this.setState({descriptionHTML: marked(nextProps.description)})
	},

	componentWillMount() {
		this.componentWillReceiveProps(this.props)
	},

	render() {
		// console.log('requirement-set render', this.props)

		let title = React.createElement('h2', {
			className: this.props.result ? 'completed' : 'incomplete',
			title: this.props.description,
		}, this.props.title)

		let description = null;
		if (this.state.showDescription && this.props.description)
			description = React.createElement('div', {className: 'description', dangerouslySetInnerHTML: {__html: this.state.descriptionHTML}})

		let titlebar = React.createElement('header', {onClick: this.toggleDescription}, title, description)

		let details;
		let type = this.props.type

		if (type === 'array/requirementSet') {
			details = _.map(this.props.details, (requirement, index) => {
				return React.createElement(RequirementSet, _.merge({key: index}, requirement))
			})
		}

		else if (type === 'array/some') {
			details = React.createElement(SomeArrayRequirement, {result: this.props.result, details: this.props.details})
		}

		else if (type === 'array/boolean') {
			details = React.createElement(BooleanArrayRequirement, {details: this.props.details})
		}

		else if (type === 'boolean') {
			details = React.createElement(BooleanRequirement, {result: this.props.result})
		}

		else if (type === 'object/number') {
			details = React.createElement(NumberObjectRequirement, {result: this.props.result, details: this.props.details})
		}

		return React.createElement('div', {className: 'requirement-set', 'data-type': type},
			titlebar,
			details)
	},
})

export default RequirementSet
<commit_msg>Tweak the description showing<commit_after>import * as _ from 'lodash'
import * as React from 'react'
import * as marked from 'marked'

import {
	BooleanRequirement, SomeArrayRequirement, BooleanArrayRequirement, NumberObjectRequirement
} from 'elements/requirement'

let RequirementSet = React.createClass({
	toggleDescription() {
		this.setState({showDescription: !this.state.showDescription})
	},

	getInitialState() {
		return {
			showDescription: false
		}
	},

	componentWillReceiveProps(nextProps) {
		if (nextProps.description)
			this.setState({descriptionHTML: marked(nextProps.description)})
	},

	componentWillMount() {
		this.componentWillReceiveProps(this.props)
	},

	render() {
		// console.log('requirement-set render', this.props)

		let title = React.createElement('h2', {
			className: this.props.result ? 'completed' : 'incomplete',
			title: this.props.description,
		}, this.props.title)

		let description = null;
		if (this.state.showDescription && this.state.descriptionHTML)
			description = React.createElement('div', {className: 'description', dangerouslySetInnerHTML: {__html: this.state.descriptionHTML}})

		let titlebar = React.createElement('header', {onClick: this.toggleDescription}, title, description)

		let details;
		let type = this.props.type

		if (type === 'array/requirementSet') {
			details = _.map(this.props.details, (requirement, index) => {
				return React.createElement(RequirementSet, _.merge({key: index}, requirement))
			})
		}

		else if (type === 'array/some') {
			details = React.createElement(SomeArrayRequirement, {result: this.props.result, details: this.props.details})
		}

		else if (type === 'array/boolean') {
			details = React.createElement(BooleanArrayRequirement, {details: this.props.details})
		}

		else if (type === 'boolean') {
			details = React.createElement(BooleanRequirement, {result: this.props.result})
		}

		else if (type === 'object/number') {
			details = React.createElement(NumberObjectRequirement, {result: this.props.result, details: this.props.details})
		}

		return React.createElement('div', {className: 'requirement-set', 'data-type': type},
			titlebar,
			details)
	},
})

export default RequirementSet
<|endoftext|>