<commit_before>/**
 * 01 - Generators functions and objects
 *
 * Generator is a function which returns generator objects, that can then be iterated using
 * any of kind of loops. Generator function allow you to declare a special kind of iterator.
 * These iterators can suspend execution while retaining their context.
 */
'use strict';

/**
 * Below you cand find a simple example of how to write a generator. Note the '*' after
 * 'function'. That's not a typo, that's how you mark a generator function as a generator.
 */
function* generator () {
  yield 'f'
  yield 'o'
  yield 'o'
}

/**
 * Generator objects conform to both the 'iterable' protocol and the 'iterator'
 * protocol, which means:
 */

// A generator object g is built using the generator function
let g = generator();

// It's an iterable because it has an @@iterator
typeof g[Symbol.iterator] === 'function'

// It's also an iterator because it has a .next method
typeof g.next === 'function';

// The iterator for a generator object is the generator object itself
g[Symbol.iterator]() === g

// Iterates all the generator values
console.log([...g]);

// Can't iterate it again since it is already 'done'
console.log(g.next())

/**
 * When you create a generator, you'll get an iterator that uses the generator to
 * produce its sequence. Whenever a yield expression is reached, that value is
 * emitted by the iterator and the function execution is suspended.
 *
 * Let's use a different example, this time with some other statements mixed in between
 * yield expressions. This is a simple generator but it behaves in an interesting enough
 * way for our purposes here.
 */
function* generator() {
    yield 'w';
    console.log('e');
    yield 'e';
    console.log('k');
    yield 'e';
    console.log('n');
    yield 'd';
    console.log('!');
}

/**
 * If we use a for..of loop, this will print 'weekend!', one character at a time, as expected:
 */
let foo = generator();
for (let weekend of foo) {
  console.log(weekend);
}

/**
 * What about using the spread [...foo] syntax? Things turn out a little different here. This
 * might be a little unexpected, but that's how generators work, everything that's not yielded
 * ends up becoming a side effect. As the sequence is being constructed, the console.log statements
 * in between yield calls are executed, and they print characters to the console before foo is spread
 * over an array. The previous example worked because we were printing characters as soon as they were
 * pulled from the sequence, instead of waiting to construct a range for the entire sequence first.
 */
let fooSpread = generator();
console.log([...fooSpread]);

/**
 * You canse also use the "yield*" to delegate to another generator function. For example, if you
 * want a very contrived way to split 'weekend' into individual characters. Since strings in ES6
 * adhere to the iterable protocol, you could do the following:
 */
function* generator() {
  yield* 'weekend'
}

console.log([...generator()]);

/**
 * Yeah, in the real world you could just do [...'weekend'], since spread supports iterables just fine.
 * Just like you could yield* a string, you can yield* anything that adheres to the iterable protocol,
 * such as generators, arrays, etc.
 */
let fooMultiplier = {
  [Symbol.iterator]: () => ({
    items: ['w', 'e', 'e', 'k', 'e', 'n', 'd'],
    next: function next() {
      return {
        done: this.items.length === 0,
        value: this.items.shift()
      }
    }
  })
}

function* multiplier(value) {
  yield value * 2;
  yield value * 3;
  yield value * 4;
  yield value * 5;
}

function* trailmix() {
  yield 0
  yield* [1, 2]
  yield* [...multiplier(2)];
  yield* multiplier(3);
  yield* fooMultiplier
}

console.log([...trailmix()]);

// [0, 1, 2, 4, 6, 8, 10, 6, 9, 12, 15, 'w', 'e', 'e', 'k', 'e', 'n', 'd']

/**
 * Besides iterating over trailmix as we've already covered using '[...trailmix()]', 'for
 * value of trailmix()' and 'Array.from(trailmix())', we could use the generator returned
 * by trailmix() directly, and iterate over that. But trailmix() was an overcomplicated
 * showcase of yield*, let's go back to the side-effects generator for this one:
 */
function* generator() {
  yield 'w';
  console.log('e');
  yield 'e';
  console.log('k');
  yield 'e';
  console.log('n');
  yield 'd';
  console.log('!');
}

let g = generator();

while (true) {
  let item = g.next();
  if (item.done) {
    break;
  }
  console.log(item.value);
}

/**
 * If you’re confused about why the '!' is printed even though there are no more yield expressions after it,
 * that’s because g.next() doesn’t know that. The way it works is that each time its called, it executes the
 * method until a yield expression is reached, emits its value and suspends execution. The next time g.next()
 * is called, the execution is resumed from where it left off (the last yield expression), until the next yield
 * expression is reached. When no yield expression is reached, the generator returns { done: true }, signaling
 * that the sequence has ended. At this point, the console.log('!') statement has been already executed, though.
 *
 * Whenever, .next() is called on a generator, there's four "events" that wil suspend execution in the generator,
 * returning an IteratorResult to the caller of .next()
 *
 * - A 'yield' expression returning the next value in the sequence;
 * - A 'return' statement returning the last value in the seqeunce;
 * - A 'throw' statement halts execution in the generator entirely;
 * - Reaching the end of the generator function signals { done: true }
 *
 * Once a generator 'g' ended iterating over a sequence, subsequent calls to 'g.next()' will have no effect and
 * just return { done: true }
 */
function* generator () {
  yield 'only'
}

var g = generator();

console.log(g.next()); // <- { done: false, value: 'only' }
console.log(g.next()); // <- { done: true }
console.log(g.next()); // <- { done: true }<commit_msg>Fixed typo on section description<commit_after>/**
 * 02 - Generators functions and objects
 *
 * Generator is a function which returns generator objects, that can then be iterated using
 * any of kind of loops. Generator function allow you to declare a special kind of iterator.
 * These iterators can suspend execution while retaining their context.
 */
'use strict';

/**
 * Below you cand find a simple example of how to write a generator. Note the '*' after
 * 'function'. That's not a typo, that's how you mark a generator function as a generator.
 */
function* generator () {
  yield 'f'
  yield 'o'
  yield 'o'
}

/**
 * Generator objects conform to both the 'iterable' protocol and the 'iterator'
 * protocol, which means:
 */

// A generator object g is built using the generator function
let g = generator();

// It's an iterable because it has an @@iterator
typeof g[Symbol.iterator] === 'function'

// It's also an iterator because it has a .next method
typeof g.next === 'function';

// The iterator for a generator object is the generator object itself
g[Symbol.iterator]() === g

// Iterates all the generator values
console.log([...g]);

// Can't iterate it again since it is already 'done'
console.log(g.next())

/**
 * When you create a generator, you'll get an iterator that uses the generator to
 * produce its sequence. Whenever a yield expression is reached, that value is
 * emitted by the iterator and the function execution is suspended.
 *
 * Let's use a different example, this time with some other statements mixed in between
 * yield expressions. This is a simple generator but it behaves in an interesting enough
 * way for our purposes here.
 */
function* generator() {
    yield 'w';
    console.log('e');
    yield 'e';
    console.log('k');
    yield 'e';
    console.log('n');
    yield 'd';
    console.log('!');
}

/**
 * If we use a for..of loop, this will print 'weekend!', one character at a time, as expected:
 */
let foo = generator();
for (let weekend of foo) {
  console.log(weekend);
}

/**
 * What about using the spread [...foo] syntax? Things turn out a little different here. This
 * might be a little unexpected, but that's how generators work, everything that's not yielded
 * ends up becoming a side effect. As the sequence is being constructed, the console.log statements
 * in between yield calls are executed, and they print characters to the console before foo is spread
 * over an array. The previous example worked because we were printing characters as soon as they were
 * pulled from the sequence, instead of waiting to construct a range for the entire sequence first.
 */
let fooSpread = generator();
console.log([...fooSpread]);

/**
 * You canse also use the "yield*" to delegate to another generator function. For example, if you
 * want a very contrived way to split 'weekend' into individual characters. Since strings in ES6
 * adhere to the iterable protocol, you could do the following:
 */
function* generator() {
  yield* 'weekend'
}

console.log([...generator()]);

/**
 * Yeah, in the real world you could just do [...'weekend'], since spread supports iterables just fine.
 * Just like you could yield* a string, you can yield* anything that adheres to the iterable protocol,
 * such as generators, arrays, etc.
 */
let fooMultiplier = {
  [Symbol.iterator]: () => ({
    items: ['w', 'e', 'e', 'k', 'e', 'n', 'd'],
    next: function next() {
      return {
        done: this.items.length === 0,
        value: this.items.shift()
      }
    }
  })
}

function* multiplier(value) {
  yield value * 2;
  yield value * 3;
  yield value * 4;
  yield value * 5;
}

function* trailmix() {
  yield 0
  yield* [1, 2]
  yield* [...multiplier(2)];
  yield* multiplier(3);
  yield* fooMultiplier
}

console.log([...trailmix()]);

// [0, 1, 2, 4, 6, 8, 10, 6, 9, 12, 15, 'w', 'e', 'e', 'k', 'e', 'n', 'd']

/**
 * Besides iterating over trailmix as we've already covered using '[...trailmix()]', 'for
 * value of trailmix()' and 'Array.from(trailmix())', we could use the generator returned
 * by trailmix() directly, and iterate over that. But trailmix() was an overcomplicated
 * showcase of yield*, let's go back to the side-effects generator for this one:
 */
function* generator() {
  yield 'w';
  console.log('e');
  yield 'e';
  console.log('k');
  yield 'e';
  console.log('n');
  yield 'd';
  console.log('!');
}

let g = generator();

while (true) {
  let item = g.next();
  if (item.done) {
    break;
  }
  console.log(item.value);
}

/**
 * If you’re confused about why the '!' is printed even though there are no more yield expressions after it,
 * that’s because g.next() doesn’t know that. The way it works is that each time its called, it executes the
 * method until a yield expression is reached, emits its value and suspends execution. The next time g.next()
 * is called, the execution is resumed from where it left off (the last yield expression), until the next yield
 * expression is reached. When no yield expression is reached, the generator returns { done: true }, signaling
 * that the sequence has ended. At this point, the console.log('!') statement has been already executed, though.
 *
 * Whenever, .next() is called on a generator, there's four "events" that wil suspend execution in the generator,
 * returning an IteratorResult to the caller of .next()
 *
 * - A 'yield' expression returning the next value in the sequence;
 * - A 'return' statement returning the last value in the seqeunce;
 * - A 'throw' statement halts execution in the generator entirely;
 * - Reaching the end of the generator function signals { done: true }
 *
 * Once a generator 'g' ended iterating over a sequence, subsequent calls to 'g.next()' will have no effect and
 * just return { done: true }
 */
function* generator () {
  yield 'only'
}

var g = generator();

console.log(g.next()); // <- { done: false, value: 'only' }
console.log(g.next()); // <- { done: true }
console.log(g.next()); // <- { done: true }<|endoftext|>


<commit_before>/**
 * The MIT License
 *
 * Copyright (c) 2009 Steven G. Brown
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
      var response = {};
      if (request.operation === 'open-options-page') {
        var tabProperties = {
          windowId: sender.tab.windowId,
          index: sender.tab.index + 1,
          url: chrome.extension.getURL('options.html')
        };
        chrome.tabs.create(tabProperties);
      }
      sendResponse(response);
    }
);

chrome.commands.onCommand.addListener(function(command) {
  chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
    tabs.forEach(function(tab) {
      chrome.tabs.sendMessage(tab.id, command);
    });
  });
});

<commit_msg>Chrome: Migrate configured options from localStorage to storage.sync<commit_after>/**
 * The MIT License
 *
 * Copyright (c) 2009 Steven G. Brown
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

chrome.runtime.onInstalled.addListener(function(details) {
  if (details.reason !== 'install' && details.reason !== 'update') {
    return;
  }
  chrome.storage.local.get('optionsMigrated', function(items) {
    if (items.optionsMigrated) {
      return;
    }
    console.log('Migrating options from localStorage');
    var itemsToSave = {};
    var ver = '1.4.6';
    var autoOpen = localStorage.getItem('auto_open');
    if (autoOpen === 'true') {
      itemsToSave['auto_open'] = {'value': true, 'version': ver};
    }
    var hidePackageFrame = localStorage.getItem('hide_package_frame');
    if (hidePackageFrame === 'false') {
      itemsToSave['hide_package_frame'] = {'value': false, 'version': ver};
    }
    var packageMenu = localStorage.getItem('package_menu');
    var packageMenuDefault =
        '@1:search(koders) -> http://www.koders.com/?s=##PACKAGE_NAME##\n' +
        '@2:search(Docjar) -> http://www.docjar.com/s.jsp?q=##PACKAGE_NAME##';
    if (packageMenu && packageMenu !== packageMenuDefault) {
      itemsToSave['package_menu'] = {'value': packageMenu, 'version': ver};
    }
    var classMenu = localStorage.getItem('class_menu');
    var classMenuDefault =
        '@1:search(koders) -> http://www.koders.com/' +
        '?s=##PACKAGE_NAME##+##CLASS_NAME##+##MEMBER_NAME##\n' +
        '@2:search(Docjar) -> http://www.docjar.com/s.jsp?q=##CLASS_NAME##\n' +
        '@3:source(Docjar) -> http://www.docjar.com/html/api/' +
        '##PACKAGE_PATH##/##CLASS_NAME##.java.html';
    if (classMenu && classMenu !== classMenuDefault) {
      itemsToSave['class_menu'] = {'value': classMenu, 'version': ver};
    }
    chrome.storage.sync.set(itemsToSave);
    console.log(itemsToSave);
    chrome.storage.local.set({'optionsMigrated': true});
  });
});

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
      var response = {};
      if (request.operation === 'open-options-page') {
        var tabProperties = {
          windowId: sender.tab.windowId,
          index: sender.tab.index + 1,
          url: chrome.extension.getURL('options.html')
        };
        chrome.tabs.create(tabProperties);
      }
      sendResponse(response);
    }
);

chrome.commands.onCommand.addListener(function(command) {
  chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
    tabs.forEach(function(tab) {
      chrome.tabs.sendMessage(tab.id, command);
    });
  });
});

<|endoftext|>


<commit_before>module.exports = function(grunt) {

  require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks);

  grunt.initConfig({
    temp: ".tmp",

    bump: {
      options: {
        files: ["package.json"],
        commit: true,
        commitMessage: "Release v%VERSION%",
        commitFiles: ["-a"],
        createTag: true,
        tagName: "v%VERSION%",
        tagMessage: "Version %VERSION%",
        push: true,
        pushTo: "origin",
        gitDescribeOptions: "--tags --always --abbrev=1 --dirty=-d"
      }
    },

    clean: {
      tests: ["<%= temp %>"],
    },

    fontfactory: {
      test: {
        options: {
          font: "my-test-font"
        },
        src: "test/fixtures/*.svg",
        dest: "<%= temp %>"
      }
    },

    nodeunit: {
      tests: ["test/*_test.js"],
    }
  });

  grunt.loadTasks("tasks");

  grunt.registerTask("default", ["test"]);
  grunt.registerTask("test", [
    "clean",
    "fontfactory",
    "nodeunit",
    // "clean"
  ]);
};
<commit_msg>Cleaning up after test run<commit_after>module.exports = function(grunt) {

  require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks);

  grunt.initConfig({
    temp: ".tmp",

    bump: {
      options: {
        files: ["package.json"],
        commit: true,
        commitMessage: "Release v%VERSION%",
        commitFiles: ["-a"],
        createTag: true,
        tagName: "v%VERSION%",
        tagMessage: "Version %VERSION%",
        push: true,
        pushTo: "origin",
        gitDescribeOptions: "--tags --always --abbrev=1 --dirty=-d"
      }
    },

    clean: {
      tests: ["<%= temp %>"],
    },

    fontfactory: {
      test: {
        options: {
          font: "my-test-font"
        },
        src: "test/fixtures/*.svg",
        dest: "<%= temp %>"
      }
    },

    nodeunit: {
      tests: ["test/*_test.js"],
    }
  });

  grunt.loadTasks("tasks");

  grunt.registerTask("default", ["test"]);
  grunt.registerTask("test", [
    "clean",
    "fontfactory",
    "nodeunit",
    "clean"
  ]);
};
<|endoftext|>


<commit_before>import React from 'react';

import './ChatBoxCard.css';

class ChatBoxCard extends React.Component {
    render() {
        const className = (this.props.isUserAuthor) ? 'ChatBoxCard me' : 'ChatBoxCard';

        return (

                    { this.props.author }: { this.props.message }

        );
    };
}

export default ChatBoxCard;
<commit_msg>[CHORE] Updated chat card author name to identify current user<commit_after>import React from 'react';

import './ChatBoxCard.css';

class ChatBoxCard extends React.Component {
    render() {
        const className = (this.props.isUserAuthor) ? 'ChatBoxCard me' : 'ChatBoxCard';

        return (

                    { (this.props.isUserAuthor) ? 'You' : this.props.author }: { this.props.message }

        );
    };
}

export default ChatBoxCard;
<|endoftext|>


<commit_before>module.exports = function lgGutter(css, settings) {
  var gutter = settings.gutter;
  css.eachDecl( decl => {

    if (decl.value === '$lost-gutter') {
      decl.value = gutter;
    }

  });
};
<commit_msg>Lost-gutter using regex<commit_after>module.exports = function lgGutter(css, settings) {
  var gutter = settings.gutter;
  css.eachDecl( decl => {
    if (/(\$lost-gutter)/g.test(decl.value)) {
      var newValue = decl.value.replace(/(\$lost-gutter)/g, gutter);
      decl.value = newValue;
    }

  });
};
<|endoftext|>


<commit_before>// Load in our dependencies
var app = require('app');
var BrowserWindow = require('browser-window');
var globalShortcut = require('global-shortcut');
var ipc = require('ipc');
var Menu = require('menu');
var MenuItem = require('menu-item');
var Tray = require('tray');
var GoogleMusic = require('google-music');

// Load in package info
var pkg = require('../package.json');

// Load in JSON for our menus (e.g. `./menus/linux.json`)
// https://github.com/atom/electron-starter/blob/96f6117b4c1f33c0881d504d655467fc049db433/src/browser/appmenu.coffee#L15
var menuTemplate = require('./menus/' + process.platform + '.json');

// Report any crashes to Electron's servers
require('crash-reporter').start();

// Add a reference to our window
// DEV: This needs to be global, otherwise it will get garbage collected (and close silently)
var browserWindow = null;

// When all Windows are closed
app.on('window-all-closed', function handleWindowsClosed () {
  // If we are not on OSX, exit
  // DEV: OSX requires users to quit via the menu/cmd+q
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

// When Electron is done loading
app.on('ready', function handleReady () {
  // Create our browser window for Google Music
  browserWindow = new BrowserWindow({
    'use-content-size': true,
    height: 920,
    // Load in our Google Music bindings on the page
    preload: __dirname + '/browser.js',
    width: 1024
  });
  browserWindow.loadUrl('https://play.google.com/music/listen');

  // When our window is closed, clean up the reference to our window
  browserWindow.on('closed', function handleWindowClose () {
    browserWindow = null;
  });

  // Define helpers for controlling/sending messages to our window
  // https://github.com/atom/electron-starter/blob/96f6117b4c1f33c0881d504d655467fc049db433/src/browser/application.coffee#L87-L104
  function controlPlayPause() {
    if (browserWindow.webContents) {
      browserWindow.webContents.send('control:play-pause');
    }
  }
  function controlNext() {
    if (browserWindow.webContents) {
      browserWindow.webContents.send('control:next');
    }
  }
  function controlPrevious() {
    if (browserWindow.webContents) {
      browserWindow.webContents.send('control:previous');
    }
  }
  function openAboutWindow() {
    var info = [
      '
',
        '
google-music-electron
',
        '
Version: ' + pkg.version + '
',
      '
'
    ].join('');
    // DEV: aboutWindow will be garbage collection automatically
    var aboutWindow = new BrowserWindow({
      height: 150,
      width: 400
    });
    aboutWindow.loadUrl('data:text/html,' + info);
  }
  function quitApplication() {
    app.quit();
  }
  function reloadWindow() {
    BrowserWindow.getFocusedWindow().reload();
  }
  function toggleFullScreen() {
    var focusedWindow = BrowserWindow.getFocusedWindow();
    // Move to other full screen state (e.g. true -> false)
    var fullScreen = !focusedWindow.isFullScreen();
    focusedWindow.setFullScreen(fullScreen);
  }
  function toggleDevTools() {
    BrowserWindow.getFocusedWindow().toggleDevTools();
  }

  // Parse and set up our menu
  // https://github.com/atom/electron-starter/blob/96f6117b4c1f33c0881d504d655467fc049db433/src/browser/appmenu.coffee#L27-L41
  function bindMenuItems(menuItems) {
    menuItems.forEach(function bindMenuItemFn (menuItem) {
      // If there is a selector, continue
      // DEV: This is an OSX specific binding
      if (menuItem.selector !== undefined) {
        return;
      }

      // If there is a submenu, recurse it
      if (menuItem.submenu) {
        bindMenuItems(menuItem.submenu);
        return;
      }

      // Otherwise, find the function for our command
      switch (menuItem.command) {
        case 'application:about':
          menuItem.click = openAboutWindow;
          break;
        case 'application:quit':
          menuItem.click = quitApplication;
          break;
        case 'window:reload':
          menuItem.click = reloadWindow;
          break;
        case 'window:toggle-dev-tools':
          menuItem.click = toggleDevTools;
          break;
        case 'window:toggle-full-screen':
          menuItem.click = toggleFullScreen;
          break;
        default:
          throw new Error('Could not find function for menu command "' + menuItem.command + '" ' +
            'under label "' + menuItem.label + '"');
      }
    });
  }
  bindMenuItems(menuTemplate.menu);
  Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplate.menu));

  // Set up our tray
  var trayMenu = new Menu();
  trayMenu.append(new MenuItem({
    label: 'Play/Pause',
    click: controlPlayPause
  }));
  trayMenu.append(new MenuItem({
    label: 'Next',
    click: controlNext
  }));
  trayMenu.append(new MenuItem({
    label: 'Previous',
    click: controlPrevious
  }));
  trayMenu.append(new MenuItem({
    type: 'separator'
  }));
  trayMenu.append(new MenuItem({
    label: 'Quit',
    click: quitApplication
  }));
  var tray = new Tray(__dirname + '/assets/icon.png');
  tray.setContextMenu(trayMenu);

  // When the song changes, update our tooltip
  ipc.on('change:song', function handleSongChange (evt, songInfo) {
    var infoStr = [
      'Title: ' + songInfo.title,
      'Artist: ' + songInfo.artist,
      'Album: ' + songInfo.album,
    ].join('\n');
    tray.setToolTip(infoStr);
  });

  // When the playback state changes, update the icon
  ipc.on('change:playback', function handlePlaybackChange (evt, playbackState) {
    // Determine which icon to display based on state
    // By default, render the clean icon (stopped state)
    var icon = __dirname + '/assets/icon.png';
    if (playbackState === GoogleMusic.Playback.PLAYING) {
      icon = __dirname + '/assets/icon-playing.png';
    } else if (playbackState === GoogleMusic.Playback.PAUSED) {
      icon = __dirname + '/assets/icon-paused.png';
    }

    // Update the icon
    tray.setImage(icon);
  });

  // Set up media keys
  if (!globalShortcut.register('mediaplaypause', controlPlayPause)) {
    console.log('Failed to bind `mediaplaypause` shortcut');
  }
  if (!globalShortcut.register('medianexttrack', controlNext)) {
    console.log('Failed to bind `medianexttrack` shortcut');
  }
  if (!globalShortcut.register('mediaprevioustrack', controlPrevious)) {
    console.log('Failed to bind `mediaprevioustrack` shortcut');
  }
});
<commit_msg>Thinned out switch<commit_after>// Load in our dependencies
var app = require('app');
var BrowserWindow = require('browser-window');
var globalShortcut = require('global-shortcut');
var ipc = require('ipc');
var Menu = require('menu');
var MenuItem = require('menu-item');
var Tray = require('tray');
var GoogleMusic = require('google-music');

// Load in package info
var pkg = require('../package.json');

// Load in JSON for our menus (e.g. `./menus/linux.json`)
// https://github.com/atom/electron-starter/blob/96f6117b4c1f33c0881d504d655467fc049db433/src/browser/appmenu.coffee#L15
var menuTemplate = require('./menus/' + process.platform + '.json');

// Report any crashes to Electron's servers
require('crash-reporter').start();

// Add a reference to our window
// DEV: This needs to be global, otherwise it will get garbage collected (and close silently)
var browserWindow = null;

// When all Windows are closed
app.on('window-all-closed', function handleWindowsClosed () {
  // If we are not on OSX, exit
  // DEV: OSX requires users to quit via the menu/cmd+q
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

// When Electron is done loading
app.on('ready', function handleReady () {
  // Create our browser window for Google Music
  browserWindow = new BrowserWindow({
    'use-content-size': true,
    height: 920,
    // Load in our Google Music bindings on the page
    preload: __dirname + '/browser.js',
    width: 1024
  });
  browserWindow.loadUrl('https://play.google.com/music/listen');

  // When our window is closed, clean up the reference to our window
  browserWindow.on('closed', function handleWindowClose () {
    browserWindow = null;
  });

  // Define helpers for controlling/sending messages to our window
  // https://github.com/atom/electron-starter/blob/96f6117b4c1f33c0881d504d655467fc049db433/src/browser/application.coffee#L87-L104
  function controlPlayPause() {
    if (browserWindow.webContents) {
      browserWindow.webContents.send('control:play-pause');
    }
  }
  function controlNext() {
    if (browserWindow.webContents) {
      browserWindow.webContents.send('control:next');
    }
  }
  function controlPrevious() {
    if (browserWindow.webContents) {
      browserWindow.webContents.send('control:previous');
    }
  }
  function openAboutWindow() {
    var info = [
      '
',
        '
google-music-electron
',
        '
Version: ' + pkg.version + '
',
      '
'
    ].join('');
    // DEV: aboutWindow will be garbage collection automatically
    var aboutWindow = new BrowserWindow({
      height: 150,
      width: 400
    });
    aboutWindow.loadUrl('data:text/html,' + info);
  }
  function quitApplication() {
    app.quit();
  }
  function reloadWindow() {
    BrowserWindow.getFocusedWindow().reload();
  }
  function toggleFullScreen() {
    var focusedWindow = BrowserWindow.getFocusedWindow();
    // Move to other full screen state (e.g. true -> false)
    var fullScreen = !focusedWindow.isFullScreen();
    focusedWindow.setFullScreen(fullScreen);
  }
  function toggleDevTools() {
    BrowserWindow.getFocusedWindow().toggleDevTools();
  }

  // Parse and set up our menu
  // https://github.com/atom/electron-starter/blob/96f6117b4c1f33c0881d504d655467fc049db433/src/browser/appmenu.coffee#L27-L41
  function bindMenuItems(menuItems) {
    menuItems.forEach(function bindMenuItemFn (menuItem) {
      // If there is a selector, continue
      // DEV: This is an OSX specific binding
      if (menuItem.selector !== undefined) {
        return;
      }

      // If there is a submenu, recurse it
      if (menuItem.submenu) {
        bindMenuItems(menuItem.submenu);
        return;
      }

      // Otherwise, find the function for our command
      var cmd = menuItem.command;
      if (cmd === 'application:about') {
        menuItem.click = openAboutWindow;
      } else if (cmd === 'application:quit') {
        menuItem.click = quitApplication;
      } else if (cmd === 'window:reload') {
        menuItem.click = reloadWindow;
      } else if (cmd === 'window:toggle-dev-tools') {
        menuItem.click = toggleDevTools;
      } else if (cmd === 'window:toggle-full-screen') {
        menuItem.click = toggleFullScreen;
      } else {
        throw new Error('Could not find function for menu command "' + cmd + '" ' +
          'under label "' + menuItem.label + '"');
      }
    });
  }
  bindMenuItems(menuTemplate.menu);
  Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplate.menu));

  // Set up our tray
  var trayMenu = new Menu();
  trayMenu.append(new MenuItem({
    label: 'Play/Pause',
    click: controlPlayPause
  }));
  trayMenu.append(new MenuItem({
    label: 'Next',
    click: controlNext
  }));
  trayMenu.append(new MenuItem({
    label: 'Previous',
    click: controlPrevious
  }));
  trayMenu.append(new MenuItem({
    type: 'separator'
  }));
  trayMenu.append(new MenuItem({
    label: 'Quit',
    click: quitApplication
  }));
  var tray = new Tray(__dirname + '/assets/icon.png');
  tray.setContextMenu(trayMenu);

  // When the song changes, update our tooltip
  ipc.on('change:song', function handleSongChange (evt, songInfo) {
    var infoStr = [
      'Title: ' + songInfo.title,
      'Artist: ' + songInfo.artist,
      'Album: ' + songInfo.album,
    ].join('\n');
    tray.setToolTip(infoStr);
  });

  // When the playback state changes, update the icon
  ipc.on('change:playback', function handlePlaybackChange (evt, playbackState) {
    // Determine which icon to display based on state
    // By default, render the clean icon (stopped state)
    var icon = __dirname + '/assets/icon.png';
    if (playbackState === GoogleMusic.Playback.PLAYING) {
      icon = __dirname + '/assets/icon-playing.png';
    } else if (playbackState === GoogleMusic.Playback.PAUSED) {
      icon = __dirname + '/assets/icon-paused.png';
    }

    // Update the icon
    tray.setImage(icon);
  });

  // Set up media keys
  if (!globalShortcut.register('mediaplaypause', controlPlayPause)) {
    console.log('Failed to bind `mediaplaypause` shortcut');
  }
  if (!globalShortcut.register('medianexttrack', controlNext)) {
    console.log('Failed to bind `medianexttrack` shortcut');
  }
  if (!globalShortcut.register('mediaprevioustrack', controlPrevious)) {
    console.log('Failed to bind `mediaprevioustrack` shortcut');
  }
});
<|endoftext|>


<commit_before>// This test written in mocha+should.js
var should = require('./init.js');

var db, Book, Chapter, Author, Reader;

describe('relations', function() {
    before(function(done) {
        db = getSchema();
        Book = db.define('Book', {name: String});
        Chapter = db.define('Chapter', {name: {type: String, index: true}});
        Author = db.define('Author', {name: String});
        Reader = db.define('Reader', {name: String});

        db.automigrate(function() {
            Book.destroyAll(function() {
                Chapter.destroyAll(function() {
                    Author.destroyAll(function() {
                        Reader.destroyAll(done);
                    });
                });
            });
        });
    });

    after(function() {
        db.disconnect();
    });

    describe('hasMany', function() {
        it('can be declared in different ways', function(done) {
            Book.hasMany(Chapter);
            Book.hasMany(Reader, {as: 'users'});
            Book.hasMany(Author, {foreignKey: 'projectId'});
            var b = new Book;
            b.chapters.should.be.an.instanceOf(Function);
            b.users.should.be.an.instanceOf(Function);
            b.authors.should.be.an.instanceOf(Function);
            Object.keys((new Chapter).toObject()).should.include('bookId');
            Object.keys((new Author).toObject()).should.include('projectId');

            db.automigrate(done);
        });

        it('can be declared in short form', function(done) {
            Author.hasMany('readers');
            (new Author).readers.should.be.an.instanceOf(Function);
            Object.keys((new Reader).toObject()).should.include('authorId');

            db.autoupdate(done);
        });

        it('should build record on scope', function(done) {
            Book.create(function(err, book) {
                var c = book.chapters.build();
                c.bookId.should.equal(book.id);
                c.save(done);
            });
        });

        it('should create record on scope', function(done) {
            Book.create(function(err, book) {
                book.chapters.create(function(err, c) {
                    should.not.exist(err);
                    should.exist(c);
                    c.bookId.should.equal(book.id);
                    done();
                });
            });
        });

        it.skip('should fetch all scoped instances', function(done) {
            Book.create(function(err, book) {
                book.chapters.create({name: 'a'}, function() {
                    book.chapters.create({name: 'z'}, function() {
                        book.chapters.create({name: 'c'}, function() {
                            fetch(book);
                        });
                    });
                });
            });

            function fetch(book) {
                book.chapters(function(err, ch) {
                    should.not.exist(err);
                    should.exist(ch);
                    ch.should.have.lengthOf(3);

                    book.chapters({order: 'name DESC'}, function(e, c) {
                        should.not.exist(e);
                        should.exist(c);
                        c.shift().name.should.equal('z');
                        c.pop().name.should.equal('a');
                        done();
                    });
                });
            }
        });

        it('should find scoped record', function(done) {
            var id;
            Book.create(function(err, book) {
                book.chapters.create({name: 'a'}, function(err, ch) {
                    id = ch.id;
                    book.chapters.create({name: 'z'}, function() {
                        book.chapters.create({name: 'c'}, function() {
                            fetch(book);
                        });
                    });
                });
            });

            function fetch(book) {
                book.chapters.find(id, function(err, ch) {
                    should.not.exist(err);
                    should.exist(ch);
                    ch.id.should.equal(id);
                    done();
                });
            }
        });
    });

    describe('belongsTo', function() {
        var List, Item, Fear, Mind;

        it('can be declared in different ways', function() {
            List = db.define('List', {name: String});
            Item = db.define('Item', {name: String});
            Fear = db.define('Fear');
            Mind = db.define('Mind');

            // syntax 1 (old)
            Item.belongsTo(List);
            Object.keys((new Item).toObject()).should.include('listId');
            (new Item).list.should.be.an.instanceOf(Function);

            // syntax 2 (new)
            Fear.belongsTo('mind');
            Object.keys((new Fear).toObject()).should.include('mindId');
            (new Fear).mind.should.be.an.instanceOf(Function);
            // (new Fear).mind.build().should.be.an.instanceOf(Mind);
        });

        it('can be used to query data', function(done) {
            List.hasMany('todos', {model: Item});
            db.automigrate(function() {
                List.create(function(e, list) {
                    should.not.exist(e);
                    should.exist(list);
                    list.todos.create(function(err, todo) {
                        todo.list(function(e, l) {
                            should.not.exist(e);
                            should.exist(l);
                            done();
                        });
                    });
                });
            });
        });
    });

    describe('hasAndBelongsToMany', function() {
        var Article, Tag, ArticleTag;
        it('can be declared', function(done) {
            Article = db.define('Article', {title: String});
            Tag = db.define('Tag', {name: String});
            Article.hasAndBelongsToMany('tags');
            ArticleTag = db.models.ArticleTag;
            db.automigrate(function() {
                Article.destroyAll(function() {
                    Tag.destroyAll(function() {
                        ArticleTag.destroyAll(done)
                    });
                });
            });
        });

        it('should allow to create instances on scope', function(done) {
            Article.create(function(e, article) {
                article.tags.create({name: 'popular'}, function(e, t) {
                    t.should.be.an.instanceOf(Tag);
                    ArticleTag.findOne(function(e, at) {
                        should.exist(at);
                        at.tagId.should.equal(t.id);
                        at.articleId.should.equal(article.id);
                        done();
                    });
                });
            });
        });

        it('should allow to fetch scoped instances', function(done) {
            Article.findOne(function(e, article) {
                article.tags(function(e, tags) {
                    should.not.exist(e);
                    should.exist(tags);
                    done();
                });
            });
        });

        it('should allow to add connection with instance', function(done) {
            Article.findOne(function(e, article) {
                Tag.create({name: 'awesome'}, function(e, tag) {
                    article.tags.add(tag, function(e, at) {
                        should.not.exist(e);
                        should.exist(at);
                        at.should.be.an.instanceOf(ArticleTag);
                        at.tagId.should.equal(tag.id);
                        at.articleId.should.equal(article.id);
                        done();
                    });
                });
            });
        });

        it('should allow to remove connection with instance', function(done) {
            Article.findOne(function(e, article) {
                article.tags(function(e, tags) {
                    var len = tags.length;
                    article.tags.remove(tags[0], function(e, at) {
                        should.not.exist(e);
                        article.tags(true, function(e, tags) {
                            tags.should.have.lengthOf(len - 1);
                            done();
                        });
                    });
                });
            });
        });

    });

});
<commit_msg>Check types of sync and async getters created by belongsTo, close #266<commit_after>// This test written in mocha+should.js
var should = require('./init.js');

var db, Book, Chapter, Author, Reader;

describe('relations', function() {
    before(function(done) {
        db = getSchema();
        Book = db.define('Book', {name: String});
        Chapter = db.define('Chapter', {name: {type: String, index: true}});
        Author = db.define('Author', {name: String});
        Reader = db.define('Reader', {name: String});

        db.automigrate(function() {
            Book.destroyAll(function() {
                Chapter.destroyAll(function() {
                    Author.destroyAll(function() {
                        Reader.destroyAll(done);
                    });
                });
            });
        });
    });

    after(function() {
        db.disconnect();
    });

    describe('hasMany', function() {
        it('can be declared in different ways', function(done) {
            Book.hasMany(Chapter);
            Book.hasMany(Reader, {as: 'users'});
            Book.hasMany(Author, {foreignKey: 'projectId'});
            var b = new Book;
            b.chapters.should.be.an.instanceOf(Function);
            b.users.should.be.an.instanceOf(Function);
            b.authors.should.be.an.instanceOf(Function);
            Object.keys((new Chapter).toObject()).should.include('bookId');
            Object.keys((new Author).toObject()).should.include('projectId');

            db.automigrate(done);
        });

        it('can be declared in short form', function(done) {
            Author.hasMany('readers');
            (new Author).readers.should.be.an.instanceOf(Function);
            Object.keys((new Reader).toObject()).should.include('authorId');

            db.autoupdate(done);
        });

        it('should build record on scope', function(done) {
            Book.create(function(err, book) {
                var c = book.chapters.build();
                c.bookId.should.equal(book.id);
                c.save(done);
            });
        });

        it('should create record on scope', function(done) {
            Book.create(function(err, book) {
                book.chapters.create(function(err, c) {
                    should.not.exist(err);
                    should.exist(c);
                    c.bookId.should.equal(book.id);
                    done();
                });
            });
        });

        it.skip('should fetch all scoped instances', function(done) {
            Book.create(function(err, book) {
                book.chapters.create({name: 'a'}, function() {
                    book.chapters.create({name: 'z'}, function() {
                        book.chapters.create({name: 'c'}, function() {
                            fetch(book);
                        });
                    });
                });
            });
            function fetch(book) {
                book.chapters(function(err, ch) {
                    should.not.exist(err);
                    should.exist(ch);
                    ch.should.have.lengthOf(3);

                    book.chapters({order: 'name DESC'}, function(e, c) {
                        should.not.exist(e);
                        should.exist(c);
                        c.shift().name.should.equal('z');
                        c.pop().name.should.equal('a');
                        done();
                    });
                });
            }
        });

        it('should find scoped record', function(done) {
            var id;
            Book.create(function(err, book) {
                book.chapters.create({name: 'a'}, function(err, ch) {
                    id = ch.id;
                    book.chapters.create({name: 'z'}, function() {
                        book.chapters.create({name: 'c'}, function() {
                            fetch(book);
                        });
                    });
                });
            });

            function fetch(book) {
                book.chapters.find(id, function(err, ch) {
                    should.not.exist(err);
                    should.exist(ch);
                    ch.id.should.equal(id);
                    done();
                });
            }
        });
    });

    describe('belongsTo', function() {
        var List, Item, Fear, Mind;

        it('can be declared in different ways', function() {
            List = db.define('List', {name: String});
            Item = db.define('Item', {name: String});
            Fear = db.define('Fear');
            Mind = db.define('Mind');

            // syntax 1 (old)
            Item.belongsTo(List);
            Object.keys((new Item).toObject()).should.include('listId');
            (new Item).list.should.be.an.instanceOf(Function);

            // syntax 2 (new)
            Fear.belongsTo('mind');
            Object.keys((new Fear).toObject()).should.include('mindId');
            (new Fear).mind.should.be.an.instanceOf(Function);
            // (new Fear).mind.build().should.be.an.instanceOf(Mind);
        });

        it('can be used to query data', function(done) {
            List.hasMany('todos', {model: Item});
            db.automigrate(function() {
                List.create(function(e, list) {
                    should.not.exist(e);
                    should.exist(list);
                    list.todos.create(function(err, todo) {
                        todo.list(function(e, l) {
                            should.not.exist(e);
                            should.exist(l);
                            l.should.be.an.instanceOf(List);
                            todo.list().should.equal(l.id);
                            done();
                        });
                    });
                });
            });
        });

    });

    describe('hasAndBelongsToMany', function() {
        var Article, Tag, ArticleTag;
        it('can be declared', function(done) {
            Article = db.define('Article', {title: String});
            Tag = db.define('Tag', {name: String});
            Article.hasAndBelongsToMany('tags');
            ArticleTag = db.models.ArticleTag;
            db.automigrate(function() {
                Article.destroyAll(function() {
                    Tag.destroyAll(function() {
                        ArticleTag.destroyAll(done)
                    });
                });
            });
        });

        it('should allow to create instances on scope', function(done) {
            Article.create(function(e, article) {
                article.tags.create({name: 'popular'}, function(e, t) {
                    t.should.be.an.instanceOf(Tag);
                    ArticleTag.findOne(function(e, at) {
                        should.exist(at);
                        at.tagId.should.equal(t.id);
                        at.articleId.should.equal(article.id);
                        done();
                    });
                });
            });
        });

        it('should allow to fetch scoped instances', function(done) {
            Article.findOne(function(e, article) {
                article.tags(function(e, tags) {
                    should.not.exist(e);
                    should.exist(tags);
                    done();
                });
            });
        });

        it('should allow to add connection with instance', function(done) {
            Article.findOne(function(e, article) {
                Tag.create({name: 'awesome'}, function(e, tag) {
                    article.tags.add(tag, function(e, at) {
                        should.not.exist(e);
                        should.exist(at);
                        at.should.be.an.instanceOf(ArticleTag);
                        at.tagId.should.equal(tag.id);
                        at.articleId.should.equal(article.id);
                        done();
                    });
                });
            });
        });

        it('should allow to remove connection with instance', function(done) {
            Article.findOne(function(e, article) {
                article.tags(function(e, tags) {
                    var len = tags.length;
                    article.tags.remove(tags[0], function(e, at) {
                        should.not.exist(e);
                        article.tags(true, function(e, tags) {
                            tags.should.have.lengthOf(len - 1);
                            done();
                        });
                    });
                });
            });
        });

    });

});
<|endoftext|>


<commit_before>import React from 'react';
import Link from 'react-router/lib/Link';
import IconButton from 'material-ui/IconButton';
import { injectIntl } from 'react-intl';
import messages from '../../resources/messages';
import ExploreIcon from './icons/ExploreIcon';
import DownloadIcon from './icons/DownloadIcon';
import HelpIcon from './icons/HelpIcon';

/**
 * The wrapper for our custom icons.  The idea is that you define all the SVG icons in individual
 * Components in the `icons` directory, then export wrapper instances of them here.
 */
function composeIconButton(Icon, tooltipMessage) {
  class AppIconButton extends React.Component {
    handleClick = (event) => {
      const { onClick } = this.props;
      event.preventDefault();
      if (onClick) {
        onClick();
      }
    }
    render() {
      const { linkTo, onClick } = this.props;
      const { formatMessage } = this.props.intl;
      const linkTarget = linkTo || formatMessage(tooltipMessage);
      const clickHandler = (onClick) ? this.handleClick : null;
      return (
        <Link to={linkTarget} onClick={clickHandler} className="icon-button-link" name={formatMessage(tooltipMessage)}>
          <IconButton
            tooltip={formatMessage(tooltipMessage)}
            style={{ padding: 0, border: 0, width: 26, height: 26 }}
            tooltipStyles={{ top: 20 }}
          >
            <Icon />
          </IconButton>
        </Link>
      );
    }
  }
  AppIconButton.propTypes = {
    onClick: React.PropTypes.func,
    linkTo: React.PropTypes.string,
    intl: React.PropTypes.object.isRequired,
  };
  return injectIntl(AppIconButton);
}

export const ExploreButton = composeIconButton(ExploreIcon, messages.explore);

export const DownloadButton = composeIconButton(DownloadIcon, messages.download);

export const HelpButton = composeIconButton(HelpIcon, messages.help);
<commit_msg>fix Safari double-tap bug<commit_after>import React from 'react';
import Link from 'react-router/lib/Link';
import IconButton from 'material-ui/IconButton';
import { injectIntl } from 'react-intl';
import messages from '../../resources/messages';
import ExploreIcon from './icons/ExploreIcon';
import DownloadIcon from './icons/DownloadIcon';
import HelpIcon from './icons/HelpIcon';

/**
 * The wrapper for our custom icons.  The idea is that you define all the SVG icons in individual
 * Components in the `icons` directory, then export wrapper instances of them here.
 */
function composeIconButton(Icon, tooltipMessage) {
  class AppIconButton extends React.Component {
    handleClick = (event) => {
      const { onClick } = this.props;
      event.preventDefault();
      if (onClick) {
        onClick();
      }
    }
    render() {
      const { linkTo, onClick } = this.props;
      const { formatMessage } = this.props.intl;
      const linkTarget = linkTo || formatMessage(tooltipMessage);
      const clickHandler = (onClick) ? this.handleClick : null;
      return (
        <Link to={linkTarget} onTouchTap={clickHandler} className="icon-button-link" name={formatMessage(tooltipMessage)}>
          <IconButton
            tooltip={formatMessage(tooltipMessage)}
            style={{ padding: 0, border: 0, width: 26, height: 26 }}
            tooltipStyles={{ top: 20 }}
          >
            <Icon />
          </IconButton>
        </Link>
      );
    }
  }
  AppIconButton.propTypes = {
    onClick: React.PropTypes.func,
    linkTo: React.PropTypes.string,
    intl: React.PropTypes.object.isRequired,
  };
  return injectIntl(AppIconButton);
}

export const ExploreButton = composeIconButton(ExploreIcon, messages.explore);

export const DownloadButton = composeIconButton(DownloadIcon, messages.download);

export const HelpButton = composeIconButton(HelpIcon, messages.help);
<|endoftext|>


<commit_before>// Generated on 2013-07-10 using generator-webapp 0.2.6
'use strict';
var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT});
var mountFolder = function (connect, dir) {
    return connect.static(require('path').resolve(dir));
};

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {
    // load all grunt tasks
    require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

    // configurable paths
    var yeomanConfig = {
        app: 'app',
        dist: 'dist'
    };

    grunt.initConfig({
        yeoman: yeomanConfig,
        watch: {
            coffee: {
                files: ['<%= yeoman.app %>/scripts/{,*/}*.coffee'],
                tasks: ['coffee:dist']
            },
            coffeeTest: {
                files: ['test/spec/{,*/}*.coffee'],
                tasks: ['coffee:test']
            },
            compass: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['compass:server', 'autoprefixer' ]
            },
            styles: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
                tasks: ['copy:styles', 'autoprefixer']
            },
            livereload: {
                options: {
                    livereload: LIVERELOAD_PORT
                },
                files: [
                    '<%= yeoman.app %>/*.html',
                    '.tmp/styles/{,*/}*.css',
                    '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
                    '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
                ]
            }
        },
        connect: {
            options: {
                port: 9000,
                // change this to '0.0.0.0' to access the server from outside
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    middleware: function (connect) {
                        return [
                            lrSnippet,
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, yeomanConfig.app)
                        ];
                    }
                }
            },
            test: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, 'test')
                        ];
                    }
                }
            },
            dist: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, yeomanConfig.dist)
                        ];
                    }
                }
            }
        },
        open: {
            server: {
                path: 'http://localhost:<%= connect.options.port %>'
            }
        },
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%= yeoman.dist %>/*',
                        '!<%= yeoman.dist %>/.git*'
                    ]
                }]
            },
            server: '.tmp'
        },
        jshint: {
            options: {
                jshintrc: '.jshintrc'
            },
            all: [
                'Gruntfile.js',
                '<%= yeoman.app %>/scripts/{,*/}*.js',
                '!<%= yeoman.app %>/scripts/vendor/*',
                'test/spec/{,*/}*.js'
            ]
        },
        mocha: {
            all: {
                options: {
                    run: true,
                    urls: ['http://localhost:<%= connect.options.port %>/index.html']
                }
            }
        },
        coffee: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/scripts',
                    src: '{,*/}*.coffee',
                    dest: '.tmp/scripts',
                    ext: '.js'
                }]
            },
            test: {
                files: [{
                    expand: true,
                    cwd: 'test/spec',
                    src: '{,*/}*.coffee',
                    dest: '.tmp/spec',
                    ext: '.js'
                }]
            }
        },
        compass: {
            options: {
                sassDir: '<%= yeoman.app %>/styles',
                cssDir: '.tmp/styles',
                generatedImagesDir: '.tmp/images/generated',
                imagesDir: '<%= yeoman.app %>/images',
                javascriptsDir: '<%= yeoman.app %>/scripts',
                fontsDir: '<%= yeoman.app %>/styles/fonts',
                importPath: '<%= yeoman.app %>/bower_components',
                httpImagesPath: '/images',
                httpGeneratedImagesPath: '/images/generated',
                httpFontsPath: '/styles/fonts',
                relativeAssets: false
            },
            dist: {},
            server: {
                options: {
                    debugInfo: true
                }
            }
        },
        autoprefixer: {
            options: {
                browsers: ['last 1 version']
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/styles/',
                    src: '{,*/}*.css',
                    dest: '.tmp/styles/'
                }]
            }
        },
        // not used since Uglify task does concat,
        // but still available if needed
        concat: {
            dist: {
                files: {
                    'app/bower_components/qTip2/dist/jquery.qtip.js': [
                        'app/bower_components/qTip2/src/core/intro.js',
                        'app/bower_components/qTip2/src/core/constants.js',
                        'app/bower_components/qTip2/src/core/class.js',

                        'app/bower_components/qTip2/src/core/options.js',
                        'app/bower_components/qTip2/src/core/content.js',
                        'app/bower_components/qTip2/src/core/position.js',
                        'app/bower_components/qTip2/src/core/toggle.js',
                        'app/bower_components/qTip2/src/core/focus.js',
                        'app/bower_components/qTip2/src/core/disable.js',
                        'app/bower_components/qTip2/src/core/button.js',
                        'app/bower_components/qTip2/src/core/style.js',
                        'app/bower_components/qTip2/src/core/events.js',

                        'app/bower_components/qTip2/src/core/jquery_methods.js',
                        'app/bower_components/qTip2/src/core/jquery_overrides.js',

                        'app/bower_components/qTip2/src/core/defaults.js',

                        'app/bower_components/qTip2/src/tips/tips.js',
                        'app/bower_components/qTip2/src/viewport/viewport.js',

                        'app/bower_components/qTip2/src/core/outro.js',

                        'app/bower_components/imagesloaded/jquery.imagesloaded.js'
                    ],
                    'app/bower_components/qTip2/src/_core.scss': 'app/bower_components/qTip2/src/core.css',
                    'app/bower_components/qTip2/src/_css3.scss': 'app/bower_components/qTip2/src/css3.css',
                    'app/bower_components/qTip2/src/tips/_tips.scss': 'app/bower_components/qTip2/src/tips/tips.css',
                    'app/bower_components/select2/_select2.scss': 'app/bower_components/select2/select2.css'
                }
            }
        },
        requirejs: {
            dist: {
                // Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js
                options: {
                    // `name` and `out` is set by grunt-usemin
                    baseUrl: yeomanConfig.app + '/scripts',
                    optimize: 'none',
                    // TODO: Figure out how to make sourcemaps work with grunt-usemin
                    // https://github.com/yeoman/grunt-usemin/issues/30
                    //generateSourceMaps: true,
                    // required to support SourceMaps
                    // http://requirejs.org/docs/errors.html#sourcemapcomments
                    paths: {
                        timetableData: 'empty:'
                    },
                    preserveLicenseComments: false,
                    useStrict: true,
                    wrap: true
                    //uglify2: {} // https://github.com/mishoo/UglifyJS2
                }
            }
        },
        rev: {
            dist: {
                files: {
                    src: [
                        //'<%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}',
                        '<%= yeoman.dist %>/styles/fonts/*'
                    ]
                }
            }
        },
        useminPrepare: {
            options: {
                dest: '<%= yeoman.dist %>'
            },
            html: '<%= yeoman.app %>/index.html'
        },
        usemin: {
            options: {
                dirs: ['<%= yeoman.dist %>']
            },
            html: ['<%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%= yeoman.dist %>/styles/{,*/}*.css']
        },
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg}',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        cssmin: {
            // This task is pre-configured if you do not wish to use Usemin
            // blocks for your CSS. By default, the Usemin block from your
            // `index.html` will take care of minification, e.g.
            //
            //     <!-- build:css({.tmp,app}) styles/main.css -->
            //
            dist: {
                files: {
                    '<%= yeoman.dist %>/styles/exports.css': '.tmp/styles/exports.css'
                }
            }
        },
        htmlmin: {
            dist: {
                options: {
                    /*removeCommentsFromCDATA: true,
                    // https://github.com/yeoman/grunt-usemin/issues/44
                    //collapseWhitespace: true,
                    collapseBooleanAttributes: true,
                    removeAttributeQuotes: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true*/
                },
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>',
                    src: '*.html',
                    dest: '<%= yeoman.dist %>'
                }]
            }
        },
        // Put files not handled in other tasks here
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{config,ico,php,png,txt}',
                        '.htaccess',
                        'bower_components/Downloadify/media/downloadify.swf',
                        'bower_components/ZeroClipboard/ZeroClipboard.swf',
                        'images/{,*/}*.{webp,gif}',
                        'json/*',
                        'scripts/nus_timetable_data.js',
                        'snappy/**',
                        'styles/*.{gif,png}',
                        'styles/fonts/*'
                    ]
                }, {
                    expand: true,
                    cwd: '.tmp/images',
                    dest: '<%= yeoman.dist %>/images',
                    src: [
                        'generated/*'
                    ]
                }]
            },
            styles: {
                expand: true,
                dot: true,
                cwd: '<%= yeoman.app %>/styles',
                dest: '.tmp/styles/',
                src: '{,*/}*.css'
            }
        },
        concurrent: {
            server: [
                'compass',
                'coffee:dist',
                'copy:styles'
            ],
            test: [
                'coffee',
                'copy:styles'
            ],
            dist: [
                'coffee',
                'compass',
                'copy:styles',
                'imagemin',
                'svgmin',
                'htmlmin'
            ]
        },
        bower: {
            options: {
                exclude: ['modernizr']
            },
            all: {
                rjsConfig: '<%= yeoman.app %>/scripts/main.js'
            }
        },
        crawlNTU: {
            academicYear: 2012,
            baseUrl: 'https://wish.wis.ntu.edu.sg/webexe/owa/',
            cachePath: 'cache',
            dest: 'app/json/ntu_module_info.json',
            maxCacheAge: 24 * 60 * 60, // in seconds
            maxConcurrentFiles: 64,
            maxConcurrentSockets: 8,
            refresh: false,
            semester: 2
        },
        crawlNUS: {
            academicYear: 2012,
            baseUrl: 'https://sit.aces01.nus.edu.sg/cors/jsp/report/',
            cachePath: 'cache',
            dest: 'app/json/nus_module_info.json',
            maxCacheAge: 24 * 60 * 60, // in seconds
            maxConcurrentFiles: 64,
            maxConcurrentSockets: 8,
            refresh: false,
            semester: 2
        },
        download: {
            ntu: {
                options: {
                    dest: 'app/json/ntu_module_info.json',
                    src: 'http://nusmods.com/json/ntu_module_info.json'
                }
            },
            nus: {
                options: {
                    dest: 'app/json/nus_module_info.json',
                    src: 'http://nusmods.com/json/nus_module_info.json'
                }
            }
        },
        jsifyNTU: {
            destModuleFinder: 'app/scripts/ntu_module_data.js',
            destTimetable: 'app/scripts/ntu_timetable_data.js',
            src: 'app/json/ntu_module_info.json'
        },
        jsifyNUS: {
            destModuleFinder: 'app/scripts/nus_module_data.js',
            destTimetable: 'app/scripts/nus_timetable_data.js',
            src: 'app/json/nus_module_info.json'
        }
    });

    grunt.loadTasks('tasks');

    grunt.registerTask('server', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'open', 'connect:dist:keepalive']);
        }

        grunt.task.run([
            'clean:server',
            'concat',
            'concurrent:server',
            'autoprefixer',
            'connect:livereload',
            'open',
            'watch'
        ]);
    });

    grunt.registerTask('test', [
        'clean:server',
        'concurrent:test',
        'autoprefixer',
        'connect:test',
        'mocha'
    ]);

    grunt.registerTask('build', [
        'clean:dist',
        'useminPrepare',
        'concat',
        'concurrent:dist',
        'autoprefixer',
        'requirejs',
        'concat',
        'cssmin',
        'uglify',
        'copy:dist',
        'rev',
        'usemin'
    ]);

    grunt.registerTask('default', [
        //'jshint',
        //'test',
        'build'
    ]);
};
<commit_msg>Do not concatenate imagesLoaded with qTip2<commit_after>// Generated on 2013-07-10 using generator-webapp 0.2.6
'use strict';
var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT});
var mountFolder = function (connect, dir) {
    return connect.static(require('path').resolve(dir));
};

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {
    // load all grunt tasks
    require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

    // configurable paths
    var yeomanConfig = {
        app: 'app',
        dist: 'dist'
    };

    grunt.initConfig({
        yeoman: yeomanConfig,
        watch: {
            coffee: {
                files: ['<%= yeoman.app %>/scripts/{,*/}*.coffee'],
                tasks: ['coffee:dist']
            },
            coffeeTest: {
                files: ['test/spec/{,*/}*.coffee'],
                tasks: ['coffee:test']
            },
            compass: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['compass:server', 'autoprefixer' ]
            },
            styles: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
                tasks: ['copy:styles', 'autoprefixer']
            },
            livereload: {
                options: {
                    livereload: LIVERELOAD_PORT
                },
                files: [
                    '<%= yeoman.app %>/*.html',
                    '.tmp/styles/{,*/}*.css',
                    '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
                    '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
                ]
            }
        },
        connect: {
            options: {
                port: 9000,
                // change this to '0.0.0.0' to access the server from outside
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    middleware: function (connect) {
                        return [
                            lrSnippet,
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, yeomanConfig.app)
                        ];
                    }
                }
            },
            test: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, 'test')
                        ];
                    }
                }
            },
            dist: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, yeomanConfig.dist)
                        ];
                    }
                }
            }
        },
        open: {
            server: {
                path: 'http://localhost:<%= connect.options.port %>'
            }
        },
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%= yeoman.dist %>/*',
                        '!<%= yeoman.dist %>/.git*'
                    ]
                }]
            },
            server: '.tmp'
        },
        jshint: {
            options: {
                jshintrc: '.jshintrc'
            },
            all: [
                'Gruntfile.js',
                '<%= yeoman.app %>/scripts/{,*/}*.js',
                '!<%= yeoman.app %>/scripts/vendor/*',
                'test/spec/{,*/}*.js'
            ]
        },
        mocha: {
            all: {
                options: {
                    run: true,
                    urls: ['http://localhost:<%= connect.options.port %>/index.html']
                }
            }
        },
        coffee: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/scripts',
                    src: '{,*/}*.coffee',
                    dest: '.tmp/scripts',
                    ext: '.js'
                }]
            },
            test: {
                files: [{
                    expand: true,
                    cwd: 'test/spec',
                    src: '{,*/}*.coffee',
                    dest: '.tmp/spec',
                    ext: '.js'
                }]
            }
        },
        compass: {
            options: {
                sassDir: '<%= yeoman.app %>/styles',
                cssDir: '.tmp/styles',
                generatedImagesDir: '.tmp/images/generated',
                imagesDir: '<%= yeoman.app %>/images',
                javascriptsDir: '<%= yeoman.app %>/scripts',
                fontsDir: '<%= yeoman.app %>/styles/fonts',
                importPath: '<%= yeoman.app %>/bower_components',
                httpImagesPath: '/images',
                httpGeneratedImagesPath: '/images/generated',
                httpFontsPath: '/styles/fonts',
                relativeAssets: false
            },
            dist: {},
            server: {
                options: {
                    debugInfo: true
                }
            }
        },
        autoprefixer: {
            options: {
                browsers: ['last 1 version']
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/styles/',
                    src: '{,*/}*.css',
                    dest: '.tmp/styles/'
                }]
            }
        },
        // not used since Uglify task does concat,
        // but still available if needed
        concat: {
            dist: {
                files: {
                    'app/bower_components/qTip2/dist/jquery.qtip.js': [
                        'app/bower_components/qTip2/src/core/intro.js',
                        'app/bower_components/qTip2/src/core/constants.js',
                        'app/bower_components/qTip2/src/core/class.js',

                        'app/bower_components/qTip2/src/core/options.js',
                        'app/bower_components/qTip2/src/core/content.js',
                        'app/bower_components/qTip2/src/core/position.js',
                        'app/bower_components/qTip2/src/core/toggle.js',
                        'app/bower_components/qTip2/src/core/focus.js',
                        'app/bower_components/qTip2/src/core/disable.js',
                        'app/bower_components/qTip2/src/core/button.js',
                        'app/bower_components/qTip2/src/core/style.js',
                        'app/bower_components/qTip2/src/core/events.js',

                        'app/bower_components/qTip2/src/core/jquery_methods.js',
                        'app/bower_components/qTip2/src/core/jquery_overrides.js',

                        'app/bower_components/qTip2/src/core/defaults.js',

                        'app/bower_components/qTip2/src/tips/tips.js',
                        'app/bower_components/qTip2/src/viewport/viewport.js',

                        'app/bower_components/qTip2/src/core/outro.js'
                    ],
                    'app/bower_components/qTip2/src/_core.scss': 'app/bower_components/qTip2/src/core.css',
                    'app/bower_components/qTip2/src/_css3.scss': 'app/bower_components/qTip2/src/css3.css',
                    'app/bower_components/qTip2/src/tips/_tips.scss': 'app/bower_components/qTip2/src/tips/tips.css',
                    'app/bower_components/select2/_select2.scss': 'app/bower_components/select2/select2.css'
                }
            }
        },
        requirejs: {
            dist: {
                // Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js
                options: {
                    // `name` and `out` is set by grunt-usemin
                    baseUrl: yeomanConfig.app + '/scripts',
                    optimize: 'none',
                    // TODO: Figure out how to make sourcemaps work with grunt-usemin
                    // https://github.com/yeoman/grunt-usemin/issues/30
                    //generateSourceMaps: true,
                    // required to support SourceMaps
                    // http://requirejs.org/docs/errors.html#sourcemapcomments
                    paths: {
                        timetableData: 'empty:'
                    },
                    preserveLicenseComments: false,
                    useStrict: true,
                    wrap: true
                    //uglify2: {} // https://github.com/mishoo/UglifyJS2
                }
            }
        },
        rev: {
            dist: {
                files: {
                    src: [
                        //'<%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}',
                        '<%= yeoman.dist %>/styles/fonts/*'
                    ]
                }
            }
        },
        useminPrepare: {
            options: {
                dest: '<%= yeoman.dist %>'
            },
            html: '<%= yeoman.app %>/index.html'
        },
        usemin: {
            options: {
                dirs: ['<%= yeoman.dist %>']
            },
            html: ['<%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%= yeoman.dist %>/styles/{,*/}*.css']
        },
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg}',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        cssmin: {
            // This task is pre-configured if you do not wish to use Usemin
            // blocks for your CSS. By default, the Usemin block from your
            // `index.html` will take care of minification, e.g.
            //
            //     <!-- build:css({.tmp,app}) styles/main.css -->
            //
            dist: {
                files: {
                    '<%= yeoman.dist %>/styles/exports.css': '.tmp/styles/exports.css'
                }
            }
        },
        htmlmin: {
            dist: {
                options: {
                    /*removeCommentsFromCDATA: true,
                    // https://github.com/yeoman/grunt-usemin/issues/44
                    //collapseWhitespace: true,
                    collapseBooleanAttributes: true,
                    removeAttributeQuotes: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true*/
                },
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>',
                    src: '*.html',
                    dest: '<%= yeoman.dist %>'
                }]
            }
        },
        // Put files not handled in other tasks here
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{config,ico,php,png,txt}',
                        '.htaccess',
                        'bower_components/Downloadify/media/downloadify.swf',
                        'bower_components/ZeroClipboard/ZeroClipboard.swf',
                        'images/{,*/}*.{webp,gif}',
                        'json/*',
                        'scripts/nus_timetable_data.js',
                        'snappy/**',
                        'styles/*.{gif,png}',
                        'styles/fonts/*'
                    ]
                }, {
                    expand: true,
                    cwd: '.tmp/images',
                    dest: '<%= yeoman.dist %>/images',
                    src: [
                        'generated/*'
                    ]
                }]
            },
            styles: {
                expand: true,
                dot: true,
                cwd: '<%= yeoman.app %>/styles',
                dest: '.tmp/styles/',
                src: '{,*/}*.css'
            }
        },
        concurrent: {
            server: [
                'compass',
                'coffee:dist',
                'copy:styles'
            ],
            test: [
                'coffee',
                'copy:styles'
            ],
            dist: [
                'coffee',
                'compass',
                'copy:styles',
                'imagemin',
                'svgmin',
                'htmlmin'
            ]
        },
        bower: {
            options: {
                exclude: ['modernizr']
            },
            all: {
                rjsConfig: '<%= yeoman.app %>/scripts/main.js'
            }
        },
        crawlNTU: {
            academicYear: 2012,
            baseUrl: 'https://wish.wis.ntu.edu.sg/webexe/owa/',
            cachePath: 'cache',
            dest: 'app/json/ntu_module_info.json',
            maxCacheAge: 24 * 60 * 60, // in seconds
            maxConcurrentFiles: 64,
            maxConcurrentSockets: 8,
            refresh: false,
            semester: 2
        },
        crawlNUS: {
            academicYear: 2012,
            baseUrl: 'https://sit.aces01.nus.edu.sg/cors/jsp/report/',
            cachePath: 'cache',
            dest: 'app/json/nus_module_info.json',
            maxCacheAge: 24 * 60 * 60, // in seconds
            maxConcurrentFiles: 64,
            maxConcurrentSockets: 8,
            refresh: false,
            semester: 2
        },
        download: {
            ntu: {
                options: {
                    dest: 'app/json/ntu_module_info.json',
                    src: 'http://nusmods.com/json/ntu_module_info.json'
                }
            },
            nus: {
                options: {
                    dest: 'app/json/nus_module_info.json',
                    src: 'http://nusmods.com/json/nus_module_info.json'
                }
            }
        },
        jsifyNTU: {
            destModuleFinder: 'app/scripts/ntu_module_data.js',
            destTimetable: 'app/scripts/ntu_timetable_data.js',
            src: 'app/json/ntu_module_info.json'
        },
        jsifyNUS: {
            destModuleFinder: 'app/scripts/nus_module_data.js',
            destTimetable: 'app/scripts/nus_timetable_data.js',
            src: 'app/json/nus_module_info.json'
        }
    });

    grunt.loadTasks('tasks');

    grunt.registerTask('server', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'open', 'connect:dist:keepalive']);
        }

        grunt.task.run([
            'clean:server',
            'concat',
            'concurrent:server',
            'autoprefixer',
            'connect:livereload',
            'open',
            'watch'
        ]);
    });

    grunt.registerTask('test', [
        'clean:server',
        'concurrent:test',
        'autoprefixer',
        'connect:test',
        'mocha'
    ]);

    grunt.registerTask('build', [
        'clean:dist',
        'useminPrepare',
        'concat',
        'concurrent:dist',
        'autoprefixer',
        'requirejs',
        'concat',
        'cssmin',
        'uglify',
        'copy:dist',
        'rev',
        'usemin'
    ]);

    grunt.registerTask('default', [
        //'jshint',
        //'test',
        'build'
    ]);
};
<|endoftext|>


<commit_before>const got = require('got');
const request = require('request');
const Buffer = require('Buffer');

const crypto = require('crypto');
const companyCode = 88801;
const Oauth = require('client-oauth2');
const cID = '268b2069-b099-4fa2-8148-1f1c0327fe63';
const cS = 'b383c35d-3c11-4ce6-b631-8767f4c2084b';
const aK = '0dfb11cd-b140-40cd-b65a-220e9998a129';
const aS = '199505e4-9d5f-4ba9-bb96-a3ea8b2f69c1';

const auth = new Oauth({
  clientId: cID,
  clientSecret: cS,
  accessTokenUri: 'https://api.finhacks.id/api/oauth/token',
});

function getAccessToken (res) {

  let header = {
    Authorization: 'Basic ' + new Buffer(cID + ':' + cS).toString('base64'),
    'Content-Type': 'application/x-www-form-urlencoded',
  };

  let body = { grant_type: 'client_credentials' };
  let options = {
    method: 'POST',
    url: 'https://api.finhacks.id/api/oauth/token',
    headers: header,
  };
  request.post(options).form(body)
    .on('body', function(body) {
      res.send(body);
    })
    .on('error', function(error) {
      console.log(error);
    });
}
// function gat () {
//   return auth.credentials.getToken()
//     .then(function(response){
//       return response;
//     })
// }
// function getAccessToken () {
//   return auth.credentials.getToken();
// }

function createHeader (data, body){
  let method = 'post'.toUpperCase();
  let url = encodeURI('/ewallet/customers');
  let accessToken = data.accessToken;
  let bd = crypto.createHash('SHA-256', body);
  bd = bd.toLowerCase();

  let dt = new Date();
  dt = dt.toISOString();

  let str = method + ':' + url + ':' + accessToken + ':' + bd + ':' + dt;

  let hash = crypto.createHmac('SHA_256', aS, str);

  var header = {
    'Authorization' : 'Bearer '+accessToken,
    'X-BCA-Key' : aK,
    'X-BCA-Signature' : hash,
    'X-BCA-Timestamp' : dT
  }

    return header;
}

function registerUser (req) {
  let body = {};

  body.CustomerName = req.customer_name;
  body.DateOfBirth = req.birth_date;
  body.PrimaryID = req.primary_id;
  body.MobileNumber = req.mobile_number;
  body.EmailAddress = req.email_address;
  body.IDNumber = req.id_number;
  body.CompanyCode = companyCode;
  let header = {};

  getAccessToken()
    .then(function (response) {
      header = createHeader(response, body);
    });
}

module.exports = {
  getAccessToken,
};
<commit_msg>try access token<commit_after>const got = require('got');
const request = require('request');
const Buffer = require('Buffer');

const crypto = require('crypto');
const companyCode = 88801;
const Oauth = require('client-oauth2');
const cID = '268b2069-b099-4fa2-8148-1f1c0327fe63';
const cS = 'b383c35d-3c11-4ce6-b631-8767f4c2084b';
const aK = '0dfb11cd-b140-40cd-b65a-220e9998a129';
const aS = '199505e4-9d5f-4ba9-bb96-a3ea8b2f69c1';

const auth = new Oauth({
  clientId: cID,
  clientSecret: cS,
  accessTokenUri: 'https://api.finhacks.id/api/oauth/token',
});

function getAccessToken (res) {

  let header = {
    Authorization: 'Basic ' + new Buffer(cID + ':' + cS).toString('base64'),
    'Content-Type': 'application/x-www-form-urlencoded',
  };

  let body = { grant_type: 'client_credentials' };
  let options = {
    method: 'POST',
    url: 'https://api.finhacks.id/api/oauth/token',
    headers: header,
  };
  request.post(options).form(body)
    .on('body', function(bd) {
      res.send(bd);
    })
    .on('error', function(error) {
      console.log(error);
    });
}
// function gat () {
//   return auth.credentials.getToken()
//     .then(function(response){
//       return response;
//     })
// }
// function getAccessToken () {
//   return auth.credentials.getToken();
// }

function createHeader (data, body){
  let method = 'post'.toUpperCase();
  let url = encodeURI('/ewallet/customers');
  let accessToken = data.accessToken;
  let bd = crypto.createHash('SHA-256', body);
  bd = bd.toLowerCase();

  let dt = new Date();
  dt = dt.toISOString();

  let str = method + ':' + url + ':' + accessToken + ':' + bd + ':' + dt;

  let hash = crypto.createHmac('SHA_256', aS, str);

  var header = {
    'Authorization' : 'Bearer '+accessToken,
    'X-BCA-Key' : aK,
    'X-BCA-Signature' : hash,
    'X-BCA-Timestamp' : dT
  }

    return header;
}

function registerUser (req) {
  let body = {};

  body.CustomerName = req.customer_name;
  body.DateOfBirth = req.birth_date;
  body.PrimaryID = req.primary_id;
  body.MobileNumber = req.mobile_number;
  body.EmailAddress = req.email_address;
  body.IDNumber = req.id_number;
  body.CompanyCode = companyCode;
  let header = {};

  getAccessToken()
    .then(function (response) {
      header = createHeader(response, body);
    });
}

module.exports = {
  getAccessToken,
};
<|endoftext|>


<commit_before><commit_msg>Revert "FIO-3890: instance row issue fix"<commit_after><|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import { createPortal } from 'react-dom';
import PropTypes from 'prop-types';
import cx from 'classnames';
import Box from '../box';
import Overlay from '../overlay/Overlay';
import Transition from 'react-transition-group/Transition';
import theme from './theme.css';
import uiUtilities from '@teamleader/ui-utilities';

class DialogBase extends PureComponent {
  dialogRoot = document.createElement('div');

  componentDidMount() {
    document.body.appendChild(this.dialogRoot);
  }

  componentWillUnmount() {
    document.body.removeChild(this.dialogRoot);
  }

  render() {
    const {
      active,
      backdrop,
      children,
      className,
      onEscKeyDown,
      onOverlayClick,
      onOverlayMouseDown,
      onOverlayMouseMove,
      onOverlayMouseUp,
      scrollable,
      size,
    } = this.props;

    if (!active) {
      return null;
    }

    const dialogClassNames = cx(uiUtilities['box-shadow-300'], theme['dialog-base'], theme[`is-${size}`], className);

    const dialog = (
      <Transition timeout={0} in={active} appear>
        {state => {
          return (

              <Overlay
                active={active}
                backdrop={backdrop}
                className={theme['overlay']}
                onClick={onOverlayClick}
                onEscKeyDown={onEscKeyDown}
                onMouseDown={onOverlayMouseDown}
                onMouseMove={onOverlayMouseMove}
                onMouseUp={onOverlayMouseUp}
              />

                  {scrollable ? (
                    <Box display="flex" flexDirection="column" overflowY="auto">
                      {children}
                    </Box>
                  ) : (
                    children
                  )}

          );
        }}
      </Transition>
    );

    return createPortal(dialog, this.dialogRoot);
  }
}

DialogBase.propTypes = {
  /** If true, the dialog will show on screen. */
  active: PropTypes.bool,
  /** Specify which backdrop the dialog should show. */
  backdrop: PropTypes.string,
  /** The content to display inside the dialog. */
  children: PropTypes.node,
  /** A class name for the wrapper to give custom styles. */
  className: PropTypes.string,
  /** Callback function that is fired when the escape key is pressed. */
  onEscKeyDown: PropTypes.func,
  /** Callback function that is fired when the mouse clicks on the overlay. */
  onOverlayClick: PropTypes.func,
  /** Callback function that is fired when the mouse button is pressed on the overlay. */
  onOverlayMouseDown: PropTypes.func,
  /** Callback function that is fired when the mouse moves over the overlay. */
  onOverlayMouseMove: PropTypes.func,
  /** Callback function that is fired when the mouse button is released from the overlay. */
  onOverlayMouseUp: PropTypes.func,
  /** If true, the content of the dialog will be scrollable when it exceeds the available height. */
  scrollable: PropTypes.bool,
  /** The size of the dialog. */
  size: PropTypes.oneOf(['small', 'medium', 'large', 'fullscreen']),
};

DialogBase.defaultProps = {
  active: false,
  backdrop: 'dark',
  scrollable: true,
  size: 'medium',
};

export default DialogBase;
<commit_msg>:sparkles: expose the Header, Body and Footer on the DialogBase component<commit_after>import React, { PureComponent } from 'react';
import { createPortal } from 'react-dom';
import PropTypes from 'prop-types';
import cx from 'classnames';
import Box from '../box';
import Overlay from '../overlay/Overlay';
import Transition from 'react-transition-group/Transition';
import Header from './Header';
import Body from './Body';
import Footer from './Footer';
import theme from './theme.css';
import uiUtilities from '@teamleader/ui-utilities';

class DialogBase extends PureComponent {
  dialogRoot = document.createElement('div');

  componentDidMount() {
    document.body.appendChild(this.dialogRoot);
  }

  componentWillUnmount() {
    document.body.removeChild(this.dialogRoot);
  }

  render() {
    const {
      active,
      backdrop,
      children,
      className,
      onEscKeyDown,
      onOverlayClick,
      onOverlayMouseDown,
      onOverlayMouseMove,
      onOverlayMouseUp,
      scrollable,
      size,
    } = this.props;

    if (!active) {
      return null;
    }

    const dialogClassNames = cx(uiUtilities['box-shadow-300'], theme['dialog-base'], theme[`is-${size}`], className);

    const dialog = (
      <Transition timeout={0} in={active} appear>
        {state => {
          return (

              <Overlay
                active={active}
                backdrop={backdrop}
                className={theme['overlay']}
                onClick={onOverlayClick}
                onEscKeyDown={onEscKeyDown}
                onMouseDown={onOverlayMouseDown}
                onMouseMove={onOverlayMouseMove}
                onMouseUp={onOverlayMouseUp}
              />

                  {scrollable ? (
                    <Box display="flex" flexDirection="column" overflowY="auto">
                      {children}
                    </Box>
                  ) : (
                    children
                  )}

          );
        }}
      </Transition>
    );

    return createPortal(dialog, this.dialogRoot);
  }
}

DialogBase.propTypes = {
  /** If true, the dialog will show on screen. */
  active: PropTypes.bool,
  /** Specify which backdrop the dialog should show. */
  backdrop: PropTypes.string,
  /** The content to display inside the dialog. */
  children: PropTypes.node,
  /** A class name for the wrapper to give custom styles. */
  className: PropTypes.string,
  /** Callback function that is fired when the escape key is pressed. */
  onEscKeyDown: PropTypes.func,
  /** Callback function that is fired when the mouse clicks on the overlay. */
  onOverlayClick: PropTypes.func,
  /** Callback function that is fired when the mouse button is pressed on the overlay. */
  onOverlayMouseDown: PropTypes.func,
  /** Callback function that is fired when the mouse moves over the overlay. */
  onOverlayMouseMove: PropTypes.func,
  /** Callback function that is fired when the mouse button is released from the overlay. */
  onOverlayMouseUp: PropTypes.func,
  /** If true, the content of the dialog will be scrollable when it exceeds the available height. */
  scrollable: PropTypes.bool,
  /** The size of the dialog. */
  size: PropTypes.oneOf(['small', 'medium', 'large', 'fullscreen']),
};

DialogBase.defaultProps = {
  active: false,
  backdrop: 'dark',
  scrollable: true,
  size: 'medium',
};

DialogBase.Header = Header;
DialogBase.Body = Body;
DialogBase.Footer = Footer;

export default DialogBase;
<|endoftext|>


<commit_before>module.exports = function (path) {
  return require((process.env.APP_DIR_FOR_CODE_COVERAGE || '../src/') + path);
};
<commit_msg>style(jshint): declare globals<commit_after>/* global require, module, process, console, describe, it, before, __dirname */
/* jshint -W097 */
/* jshint expr: true */
'use strict';
module.exports = function (path) {
  return require((process.env.APP_DIR_FOR_CODE_COVERAGE || '../src/') + path);
};
<|endoftext|>