<commit_before>var MicroJS = [
  {
    name: "Modernizr",
    size: "3.7k",
    tags: ["feature"],
    description: "Detects native CSS3 and HTML5 features available in the current browser.",
    url: "http://www.modernizr.com/"
  },
  {
    name: "Zepto",
    size: "4.2k",
    tags: ["base", "dom", "webkit", "jquery", "ajax", "events", "mobile"],
    description: "jQuery API-compatible micro-framework for mobile WebKit browsers, with DOM manipulation, Ajax and events.",
    url: "http://zeptojs.com/"
  },
  {
    name: "xui",
    size: "4.1k",
    tags: ["base", "dom", "mobile", "ajax", "events", "webkit", "animation"],
    description: "A super micro tiny dom library for authoring HTML5 mobile web applications, works cross-device and cross-platform.",
    url: "http://xuijs.com/"
  },
  {
    name: "Underscore",
    size: "3.0k",
    tags: ["functional", "language", "data"],
    description: "A utility-belt that provides functional programming support that you would expect in Ruby.",
    url: "http://documentcloud.github.com/underscore/"
  },
  {
    name: "Backbone",
    size: "3.9k",
    tags: ["mvc", "data", "functional"],
    description: "Lightweight MVC—models with custom events, collections with rich enumerables, views and RESTful JSON.",
    url: "http://documentcloud.github.com/backbone/"
  },
  {
    name: "Mustache",
    size: "1.5k",
    tags: ["templating"],
    description: "Minimal, logic-less templating with {{mustaches}}. Great for server- or client-side templating.",
    url: "http://mustache.github.com/"
  },
  {
    name: "LABjs",
    size: "2.1k",
    tags: ["loader"],
    description: "Flexible and light-weight JavaScript dependency loader.",
    url: "http://labjs.com/"
  },
  {
    name: "Qwery",
    size: "1.0k",
    tags: ["css"],
    description: "Qwery is a 1k blazing fast query selector engine allowing you to select elements with CSS1 & CSS2 selectors.",
    url: "https://github.com/ded/qwery"
  },
  {
    name: "Sly",
    size: "3.0k",
    tags: ["css"],
    description: "Sly is a query selector engine allowing you to select elements with CSS1, CSS2 & CSS3 selectors.",
    url: "https://github.com/digitarald/sly"
  },
  {
    name: "Émile",
    size: "0.9k",
    tags: ["animation"],
    description: "Émile is a no-frills stand-alone CSS animation JavaScript framework, named after Émile Cohl, early animator.",
    url: "https://github.com/madrobby/emile"
  },
  {
    name: "Lawnchair",
    size: "2.0k",
    tags: ["data","storage","webkit"],
    description: "Sorta like a couch except smaller and outside, also, a client side JSON document store. Perfect for WebKit mobile apps.",
    url: "http://westcoastlogic.com/lawnchair/"
  },
  {
    name: "ExplorerCanvas",
    size: "4.3k",
    tags: ["polyfill"],
    description: "HTML5 canvas tag support for Internet Explorer.",
    url: "http://code.google.com/p/explorercanvas/"
  },
  {
    name: "Placeholder.js",
    size: "0.4k",
    tags: ["polyfill"],
    description: "Adds support for the placeholder attribute in older browsers that don't support this HTML5 feature.",
    url: "https://github.com/NV/placeholder.js"
  },
  {
    name: "Micro-Templating",
    size: "0.2k",
    tags: ["templating"],
    description: "Templating function that is fast, caches quickly, and is easy to use.",
    url: "http://ejohn.org/blog/javascript-micro-templating/"
  },
  {
    name: "Tweet-Templ",
    size: "0.1k",
    tags: ["templating"],
    description: "function t(s,d){for(var p in d)s=s.replace(new RegExp('{'+p+'}','g'),d[p]);return s;}",
    url: "http://mir.aculo.us/2011/03/09/little-helpers-a-tweet-sized-javascript-templating-engine/"
  },
  {
    name: "Firmin",
    size: "2.5k",
    tags: ["animation","webkit"],
    description: "Animation library that uses CSS transforms and transitions to create smooth, hardware-accelerated animations.",
    url: "http://extralogical.net/projects/firmin/"
  },
  {
    name: "Bonzo",
    size: "2.9k",
    tags: ["dom"],
    description: "A simple, to the point, hassle-free, small, library agnostic, extensible DOM utility.",
    url: "https://github.com/ded/bonzo"
  },
  {
    name: "Bean",
    size: "2.4k",
    tags: ["events", "dom"],
    description: "Bean is a small, slick, cross-platform, framework-agnostic event utility designed for desktop, mobile, and touch-based browsers. ",
    url: "https://github.com/fat/bean"
  },
  {
    name: "$script.js",
    size: "0.8k",
    tags: ["loader"],
    description: "An asynchronous JavaScript loader and dependency manager with an astonishingly impressive footprint.",
    url: "https://github.com/ded/script.js"
  },
  {
    name: "Klass.js",
    size: "0.6k",
    tags: ["language", "class"],
    description: "An expressive, cross platform JavaScript Class provider with a slick, classical interface to prototypal inheritance.",
    url: "https://github.com/ded/klass"
  },
  {
    name: "snack.js",
    size: "3.3k",
    tags: ["base", "dom", "events", "ajax", "language"],
    description: "A tiny, cross-browser, library with DOM manipulation, events, pub/sub, Ajax and language utilities.",
    url: "http://snackjs.com/"
  },
  {
    name: "Ender",
    size: "?k",
    tags: ["compose"],
    description: "A open, powerful, micro-to-macro API for composing your own custom JavaScript library.",
    url: "http://ender.no.de"
  },
  {
    name: "Mold",
    size: "1.9k",
    tags: ["templating"],
    description: "Mold is a very light-weight (200 lines) client-side JavaScript-based templating library.",
    url: "http://marijnhaverbeke.nl/mold/"
  },
  {
    name: "Tempo",
    size: "3.2k",
    tags: ["templating"],
    description: "Tempo is a JSON rendering engine that enables you to craft data templates in pure HTML.",
    url: "http://twigkit.github.com/tempo/"
  },
  {
    name: "Reqwest",
    size: "0.9k",
    tags: ["ajax"],
    description: "A robust lightweight wrapper for asynchronous http requests.",
    url: "https://github.com/ded/Reqwest"
  },
  {
    name: "JSON2",
    size: "1.3k",
    tags: ["polyfill", "json"],
    description: "Creates a JSON property in the global object, if there  isn't already one, with stringify and parse methods.",
    url: "https://github.com/douglascrockford/JSON-js"
  },
  {
    name: "yepnope",
    size: "1.6k",
    tags: ["polyfill", "loader"],
    description: "yepnope is an asynchronous conditional resource loader that allows you to load only the scripts that your users need.",
    url: "http://yepnopejs.com/"
  },
  {
    name: "ICanHaz",
    size: "1.8k",
    tags: ["templating"],
    description: "Simple & powerful client-side templating for jQuery or Zepto.js.",
    url: "http://icanhazjs.com/"
  },
  {
    name: "Q",
    size: "1.0k",
    tags: ["functional", "events"],
    description: "when/defer-style promises for JavaScript (CommonJS/Promises/A,B,D)",
    url: "https://github.com/kriskowal/q"
  },
  {
    name: "domReady",
    size: "0.2k",
    tags: ["dom", "events"],
    description: "lets you know when the DOM is ready",
    url: "https://github.com/ded/domready"
  },
  {
    name: "Spine",
    size: "2.0k",
    tags: ["mvc"],
    description: "MVC with real protypical inheritance, controllers, model layer and ORM, Ajax and local storage.",
    url: "http://maccman.github.com/spine/"
  },
  {
    name: "Kizzy",
    size: "0.6k",
    tags: ["data", "storage"],
    description: "Cross Browser caching util leveraging LocalStorage and XML Store with a Memcached interface",
    url: "https://github.com/ded/Kizzy"
  },
  {
    name: "Bowser",
    size: "0.2k",
    tags: ["feature"],
    description: "A browser detector. Because when there is no features to detect, sometimes you have to browser sniff",
    url: "https://github.com/ded/bowser"
  },
  {
    name: "ES5-Shim",
    size: "2.0k",
    tags: ["language", "polyfill"],
    description: "Compatibility shims so that legacy JavaScript engines behave as closely as possible to ES5.",
    url: "https://github.com/kriskowal/es5-shim/"
  },
  {
    name: "Loadrunner",
    size: "2.0k",
    tags: ["loader"],
    description: "Simple, flexible and sane JavaScript loader and build tool for browsers",
    url: "https://github.com/danwrong/loadrunner"
  },
  {
    name: "functools",
    size: "2.2k",
    tags: ["functional"],
    description: "A minimal library for functional operations",
    url: "https://github.com/azer/functools"
  },
  {
    name: "observer",
    size: "1.0k",
    tags: ["observer","events"],
    description: "CommonJS implementation of observer design pattern",
    url: "https://github.com/azer/observer"
  },
  {
    name: "defer.js",
    size: "4.8k",
    tags: ["loader"],
    description: "Script loader, using the 'Deferred' pattern for async operations.",
    url: "https://github.com/BorisMoore/jsdefer/"
  },
  {
    name: "Bootstrap",
    size: "1.3k",
    tags: ["loader"],
    description: "A small, simple bootloader. No frills. Black and white label.",
    url: "https://bitbucket.org/scott_koon/bootstrap"
  },
  {
    name: "LazyLoad",
    size: "0.9k",
    tags: ["loader"],
    description: "Tiny async JavaScript and CSS loader.",
    url: "https://github.com/rgrove/lazyload"
  },
  {
    name: "Valentine",
    size: "1.5k",
    tags: ["functional", "language", "data"],
    description: "JavaScripts functional sister providing iterators, type checking, and basic utils",
    url: "https://github.com/ded/valentine"
  },
  {
    name: "TurtleWax",
    size: "0.7k",
    tags: ["canvas", "2d"],
    description: "LOGO-inspired canvas drawing lib, adds method chaining and polar coordinates for added fun.",
    url: "https://github.com/davebalmer/turtlewax"
  },
  {
    name: "picoCSS",
    size: "0.35k",
    tags: ["CSS","CSS3"],
    description: "Basic CSS DOM manipulation and animation optimized for webkit browsers(Safari, Chrome) for building mobile web apps",
    url: "https://github.com/vladocar/picoCSS"
  },
  {
    name: "H5F.js",
    size: "1.8k",
    tags: ["polyfill"],
    description: "Adds support for HTML5 Forms chapters new field input types, attributes and constraint validation API in non-supporting browsers.",
    url: "https://github.com/ryanseddon/H5F"
  }
];
<commit_msg>Added fermata REST client library, size based on best http://compressorrater.thruhere.net/ result<commit_after>var MicroJS = [
  {
    name: "Modernizr",
    size: "3.7k",
    tags: ["feature"],
    description: "Detects native CSS3 and HTML5 features available in the current browser.",
    url: "http://www.modernizr.com/"
  },
  {
    name: "Zepto",
    size: "4.2k",
    tags: ["base", "dom", "webkit", "jquery", "ajax", "events", "mobile"],
    description: "jQuery API-compatible micro-framework for mobile WebKit browsers, with DOM manipulation, Ajax and events.",
    url: "http://zeptojs.com/"
  },
  {
    name: "xui",
    size: "4.1k",
    tags: ["base", "dom", "mobile", "ajax", "events", "webkit", "animation"],
    description: "A super micro tiny dom library for authoring HTML5 mobile web applications, works cross-device and cross-platform.",
    url: "http://xuijs.com/"
  },
  {
    name: "Underscore",
    size: "3.0k",
    tags: ["functional", "language", "data"],
    description: "A utility-belt that provides functional programming support that you would expect in Ruby.",
    url: "http://documentcloud.github.com/underscore/"
  },
  {
    name: "Backbone",
    size: "3.9k",
    tags: ["mvc", "data", "functional"],
    description: "Lightweight MVC—models with custom events, collections with rich enumerables, views and RESTful JSON.",
    url: "http://documentcloud.github.com/backbone/"
  },
  {
    name: "Mustache",
    size: "1.5k",
    tags: ["templating"],
    description: "Minimal, logic-less templating with {{mustaches}}. Great for server- or client-side templating.",
    url: "http://mustache.github.com/"
  },
  {
    name: "LABjs",
    size: "2.1k",
    tags: ["loader"],
    description: "Flexible and light-weight JavaScript dependency loader.",
    url: "http://labjs.com/"
  },
  {
    name: "Qwery",
    size: "1.0k",
    tags: ["css"],
    description: "Qwery is a 1k blazing fast query selector engine allowing you to select elements with CSS1 & CSS2 selectors.",
    url: "https://github.com/ded/qwery"
  },
  {
    name: "Sly",
    size: "3.0k",
    tags: ["css"],
    description: "Sly is a query selector engine allowing you to select elements with CSS1, CSS2 & CSS3 selectors.",
    url: "https://github.com/digitarald/sly"
  },
  {
    name: "Émile",
    size: "0.9k",
    tags: ["animation"],
    description: "Émile is a no-frills stand-alone CSS animation JavaScript framework, named after Émile Cohl, early animator.",
    url: "https://github.com/madrobby/emile"
  },
  {
    name: "Lawnchair",
    size: "2.0k",
    tags: ["data","storage","webkit"],
    description: "Sorta like a couch except smaller and outside, also, a client side JSON document store. Perfect for WebKit mobile apps.",
    url: "http://westcoastlogic.com/lawnchair/"
  },
  {
    name: "ExplorerCanvas",
    size: "4.3k",
    tags: ["polyfill"],
    description: "HTML5 canvas tag support for Internet Explorer.",
    url: "http://code.google.com/p/explorercanvas/"
  },
  {
    name: "Placeholder.js",
    size: "0.4k",
    tags: ["polyfill"],
    description: "Adds support for the placeholder attribute in older browsers that don't support this HTML5 feature.",
    url: "https://github.com/NV/placeholder.js"
  },
  {
    name: "Micro-Templating",
    size: "0.2k",
    tags: ["templating"],
    description: "Templating function that is fast, caches quickly, and is easy to use.",
    url: "http://ejohn.org/blog/javascript-micro-templating/"
  },
  {
    name: "Tweet-Templ",
    size: "0.1k",
    tags: ["templating"],
    description: "function t(s,d){for(var p in d)s=s.replace(new RegExp('{'+p+'}','g'),d[p]);return s;}",
    url: "http://mir.aculo.us/2011/03/09/little-helpers-a-tweet-sized-javascript-templating-engine/"
  },
  {
    name: "Firmin",
    size: "2.5k",
    tags: ["animation","webkit"],
    description: "Animation library that uses CSS transforms and transitions to create smooth, hardware-accelerated animations.",
    url: "http://extralogical.net/projects/firmin/"
  },
  {
    name: "Bonzo",
    size: "2.9k",
    tags: ["dom"],
    description: "A simple, to the point, hassle-free, small, library agnostic, extensible DOM utility.",
    url: "https://github.com/ded/bonzo"
  },
  {
    name: "Bean",
    size: "2.4k",
    tags: ["events", "dom"],
    description: "Bean is a small, slick, cross-platform, framework-agnostic event utility designed for desktop, mobile, and touch-based browsers. ",
    url: "https://github.com/fat/bean"
  },
  {
    name: "$script.js",
    size: "0.8k",
    tags: ["loader"],
    description: "An asynchronous JavaScript loader and dependency manager with an astonishingly impressive footprint.",
    url: "https://github.com/ded/script.js"
  },
  {
    name: "Klass.js",
    size: "0.6k",
    tags: ["language", "class"],
    description: "An expressive, cross platform JavaScript Class provider with a slick, classical interface to prototypal inheritance.",
    url: "https://github.com/ded/klass"
  },
  {
    name: "snack.js",
    size: "3.3k",
    tags: ["base", "dom", "events", "ajax", "language"],
    description: "A tiny, cross-browser, library with DOM manipulation, events, pub/sub, Ajax and language utilities.",
    url: "http://snackjs.com/"
  },
  {
    name: "Ender",
    size: "?k",
    tags: ["compose"],
    description: "A open, powerful, micro-to-macro API for composing your own custom JavaScript library.",
    url: "http://ender.no.de"
  },
  {
    name: "Mold",
    size: "1.9k",
    tags: ["templating"],
    description: "Mold is a very light-weight (200 lines) client-side JavaScript-based templating library.",
    url: "http://marijnhaverbeke.nl/mold/"
  },
  {
    name: "Tempo",
    size: "3.2k",
    tags: ["templating"],
    description: "Tempo is a JSON rendering engine that enables you to craft data templates in pure HTML.",
    url: "http://twigkit.github.com/tempo/"
  },
  {
    name: "Reqwest",
    size: "0.9k",
    tags: ["ajax"],
    description: "A robust lightweight wrapper for asynchronous http requests.",
    url: "https://github.com/ded/Reqwest"
  },
  {
    name: "JSON2",
    size: "1.3k",
    tags: ["polyfill", "json"],
    description: "Creates a JSON property in the global object, if there  isn't already one, with stringify and parse methods.",
    url: "https://github.com/douglascrockford/JSON-js"
  },
  {
    name: "yepnope",
    size: "1.6k",
    tags: ["polyfill", "loader"],
    description: "yepnope is an asynchronous conditional resource loader that allows you to load only the scripts that your users need.",
    url: "http://yepnopejs.com/"
  },
  {
    name: "ICanHaz",
    size: "1.8k",
    tags: ["templating"],
    description: "Simple & powerful client-side templating for jQuery or Zepto.js.",
    url: "http://icanhazjs.com/"
  },
  {
    name: "Q",
    size: "1.0k",
    tags: ["functional", "events"],
    description: "when/defer-style promises for JavaScript (CommonJS/Promises/A,B,D)",
    url: "https://github.com/kriskowal/q"
  },
  {
    name: "domReady",
    size: "0.2k",
    tags: ["dom", "events"],
    description: "lets you know when the DOM is ready",
    url: "https://github.com/ded/domready"
  },
  {
    name: "Spine",
    size: "2.0k",
    tags: ["mvc"],
    description: "MVC with real protypical inheritance, controllers, model layer and ORM, Ajax and local storage.",
    url: "http://maccman.github.com/spine/"
  },
  {
    name: "Kizzy",
    size: "0.6k",
    tags: ["data", "storage"],
    description: "Cross Browser caching util leveraging LocalStorage and XML Store with a Memcached interface",
    url: "https://github.com/ded/Kizzy"
  },
  {
    name: "Bowser",
    size: "0.2k",
    tags: ["feature"],
    description: "A browser detector. Because when there is no features to detect, sometimes you have to browser sniff",
    url: "https://github.com/ded/bowser"
  },
  {
    name: "ES5-Shim",
    size: "2.0k",
    tags: ["language", "polyfill"],
    description: "Compatibility shims so that legacy JavaScript engines behave as closely as possible to ES5.",
    url: "https://github.com/kriskowal/es5-shim/"
  },
  {
    name: "Loadrunner",
    size: "2.0k",
    tags: ["loader"],
    description: "Simple, flexible and sane JavaScript loader and build tool for browsers",
    url: "https://github.com/danwrong/loadrunner"
  },
  {
    name: "functools",
    size: "2.2k",
    tags: ["functional"],
    description: "A minimal library for functional operations",
    url: "https://github.com/azer/functools"
  },
  {
    name: "observer",
    size: "1.0k",
    tags: ["observer","events"],
    description: "CommonJS implementation of observer design pattern",
    url: "https://github.com/azer/observer"
  },
  {
    name: "defer.js",
    size: "4.8k",
    tags: ["loader"],
    description: "Script loader, using the 'Deferred' pattern for async operations.",
    url: "https://github.com/BorisMoore/jsdefer/"
  },
  {
    name: "Bootstrap",
    size: "1.3k",
    tags: ["loader"],
    description: "A small, simple bootloader. No frills. Black and white label.",
    url: "https://bitbucket.org/scott_koon/bootstrap"
  },
  {
    name: "LazyLoad",
    size: "0.9k",
    tags: ["loader"],
    description: "Tiny async JavaScript and CSS loader.",
    url: "https://github.com/rgrove/lazyload"
  },
  {
    name: "Valentine",
    size: "1.5k",
    tags: ["functional", "language", "data"],
    description: "JavaScripts functional sister providing iterators, type checking, and basic utils",
    url: "https://github.com/ded/valentine"
  },
  {
    name: "TurtleWax",
    size: "0.7k",
    tags: ["canvas", "2d"],
    description: "LOGO-inspired canvas drawing lib, adds method chaining and polar coordinates for added fun.",
    url: "https://github.com/davebalmer/turtlewax"
  },
  {
    name: "picoCSS",
    size: "0.35k",
    tags: ["CSS","CSS3"],
    description: "Basic CSS DOM manipulation and animation optimized for webkit browsers(Safari, Chrome) for building mobile web apps",
    url: "https://github.com/vladocar/picoCSS"
  },
  {
    name: "H5F.js",
    size: "1.8k",
    tags: ["polyfill"],
    description: "Adds support for HTML5 Forms chapters new field input types, attributes and constraint validation API in non-supporting browsers.",
    url: "https://github.com/ryanseddon/H5F"
  },
  {
    name: "Fermata",
    size: "1.9k",
    tags: ["ajax"],
    description: "Succinct native REST client, for client-side web apps and node.js. Turns URLs into magic JavaScript objects.",
    url: "https://github.com/andyet/fermata"
  }
];
<|endoftext|>


<commit_before>const URL_WEBSITE = 'https://dogancelik.com/atomic-tweetdeck/';
const URL_ISSUES = 'https://github.com/dogancelik/atomic-tweetdeck/issues';

const appName = global.electron.app.getName();
const appVersion = global.electron.app.getVersion();

const shell = global.electron.shell;
const dialog = global.electron.dialog;

const { getAboutMessage } = require('./utils');

exports.getMenuTemplate = function () {
  return [{
    label: '&Window',
    role: 'window',
    submenu: [
      {
        label: '&Reload',
        accelerator: 'CmdOrCtrl+R',
        click(item, wnd) { wnd.reload(); }
      },
      {
        label: '&Hide',
        accelerator: 'CmdOrCtrl+W',
        click(item, wnd) {
          if (wnd.getParentWindow() == null) {
            wnd.minimize();
          } else {
            wnd.close();
          }
        }
      },
      {
        label: '&Quit',
        accelerator: 'CmdOrCtrl+Q',
        click() {
          // app.exit() crashes Electron on child window
          // can't do app.quit() if Close to Tray
          // this will work because windowAllClosed
          Array.prototype.slice.call(global.electron.BrowserWindow.getAllWindows())
            .forEach((wnd) => wnd.destroy());
        }
      }
    ]
  }, {
    label: 'Hi&story',
    submenu: [
      {
        label: 'Go back',
        accelerator: 'Alt+Left',
        click(item, wnd) { wnd.webContents.canGoBack() && wnd.webContents.goBack(); }
      },
      {
        label: 'Go forward',
        accelerator: 'Alt+Right', // Insert smug Pepe here
        click(item, wnd) { wnd.webContents.canGoForward() && wnd.webContents.goForward(); }
      }
    ]
  }, {
    label: 'Settings',
    submenu: [
      {
        label: 'Open new windows in default browser',
        type: 'checkbox',
        click() { config.store.set(config.names.openBrowser, !config.store.get(config.names.openBrowser)); }
      },
      {
        label: 'Minimize to Tray',
        type: 'checkbox',
        click() { config.store.set(config.names.minToTray, !config.store.get(config.names.minToTray)); }
      }
    ]
  }, {
    label: '&Help',
    submenu: [
      {
        label: '&Visit website',
        click() { shell.openExternal(URL_WEBSITE); }
      },
      {
        label: '&Report an issue',
        click() { shell.openExternal(URL_ISSUES); }
      },
      {
        label: '&About ' + appName,
        click() {
          dialog.showMessageBox(global.mainWindow, {
            buttons: ['OK'],
            title: 'About ' + appName,
            message: getAboutMessage(appName, appVersion)
          })
        }
      }
    ]
  }];
};

exports.createMenu = function (template) {
  const Menu = global.electron.Menu;
  var menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
  return menu;
};

exports.setMenuChecks = function (appMenu) {
  appMenu.items[2].submenu.items[0].checked = config.store.get(config.names.openBrowser);
  appMenu.items[2].submenu.items[1].checked = config.store.get(config.names.minToTray);
};
<commit_msg>Allow user to open DevTools<commit_after>const URL_WEBSITE = 'https://dogancelik.com/atomic-tweetdeck/';
const URL_ISSUES = 'https://github.com/dogancelik/atomic-tweetdeck/issues';

const appName = global.electron.app.getName();
const appVersion = global.electron.app.getVersion();

const shell = global.electron.shell;
const dialog = global.electron.dialog;

const { getAboutMessage } = require('./utils');

exports.getMenuTemplate = function () {
  return [{
    label: '&Window',
    role: 'window',
    submenu: [
      {
        visible: false,
        label: 'Toggle &DevTools',
        accelerator: 'F12',
        click(item, wnd) { wnd.webContents.toggleDevTools(); }
      },
      {
        label: '&Reload',
        accelerator: 'CmdOrCtrl+R',
        click(item, wnd) { wnd.reload(); }
      },
      {
        label: '&Hide',
        accelerator: 'CmdOrCtrl+W',
        click(item, wnd) {
          if (wnd.getParentWindow() == null) {
            wnd.minimize();
          } else {
            wnd.close();
          }
        }
      },
      {
        label: '&Quit',
        accelerator: 'CmdOrCtrl+Q',
        click() {
          // app.exit() crashes Electron on child window
          // can't do app.quit() if Close to Tray
          // this will work because windowAllClosed
          Array.prototype.slice.call(global.electron.BrowserWindow.getAllWindows())
            .forEach((wnd) => wnd.destroy());
        }
      }
    ]
  }, {
    label: 'Hi&story',
    submenu: [
      {
        label: 'Go back',
        accelerator: 'Alt+Left',
        click(item, wnd) { wnd.webContents.canGoBack() && wnd.webContents.goBack(); }
      },
      {
        label: 'Go forward',
        accelerator: 'Alt+Right', // Insert smug Pepe here
        click(item, wnd) { wnd.webContents.canGoForward() && wnd.webContents.goForward(); }
      }
    ]
  }, {
    label: 'Settings',
    submenu: [
      {
        label: 'Open new windows in default browser',
        type: 'checkbox',
        click() { config.store.set(config.names.openBrowser, !config.store.get(config.names.openBrowser)); }
      },
      {
        label: 'Minimize to Tray',
        type: 'checkbox',
        click() { config.store.set(config.names.minToTray, !config.store.get(config.names.minToTray)); }
      }
    ]
  }, {
    label: '&Help',
    submenu: [
      {
        label: '&Visit website',
        click() { shell.openExternal(URL_WEBSITE); }
      },
      {
        label: '&Report an issue',
        click() { shell.openExternal(URL_ISSUES); }
      },
      {
        label: '&About ' + appName,
        click() {
          dialog.showMessageBox(global.mainWindow, {
            buttons: ['OK'],
            title: 'About ' + appName,
            message: getAboutMessage(appName, appVersion)
          })
        }
      }
    ]
  }];
};

exports.createMenu = function (template) {
  const Menu = global.electron.Menu;
  var menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
  return menu;
};

exports.setMenuChecks = function (appMenu) {
  appMenu.items[2].submenu.items[0].checked = config.store.get(config.names.openBrowser);
  appMenu.items[2].submenu.items[1].checked = config.store.get(config.names.minToTray);
};
<|endoftext|>


<commit_before>/* Callbacks */
Template.palette.created = function () {
	Session.setDefault("swatchStyle", "circle");
	Session.setDefault("swatchLabel", "default");
	Session.set("resultView", "palette");
};

Template.palette.rendered = function () {
	renderPalette();
};

/* Helpers */
Template.palette.helpers({
	swatches: function() {
		console.log(this._id);
		console.log(Palettes.findOne({_id: this._id}).swatches);

		// Define reactive variable
		var swatches = Palettes.findOne({_id: this._id}).swatches;
		renderPalette();
		return swatches;
	},
	swatchCount: function() {
		// return Palettes.find(this._id).swatches.length;
		return this.swatches.length;
	},

	/* General Utility helpers */
	pluralize: function(count, singular, plural) {
		if (count == 1) {
			return singular;
		} else {
			return	plural;
		}
	},

	codeText: function() {
		return this.text;
	},

	/* Toolbar Toggles */
	paletteViewActive: function() {
		return (Session.get("resultView") == "code" ? "" : "active");
	},
	codeViewActive: function() {
		return (Session.get("resultView") == "code" ? "active" : "");
	},
	squareSwatchActive: function() {
		return (Session.get("swatchStyle") == "square" ? "active" : "")
	},
	circleSwatchActive: function() {
		return (Session.get("swatchStyle") == "square" ? "" : "active")
	},
	varLabelActive: function() {
		return (Session.get("swatchLabel") == "colorVal" ? "" : "active")
	},
	colorValLabelActive: function() {
		return (Session.get("swatchLabel") == "colorVal" ? "active" : "")
	},

	paletteView: function() {
		return (Session.get("resultView") == "code" ? false : true);
	},

	/* Swatch template helpers */
	// The name or the value of the swatch.
	swatchLabel: function() {
		if (Session.get("swatchLabel") == "colorVal") {
			return this.colorVal;
		// } else if (Session.get("swatchLabel") == "varName") {
		// 	return this.varName || "undefined";
		} else {
			return this.varName || this.colorVal;
		}
	},
	// Circle or Square style swatch color
	swatchStyle: function() {
		if (Session.get("swatchStyle") == "square") {
			return	"swatch-item__swatch--square";
		} else {
			return	"swatch-item__swatch--circle";
		}
	}
});

/* Events */
Template.palette.events({
	// Click animation functionality
	'click .gridly .swatch-item__swatch': function(event) {
		event.preventDefault();
		event.stopPropagation();
		var $this, height, width;
		$this = $(event.currentTarget).parent();
		$this.toggleClass('small');
		$this.toggleClass('large');
		if ($this.hasClass('small')) {
			width = 140;
			height = 160;
		}
		if ($this.hasClass('large')) {
			width = 300;
			height = 340;
		}
		$this.data('width', width);
		$this.data('height', height);
		return $('.gridly').gridly('layout');
	},
	// Remove a swatch
	'click .gridly .delete': function(event) {
		var $this;
		event.preventDefault();
		event.stopPropagation();
		$this = $(event.currentTarget);
		$this.closest('.swatch').remove();
		return $('.gridly').gridly('layout');
	},
	// Select the label input when clicked
	'click .gridly .swatch-item__label': function(event) {
		$this = $(event.currentTarget);
		$this.select()
	},
	/* Toolbar Toggles */
	'click .toolbar .toggle-circle': function(event) {
		Session.set("swatchStyle", "circle");
	},
	'click .toolbar .toggle-square': function(event) {
		Session.set("swatchStyle", "square");
	},
	'click .toolbar .toggle-varName': function(event) {
		Session.set("swatchLabel", "varName");
	},
	'click .toolbar .toggle-colorVal': function(event) {
		Session.set("swatchLabel", "colorVal");
	},
	'click .toolbar .toggle-paletteView': function(event) {
		Session.set("resultView", "palette");
	},
	'click .toolbar .toggle-codeView': function(event) {
		Session.set("resultView", "code");
	},

	'click #re-extract': function(event) {
		event.preventDefault();
		var self = this;
		var code = $("textarea.extract-code").val();
		// Extract swatches and update current palette
		extractSwatches(code, this._id);
		console.log(this._id);
		Session.set("text", code);

		// Router.go('palette');
		Session.set("resultView", "palette");
		$('.gridly').gridly({
		      base: 60, // px
		      gutter: 20, // px
		      columns: 10,
		      draggable: {
		        zIndex: 800,
		        selector: '> *'
		      }
		  });
		$('.palette .swatch-item').each(function(index, el) {
			setTimeout(function() {
				$(el).addClass('fade-in');
			}, Math.floor(index/5)*100);
		});
		return false;
	}

	//, 'click .add': function(event) {
	// 	event.preventDefault();
	// 	event.stopPropagation();
	// 	$('.gridly').append(swatch);
	// 	return $('.gridly').gridly();
	// }
})

		// $('.gridly').gridly({
	 //      base: 60, // px
	 //      gutter: 20, // px
	 //      columns: 12
	 //  });

	    // Generated by CoffeeScript 1.6.3
// 	    (function() {
// 	    	$(function() {
// 	    		var swatch;
// 	    		swatch = "

×

";
// 	    		// $(document).on("click", ".add", function(event) {
// 	    		// 	event.preventDefault();
// 	    		// 	event.stopPropagation();
// 	    		// 	$('.gridly').append(swatch);
// 	    		// 	return $('.gridly').gridly();
// 	    		// });
// 	    		// return $('.gridly').gridly();
// 	    	});

// }).call(this);
<commit_msg>palette toggles switch to palette view<commit_after>/* Callbacks */
Template.palette.created = function () {
	Session.setDefault("swatchStyle", "circle");
	Session.setDefault("swatchLabel", "default");
	Session.set("resultView", "palette");
};

Template.palette.rendered = function () {
	renderPalette();
};

/* Helpers */
Template.palette.helpers({
	swatches: function() {
		console.log(this._id);
		console.log(Palettes.findOne({_id: this._id}).swatches);

		// Define reactive variable
		var swatches = Palettes.findOne({_id: this._id}).swatches;
		renderPalette();
		return swatches;
	},
	swatchCount: function() {
		// return Palettes.find(this._id).swatches.length;
		return this.swatches.length;
	},

	/* General Utility helpers */
	pluralize: function(count, singular, plural) {
		if (count == 1) {
			return singular;
		} else {
			return	plural;
		}
	},

	codeText: function() {
		return this.text;
	},

	/* Toolbar Toggles */
	paletteViewActive: function() {
		return (Session.get("resultView") == "code" ? "" : "active");
	},
	codeViewActive: function() {
		return (Session.get("resultView") == "code" ? "active" : "");
	},
	squareSwatchActive: function() {
		return (Session.get("swatchStyle") == "square" ? "active" : "")
	},
	circleSwatchActive: function() {
		return (Session.get("swatchStyle") == "square" ? "" : "active")
	},
	varLabelActive: function() {
		return (Session.get("swatchLabel") == "colorVal" ? "" : "active")
	},
	colorValLabelActive: function() {
		return (Session.get("swatchLabel") == "colorVal" ? "active" : "")
	},

	paletteView: function() {
		return (Session.get("resultView") == "code" ? false : true);
	},

	/* Swatch template helpers */
	// The name or the value of the swatch.
	swatchLabel: function() {
		if (Session.get("swatchLabel") == "colorVal") {
			return this.colorVal;
		// } else if (Session.get("swatchLabel") == "varName") {
		// 	return this.varName || "undefined";
		} else {
			return this.varName || this.colorVal;
		}
	},
	// Circle or Square style swatch color
	swatchStyle: function() {
		if (Session.get("swatchStyle") == "square") {
			return	"swatch-item__swatch--square";
		} else {
			return	"swatch-item__swatch--circle";
		}
	}
});

/* Events */
Template.palette.events({
	// Click animation functionality
	'click .gridly .swatch-item__swatch': function(event) {
		event.preventDefault();
		event.stopPropagation();
		var $this, height, width;
		$this = $(event.currentTarget).parent();
		$this.toggleClass('small');
		$this.toggleClass('large');
		if ($this.hasClass('small')) {
			width = 140;
			height = 160;
		}
		if ($this.hasClass('large')) {
			width = 300;
			height = 340;
		}
		$this.data('width', width);
		$this.data('height', height);
		return $('.gridly').gridly('layout');
	},
	// Remove a swatch
	'click .gridly .delete': function(event) {
		var $this;
		event.preventDefault();
		event.stopPropagation();
		$this = $(event.currentTarget);
		$this.closest('.swatch').remove();
		return $('.gridly').gridly('layout');
	},
	// Select the label input when clicked
	'click .gridly .swatch-item__label': function(event) {
		$this = $(event.currentTarget);
		$this.select()
	},
	/* Toolbar Toggles */
	'click .toolbar .toggle-circle': function(event) {
		Session.set("resultView", "palette");
		Session.set("swatchStyle", "circle");
	},
	'click .toolbar .toggle-square': function(event) {
		Session.set("resultView", "palette");
		Session.set("swatchStyle", "square");
	},
	'click .toolbar .toggle-varName': function(event) {
		Session.set("resultView", "palette");
		Session.set("swatchLabel", "varName");
	},
	'click .toolbar .toggle-colorVal': function(event) {
		Session.set("resultView", "palette");
		Session.set("swatchLabel", "colorVal");
	},
	'click .toolbar .toggle-paletteView': function(event) {
		Session.set("resultView", "palette");
	},
	'click .toolbar .toggle-codeView': function(event) {
		Session.set("resultView", "code");
	},

	'click #re-extract': function(event) {
		event.preventDefault();
		var self = this;
		var code = $("textarea.extract-code").val();
		// Extract swatches and update current palette
		extractSwatches(code, this._id);
		console.log(this._id);
		Session.set("text", code);

		// Router.go('palette');
		Session.set("resultView", "palette");
		$('.gridly').gridly({
		      base: 60, // px
		      gutter: 20, // px
		      columns: 10,
		      draggable: {
		        zIndex: 800,
		        selector: '> *'
		      }
		  });
		$('.palette .swatch-item').each(function(index, el) {
			setTimeout(function() {
				$(el).addClass('fade-in');
			}, Math.floor(index/5)*100);
		});
		return false;
	}

	//, 'click .add': function(event) {
	// 	event.preventDefault();
	// 	event.stopPropagation();
	// 	$('.gridly').append(swatch);
	// 	return $('.gridly').gridly();
	// }
})

		// $('.gridly').gridly({
	 //      base: 60, // px
	 //      gutter: 20, // px
	 //      columns: 12
	 //  });

	    // Generated by CoffeeScript 1.6.3
// 	    (function() {
// 	    	$(function() {
// 	    		var swatch;
// 	    		swatch = "

×

";
// 	    		// $(document).on("click", ".add", function(event) {
// 	    		// 	event.preventDefault();
// 	    		// 	event.stopPropagation();
// 	    		// 	$('.gridly').append(swatch);
// 	    		// 	return $('.gridly').gridly();
// 	    		// });
// 	    		// return $('.gridly').gridly();
// 	    	});

// }).call(this);
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import _ from 'lodash';
import * as TrackActions from '../actions/track';
import * as MeasureActions from '../actions/measure';
import * as PlayingNoteActions from '../actions/playingNote';

import Soundfont from 'soundfont-player';
import audioContext from '../util/audioContext';

import TabStaff from '../components/TabStaff';
import EditorArea from '../components/editor/EditorArea';
import TimeSignatureModal from '../components/editor/TimeSignatureModal';
import TuningModal from '../components/editor/TuningModal';
import BpmModal from '../components/editor/BpmModal';
import Playback from '../components/Playback';

const Actions = _.merge(TrackActions, MeasureActions, PlayingNoteActions);

class App extends Component {
  constructor(props) {
    super(props);

    if (typeof window !== 'undefined') {
      window.addEventListener('keydown', this.handleKeyPress);
      window.addEventListener('resize', this.handleResize);
    }

    this.state = {
      editingIndex: {
        measureIndex: 0,
        noteIndex: 0,
        stringIndex: 0
      }
    };
  }

  componentWillMount = () => {
    Soundfont.loadBuffers(audioContext, 'acoustic_guitar_steel').then((buffers) => {
      this.setState({ buffers });
    });
  }

  componentWillReceiveProps = (nextProps) => {
    if(nextProps.playingNote) {
      this.setState({
        editingIndex: {
          measureIndex: nextProps.playingNote.measure,
          noteIndex: nextProps.playingNote.noteIndex,
          stringIndex: this.state.editingIndex.stringIndex
        }
      }, () => {
        this.updateScrollPosition();
      });
    }
  }

  handleResize = () => {
    this.props.actions.resize();
  }

  getXOfCurrentNote = () => {
    const { measure, noteIndex } = this.props.playingNote;
    const xOfMeasures = this.props.track.measures.reduce((acc, curr, i) => {
      if(i >= measure) {
        return acc;
      }
      return acc + curr.width;
    }, 0);

    return xOfMeasures + 55 * noteIndex;
  }

  updateScrollPosition = () => {
    const x = this.getXOfCurrentNote();
    const scrollX = window.scrollX;

    if(x > window.innerWidth + scrollX - 200 && this.props.layout === 'linear') {
      window.scroll(x - 200, 0);
    }
  }

  handleStop = () => {
    cancelAnimationFrame(this.state.timer);

    this.props.actions.setPlayingNote(null);
  }

  handlePlay = () => {
    if(this.props.playingNote || !this.state.buffers) {
      return;
    }

    const { noteIndex, measureIndex } = this.state.editingIndex;

    this.props.actions.setPlayingNote({
      measure: measureIndex,
      noteIndex
    });
  }

  onNoteClick = (editingIndex) => {
    this.setState({ editingIndex });
  }

  getCurrentNote = () => {
    const { measures } = this.props.track;
    const { measureIndex, noteIndex } = this.state.editingIndex;

    return measures[measureIndex].notes[noteIndex];
  }

  getNextNote = (measureIndex, noteIndex) => {
    const { measures } = this.props.track;

    if(measureIndex === measures.length - 1 && noteIndex >= measures[measureIndex].notes.length - 1) {
      return 'NEW';
    } else if(noteIndex >= measures[measureIndex].notes.length - 1) {
      return {
        measureIndex: measureIndex + 1,
        noteIndex: 0
      };
    } else {
      return {
        measureIndex,
        noteIndex: noteIndex + 1
      };
    }
  }

  getPrevNote = (measureIndex, noteIndex) => {
    const { measures } = this.props.track;

    if(measureIndex === 0 && noteIndex === 0) {
      return { measureIndex, noteIndex };
    } else if(noteIndex === 0) {
      let prevMeasure = measures[measureIndex - 1];
      if(prevMeasure.notes.length > 0) {
        return {
          measureIndex: measureIndex - 1,
          noteIndex: measures[measureIndex - 1].notes.length - 1
        };
      } else {
        return {
          measureIndex: measureIndex - 1,
          noteIndex: 0
        };
      }
    } else {
      return {
        measureIndex,
        noteIndex: noteIndex - 1
      };
    }
  }

  getUpperString = (stringIndex) => {
    return stringIndex === 5 ? 0 : stringIndex + 1;
  }

  getLowerString = (stringIndex) => {
    return stringIndex === 0 ? 5 : stringIndex - 1;
  }

  navigateCursor = (event) => {
    let { measureIndex, noteIndex, stringIndex } = this.state.editingIndex;

    if(event.keyCode === 39) { // right arrow
      let newEditingIndex = this.getNextNote(measureIndex, noteIndex);
      if(newEditingIndex === 'NEW') {
        this.props.actions.insertMeasure(0);
        newEditingIndex = {
          stringIndex,
          measureIndex: measureIndex + 1,
          noteIndex: 0
        };
        this.setState({
          editingIndex: newEditingIndex
        });
      } else {
        newEditingIndex.stringIndex = stringIndex;
        this.setState({
          editingIndex: newEditingIndex
        });
      }
    } else if(event.keyCode === 37) { // left arrow
      let newEditingIndex = this.getPrevNote(measureIndex, noteIndex);
      newEditingIndex.stringIndex = stringIndex;
      this.setState({
        editingIndex: newEditingIndex
      });
    } else if(event.keyCode === 38) { // up arrow
      event.preventDefault();
      let newEditingIndex = {
        stringIndex: this.getUpperString(stringIndex),
        noteIndex,
        measureIndex
      };
      this.setState({
        editingIndex: newEditingIndex
      });
    } else if(event.keyCode === 40) { // down arrow
      event.preventDefault();
      let newEditingIndex = {
        stringIndex: this.getLowerString(stringIndex),
        noteIndex,
        measureIndex
      };
      this.setState({
        editingIndex: newEditingIndex
      });
    }
  }

  editNote = (fret) => {
    this.props.actions.changeNote(this.state.editingIndex, fret);
  }

  changeNoteLength = (duration) => {
    this.props.actions.changeNoteLength(this.state.editingIndex, duration);
  }

  increaseNoteLength = ({ measureIndex, noteIndex }) => {
    const note = this.props.track.measures[measureIndex].notes[noteIndex];

    let newDuration;
    switch(note.duration) {
      case 'w':
        newDuration = 'h';
        break;
      case 'h':
        newDuration = 'q';
        break;
      case 'q':
        newDuration = 'e';
        break;
      case 'e':
        newDuration = 's';
        break;
      case 's':
        newDuration = 't';
        break;
      default:
        newDuration = 's';
    }
    this.props.actions.changeNoteLength(this.state.editingIndex, newDuration);
  }

  decreaseNoteLength = ({ measureIndex, noteIndex }) => {
    const note = this.props.track.measures[measureIndex].notes[noteIndex];

    let newDuration;
    switch(note.duration) {
      case 't':
        newDuration = 's';
        break;
      case 's':
        newDuration = 'e';
        break;
      case 'e':
        newDuration = 'q';
        break;
      case 'q':
        newDuration = 'h';
        break;
      case 'h':
        newDuration = 'w';
        break;
      default:
        newDuration = 'w';
    }
    this.props.actions.changeNoteLength(this.state.editingIndex, newDuration);
  }

  deleteNote = () => {
    const { noteIndex, measureIndex, stringIndex } = this.state.editingIndex;
    let notes = this.props.track.measures[measureIndex].notes;

    if(notes.length > 1 && noteIndex === notes.length - 1 && notes[notes.length - 1].fret[0] === 'rest') {
      this.setState({
        editingIndex: {
          stringIndex,
          measureIndex,
          noteIndex: noteIndex - 1
        }
      }, () => {
        this.props.actions.deleteNote({ stringIndex, measureIndex, noteIndex });
      });
    } else if(notes.length === 0) {
      this.props.actions.deleteMeasure(measureIndex);

      if(measureIndex === this.props.track.measures.length) {
        this.setState({
          editingIndex: {
            stringIndex,
            measureIndex: measureIndex - 1,
            noteIndex: 0
          }
        });
      }
    } else {
      this.props.actions.deleteNote(this.state.editingIndex);
    }
  }

  insertNote = () => {
    const { noteIndex, measureIndex, stringIndex } = this.state.editingIndex;
    this.props.actions.insertNote(this.state.editingIndex);

    if(this.props.track.measures[measureIndex].notes.length !== 1) {
      this.setState({
        editingIndex: {
          measureIndex,
          stringIndex,
          noteIndex: noteIndex + 1
        }
      });
    }
  }

  pasteNote = () => {
    const { measureIndex, noteIndex, stringIndex } = this.state.editingIndex;

    if(this.props.clipboard) {
      event.preventDefault();
      this.props.actions.pasteNote(this.state.editingIndex, this.props.clipboard);
      this.setState({
        editingIndex: {
          measureIndex,
          noteIndex: this.props.track.measures[measureIndex].notes.length === 1 && noteIndex === 0 ? 0 : noteIndex + 1,
          stringIndex
        }
      });
    }
  }

  handleKeyPress = (event) => {
    if(this.state.openModal) {
      return;
    } else if(this.props.playingNote && event.keyCode !== 32) {
      return;
    }

    if((event.metaKey || event.ctrlKey) && event.keyCode === 67) { // cmd/ctrl+c
      event.preventDefault();
      return this.props.actions.copyNote(this.getCurrentNote());
    }
    if((event.metaKey || event.ctrlKey) && event.keyCode === 86) { // cmd/ctrl+v
      return this.pasteNote();
    }
    if((event.metaKey || event.ctrlKey) && event.keyCode === 88) { // cmd/ctrl+x
      event.preventDefault();
      const { noteIndex, measureIndex, stringIndex } = this.state.editingIndex;
      let notes = this.props.track.measures[measureIndex].notes;
      if(notes.length > 1 && noteIndex === notes.length - 1) {
        const currentNote = this.getCurrentNote();

        this.setState({
          editingIndex: {
            stringIndex,
            measureIndex,
            noteIndex: noteIndex - 1
          }
        }, () => {
          this.props.actions.cutNote({ noteIndex: noteIndex, measureIndex, stringIndex }, currentNote);
        });
      } else {
        return this.props.actions.cutNote({ noteIndex, measureIndex, stringIndex }, this.getCurrentNote());
      }
    }

    if(event.keyCode <= 57 && event.keyCode >= 48) {
      return this.editNote(event.keyCode - 48);
    } else if(event.keyCode === 82 && !event.metaKey && !event.ctrlKey) {
      this.props.actions.changeNote(this.state.editingIndex, 'rest');
    } else if(event.keyCode === 8) { // delete
      event.preventDefault();
      this.deleteNote();
    } else if(event.keyCode === 69) { // e
      return this.changeNoteLength('e');
    } else if(event.keyCode === 83) { // s
      return this.changeNoteLength('s');
    } else if(event.keyCode === 81) { // q
      return this.changeNoteLength('q');
    } else if(event.keyCode === 87) { // w
      return this.changeNoteLength('w');
    } else if(event.keyCode === 72 && !event.ctrlKey) { // h
      return this.changeNoteLength('h');
    } else if(event.keyCode === 73) { // i
      return this.insertNote();
    } else if(event.keyCode === 32) { // spacebar
      event.preventDefault();
      return this.props.playingNote ? this.handleStop() : this.handlePlay();
    } else if(event.keyCode === 190) { // period
      this.props.actions.toggleNoteDotted(this.state.editingIndex);
    } else if(event.shiftKey && event.keyCode === 187) { // plus
      return this.increaseNoteLength(this.state.editingIndex);
    } else if(event.keyCode === 189) { // minus
      return this.decreaseNoteLength(this.state.editingIndex);
    } else if(event.shiftKey && event.keyCode === 222) { // "
      this.props.actions.toggleNoteTremolo(this.state.editingIndex);
    } else {
      return this.navigateCursor(event);
    }
  }

  openTimeSignatureModal = () => {
    this.setState({ openModal: 'timeSignature' });
  }

  openBpmModal = () => {
    this.setState({ openModal: 'bpm' });
  }

  closeModal = () => {
    this.setState({ openModal: null });
  }

  openTuningModal = () => {
    this.setState({ openModal: 'tuning' });
  }

  render() {
    const { measures } = this.props.track;
    const { playingNote } = this.props;
    const { openModal, editingIndex, buffers } = this.state;
    const { measureIndex } = this.state.editingIndex;
    const timeSignature = measures[measureIndex] ? measures[measureIndex].timeSignature : '4/4';

    return (
      <div style={{ width: '100%', height: '100%' }}>
        { playingNote ? <Playback buffers={buffers} /> : null}
        <EditorArea handlePlay={this.handlePlay} handleStop={this.handleStop}
          openModal={this.openTimeSignatureModal}
          openTuning={this.openTuningModal}
          openBpm={this.openBpmModal}
          timeSignature={timeSignature}
          playingNote={playingNote}
        />
        <TabStaff onClick={this.onNoteClick} editingIndex={editingIndex} />
        <TimeSignatureModal isOpen={openModal === 'timeSignature'} closeModal={this.closeModal} measureIndex={measureIndex} />
        <TuningModal isOpen={openModal === 'tuning'} closeModal={this.closeModal} />
        <BpmModal isOpen={openModal === 'bpm'} closeModal={this.closeModal} index={editingIndex} />
      </div>
    );
  }
}

function mapStateToProps(state) {
  return {
    track: state.tracks[state.currentTrackIndex],
    clipboard: state.clipboard,
    layout: state.layout,
    playingNote: state.playingNote
  };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(Actions, dispatch)
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(App);
<commit_msg>fix bug deleting empty measure<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as TrackActions from '../actions/track';
import * as MeasureActions from '../actions/measure';
import * as PlayingNoteActions from '../actions/playingNote';

import Soundfont from 'soundfont-player';
import audioContext from '../util/audioContext';

import TabStaff from '../components/TabStaff';
import EditorArea from '../components/editor/EditorArea';
import TimeSignatureModal from '../components/editor/TimeSignatureModal';
import TuningModal from '../components/editor/TuningModal';
import BpmModal from '../components/editor/BpmModal';
import Playback from '../components/Playback';

const Actions = Object.assign(TrackActions, MeasureActions, PlayingNoteActions);

class App extends Component {
  constructor(props) {
    super(props);

    if (typeof window !== 'undefined') {
      window.addEventListener('keydown', this.handleKeyPress);
      window.addEventListener('resize', this.handleResize);
    }

    this.state = {
      editingIndex: {
        measureIndex: 0,
        noteIndex: 0,
        stringIndex: 0
      }
    };
  }

  componentWillMount = () => {
    Soundfont.loadBuffers(audioContext, 'acoustic_guitar_steel').then((buffers) => {
      this.setState({ buffers });
    });
  }

  componentWillReceiveProps = (nextProps) => {
    if(nextProps.playingNote) {
      this.setState({
        editingIndex: {
          measureIndex: nextProps.playingNote.measure,
          noteIndex: nextProps.playingNote.noteIndex,
          stringIndex: this.state.editingIndex.stringIndex
        }
      }, () => {
        this.updateScrollPosition();
      });
    }
  }

  handleResize = () => {
    this.props.actions.resize();
  }

  getXOfCurrentNote = () => {
    const { measure, noteIndex } = this.props.playingNote;
    const xOfMeasures = this.props.track.measures.reduce((acc, curr, i) => {
      if(i >= measure) {
        return acc;
      }
      return acc + curr.width;
    }, 0);

    return xOfMeasures + 55 * noteIndex;
  }

  updateScrollPosition = () => {
    const x = this.getXOfCurrentNote();
    const scrollX = window.scrollX;

    if(x > window.innerWidth + scrollX - 200 && this.props.layout === 'linear') {
      window.scroll(x - 200, 0);
    }
  }

  handleStop = () => {
    cancelAnimationFrame(this.state.timer);

    this.props.actions.setPlayingNote(null);
  }

  handlePlay = () => {
    if(this.props.playingNote || !this.state.buffers) {
      return;
    }

    const { noteIndex, measureIndex } = this.state.editingIndex;

    this.props.actions.setPlayingNote({
      measure: measureIndex,
      noteIndex
    });
  }

  onNoteClick = (editingIndex) => {
    this.setState({ editingIndex });
  }

  getCurrentNote = () => {
    const { measures } = this.props.track;
    const { measureIndex, noteIndex } = this.state.editingIndex;

    return measures[measureIndex].notes[noteIndex];
  }

  getNextNote = (measureIndex, noteIndex) => {
    const { measures } = this.props.track;

    if(measureIndex === measures.length - 1 && noteIndex >= measures[measureIndex].notes.length - 1) {
      return 'NEW';
    } else if(noteIndex >= measures[measureIndex].notes.length - 1) {
      return {
        measureIndex: measureIndex + 1,
        noteIndex: 0
      };
    } else {
      return {
        measureIndex,
        noteIndex: noteIndex + 1
      };
    }
  }

  getPrevNote = (measureIndex, noteIndex) => {
    const { measures } = this.props.track;

    if(measureIndex === 0 && noteIndex === 0) {
      return { measureIndex, noteIndex };
    } else if(noteIndex === 0) {
      let prevMeasure = measures[measureIndex - 1];
      if(prevMeasure.notes.length > 0) {
        return {
          measureIndex: measureIndex - 1,
          noteIndex: measures[measureIndex - 1].notes.length - 1
        };
      } else {
        return {
          measureIndex: measureIndex - 1,
          noteIndex: 0
        };
      }
    } else {
      return {
        measureIndex,
        noteIndex: noteIndex - 1
      };
    }
  }

  getUpperString = (stringIndex) => {
    return stringIndex === 5 ? 0 : stringIndex + 1;
  }

  getLowerString = (stringIndex) => {
    return stringIndex === 0 ? 5 : stringIndex - 1;
  }

  navigateCursor = (event) => {
    let { measureIndex, noteIndex, stringIndex } = this.state.editingIndex;

    if(event.keyCode === 39) { // right arrow
      let newEditingIndex = this.getNextNote(measureIndex, noteIndex);
      if(newEditingIndex === 'NEW') {
        this.props.actions.insertMeasure(0);
        newEditingIndex = {
          stringIndex,
          measureIndex: measureIndex + 1,
          noteIndex: 0
        };
        this.setState({
          editingIndex: newEditingIndex
        });
      } else {
        newEditingIndex.stringIndex = stringIndex;
        this.setState({
          editingIndex: newEditingIndex
        });
      }
    } else if(event.keyCode === 37) { // left arrow
      let newEditingIndex = this.getPrevNote(measureIndex, noteIndex);
      newEditingIndex.stringIndex = stringIndex;
      this.setState({
        editingIndex: newEditingIndex
      });
    } else if(event.keyCode === 38) { // up arrow
      event.preventDefault();
      let newEditingIndex = {
        stringIndex: this.getUpperString(stringIndex),
        noteIndex,
        measureIndex
      };
      this.setState({
        editingIndex: newEditingIndex
      });
    } else if(event.keyCode === 40) { // down arrow
      event.preventDefault();
      let newEditingIndex = {
        stringIndex: this.getLowerString(stringIndex),
        noteIndex,
        measureIndex
      };
      this.setState({
        editingIndex: newEditingIndex
      });
    }
  }

  editNote = (fret) => {
    this.props.actions.changeNote(this.state.editingIndex, fret);
  }

  changeNoteLength = (duration) => {
    this.props.actions.changeNoteLength(this.state.editingIndex, duration);
  }

  increaseNoteLength = ({ measureIndex, noteIndex }) => {
    const note = this.props.track.measures[measureIndex].notes[noteIndex];

    let newDuration;
    switch(note.duration) {
      case 'w':
        newDuration = 'h';
        break;
      case 'h':
        newDuration = 'q';
        break;
      case 'q':
        newDuration = 'e';
        break;
      case 'e':
        newDuration = 's';
        break;
      case 's':
        newDuration = 't';
        break;
      default:
        newDuration = 's';
    }
    this.props.actions.changeNoteLength(this.state.editingIndex, newDuration);
  }

  decreaseNoteLength = ({ measureIndex, noteIndex }) => {
    const note = this.props.track.measures[measureIndex].notes[noteIndex];

    let newDuration;
    switch(note.duration) {
      case 't':
        newDuration = 's';
        break;
      case 's':
        newDuration = 'e';
        break;
      case 'e':
        newDuration = 'q';
        break;
      case 'q':
        newDuration = 'h';
        break;
      case 'h':
        newDuration = 'w';
        break;
      default:
        newDuration = 'w';
    }
    this.props.actions.changeNoteLength(this.state.editingIndex, newDuration);
  }

  deleteNote = () => {
    const { noteIndex, measureIndex, stringIndex } = this.state.editingIndex;
    let notes = this.props.track.measures[measureIndex].notes;

    if(notes.length > 1 && noteIndex === notes.length - 1 && notes[notes.length - 1].fret[0] === 'rest') {
      this.setState({
        editingIndex: {
          stringIndex,
          measureIndex,
          noteIndex: noteIndex - 1
        }
      }, () => {
        this.props.actions.deleteNote({ stringIndex, measureIndex, noteIndex });
      });
    } else if(notes.length === 0) {
      if(measureIndex === this.props.track.measures.length - 1) {
        this.setState({
          editingIndex: {
            stringIndex,
            measureIndex: measureIndex - 1,
            noteIndex: 0
          }
        }, () => {
          this.props.actions.deleteMeasure(measureIndex);
        });
      } else {
        this.setState({
          editingIndex: {
            stringIndex,
            measureIndex,
            noteIndex: 0
          }
        }, () => {
          this.props.actions.deleteMeasure(measureIndex);
        });
      }
    } else {
      this.props.actions.deleteNote(this.state.editingIndex);
    }
  }

  insertNote = () => {
    const { noteIndex, measureIndex, stringIndex } = this.state.editingIndex;
    this.props.actions.insertNote(this.state.editingIndex);

    if(this.props.track.measures[measureIndex].notes.length !== 1) {
      this.setState({
        editingIndex: {
          measureIndex,
          stringIndex,
          noteIndex: noteIndex + 1
        }
      });
    }
  }

  pasteNote = () => {
    const { measureIndex, noteIndex, stringIndex } = this.state.editingIndex;

    if(this.props.clipboard) {
      event.preventDefault();
      this.props.actions.pasteNote(this.state.editingIndex, this.props.clipboard);
      this.setState({
        editingIndex: {
          measureIndex,
          noteIndex: this.props.track.measures[measureIndex].notes.length === 1 && noteIndex === 0 ? 0 : noteIndex + 1,
          stringIndex
        }
      });
    }
  }

  handleKeyPress = (event) => {
    if(this.state.openModal) {
      return;
    } else if(this.props.playingNote && event.keyCode !== 32) {
      return;
    }

    if((event.metaKey || event.ctrlKey) && event.keyCode === 67) { // cmd/ctrl+c
      event.preventDefault();
      return this.props.actions.copyNote(this.getCurrentNote());
    }
    if((event.metaKey || event.ctrlKey) && event.keyCode === 86) { // cmd/ctrl+v
      return this.pasteNote();
    }
    if((event.metaKey || event.ctrlKey) && event.keyCode === 88) { // cmd/ctrl+x
      event.preventDefault();
      const { noteIndex, measureIndex, stringIndex } = this.state.editingIndex;
      let notes = this.props.track.measures[measureIndex].notes;
      if(notes.length > 1 && noteIndex === notes.length - 1) {
        const currentNote = this.getCurrentNote();

        this.setState({
          editingIndex: {
            stringIndex,
            measureIndex,
            noteIndex: noteIndex - 1
          }
        }, () => {
          this.props.actions.cutNote({ noteIndex: noteIndex, measureIndex, stringIndex }, currentNote);
        });
      } else {
        return this.props.actions.cutNote({ noteIndex, measureIndex, stringIndex }, this.getCurrentNote());
      }
    }

    if(event.keyCode <= 57 && event.keyCode >= 48) {
      return this.editNote(event.keyCode - 48);
    } else if(event.keyCode === 82 && !event.metaKey && !event.ctrlKey) {
      this.props.actions.changeNote(this.state.editingIndex, 'rest');
    } else if(event.keyCode === 8) { // delete
      event.preventDefault();
      this.deleteNote();
    } else if(event.keyCode === 69) { // e
      return this.changeNoteLength('e');
    } else if(event.keyCode === 83) { // s
      return this.changeNoteLength('s');
    } else if(event.keyCode === 81) { // q
      return this.changeNoteLength('q');
    } else if(event.keyCode === 87) { // w
      return this.changeNoteLength('w');
    } else if(event.keyCode === 72 && !event.ctrlKey) { // h
      return this.changeNoteLength('h');
    } else if(event.keyCode === 73) { // i
      return this.insertNote();
    } else if(event.keyCode === 32) { // spacebar
      event.preventDefault();
      return this.props.playingNote ? this.handleStop() : this.handlePlay();
    } else if(event.keyCode === 190) { // period
      this.props.actions.toggleNoteDotted(this.state.editingIndex);
    } else if(event.shiftKey && event.keyCode === 187) { // plus
      return this.increaseNoteLength(this.state.editingIndex);
    } else if(event.keyCode === 189) { // minus
      return this.decreaseNoteLength(this.state.editingIndex);
    } else if(event.shiftKey && event.keyCode === 222) { // "
      this.props.actions.toggleNoteTremolo(this.state.editingIndex);
    } else {
      return this.navigateCursor(event);
    }
  }

  openTimeSignatureModal = () => {
    this.setState({ openModal: 'timeSignature' });
  }

  openBpmModal = () => {
    this.setState({ openModal: 'bpm' });
  }

  closeModal = () => {
    this.setState({ openModal: null });
  }

  openTuningModal = () => {
    this.setState({ openModal: 'tuning' });
  }

  render() {
    const { measures } = this.props.track;
    const { playingNote } = this.props;
    const { openModal, editingIndex, buffers } = this.state;
    const { measureIndex } = this.state.editingIndex;
    const timeSignature = measures[measureIndex] ? measures[measureIndex].timeSignature : '4/4';

    return (
      <div style={{ width: '100%', height: '100%' }}>
        { playingNote ? <Playback buffers={buffers} /> : null}
        <EditorArea handlePlay={this.handlePlay} handleStop={this.handleStop}
          openModal={this.openTimeSignatureModal}
          openTuning={this.openTuningModal}
          openBpm={this.openBpmModal}
          timeSignature={timeSignature}
          playingNote={playingNote}
        />
        <TabStaff onClick={this.onNoteClick} editingIndex={editingIndex} />
        <TimeSignatureModal isOpen={openModal === 'timeSignature'} closeModal={this.closeModal} measureIndex={measureIndex} />
        <TuningModal isOpen={openModal === 'tuning'} closeModal={this.closeModal} />
        <BpmModal isOpen={openModal === 'bpm'} closeModal={this.closeModal} index={editingIndex} />
      </div>
    );
  }
}

function mapStateToProps(state) {
  return {
    track: state.tracks[state.currentTrackIndex],
    clipboard: state.clipboard,
    layout: state.layout,
    playingNote: state.playingNote
  };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(Actions, dispatch)
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(App);
<|endoftext|>


<commit_before>var createResourceLink = function(id, name) {
  var textNode = document.createTextNode(name);
  var link = document.createElement('a');
  link.setAttribute('class', 'edit-resource');
  link.setAttribute('data-resource-id', id);
  link.appendChild(textNode);
  return link;
}

Template.timeline.onCreated(function() {
  self = this;

  Resources.find().observeChanges({
    added: function (id, fields) {
    },
    changed: function(id, fields) {
      if(undefined != fields.name) {
        // for some reason we need to clear content. otherwise it will append the new content
        timeline.groups.update({id: id, content: ''});
        var item = {id: id, content: createResourceLink(id, fields.name )};
        timeline.groups.update(item);
      }
    },
    removed: function (id) {
      timeline.groups.remove(id);
    }
  });
  Entries.find().observeChanges({
    added: function (id, fields) {
    },
    changed: function(id, fields) {
      if(undefined != fields.project) {
        // for some reason we need to clear content. otherwise it will append the new content
        var item = {id: id, className: fields.project};
        timeline.entries.update(item);
      }
    },
    removed: function (id) {
      timeline.entries.remove(id);
    }
  });

})

Template.timeline.onRendered(function() {
  var container = document.getElementById('timeline-chart');

  timeline.init(container);

  FlowRouter.subsReady("resources", function() {
    timeline.addGroups();
  });
  FlowRouter.subsReady("entries", function() {
    timeline.addEntries();
  });
});

Template.timeline.helpers({
  projects: ()=> {
    return Projects.find({active: true});
  }
});

Template.timeline.events({
  'click .edit-project': function(e, template) {
    var id = $(e.currentTarget).attr('data-project-id');
    Overlay.show('editProject', {project: Projects.findOne(id)});
  },
  'click .edit-resource': function(e, template) {
    var id = $(e.currentTarget).attr('data-resource-id');
    Overlay.show('editResource', {resource: Resources.findOne(id)});
  },
  'click .item': function(e, template) {
    var id = $(e.currentTarget).attr('data-id');
   // Overlay.show('editEntry', {entry: Entries.findOne(id)});
  }
});

var timeline = {
  container: null,
  entries: new vis.DataSet(),
  groups: new vis.DataSet(),
  timelineBeginAt: moment().locale('de').second(0).minute(0).hour(0).day(1).month(1).year(moment().locale('de').year()-1),
  timelineEndAt: moment().locale('de').second(59).minute(23).hour(59).day(31).month(12).year(moment().locale('de').year()+1),
  timeline: null,
  init: function(container) {
    this.container = container;
    // set min, max
    this.options.min = this.timelineBeginAt.toDate();
    this.options.max = this.timelineEndAt.toDate();
    // hide sa, sar
    this.options.hiddenDates[0].start = this.timelineBeginAt.isoWeekday('Saturday').format("YYYY-MM-DD HH:mm:ss");
    this.options.hiddenDates[0].end = this.timelineBeginAt.day(8).isoWeekday('Monday').format("YYYY-MM-DD HH:mm:ss");
    this.timeline = new vis.Timeline(this.container);
    this.timeline.setOptions(this.options);
    this.timeline.setGroups(this.groups);
    this.timeline.setItems(this.entries);

  },
  addEntries: function() {
    var self = this;
    Entries.find().map(function(entry) {
      var item = {start: new Date(entry.from), end: new Date(entry.till), group: entry.resource, className: entry.project, id: entry._id};
      self.entries.add(item);
    });
  },
  addGroups: function() {
    var self = this;
    Resources.find().map(function(res) {
      self.groups.add({content: createResourceLink(res._id, res.name), id: res._id, value: res.name, className: res._id});
    });
  },
  options: {
    groupOrder: function (a, b) {
      return a.value - b.value;
    },
    groupOrderSwap: function (a, b, groups) {
    	var v = a.value;
    	a.value = b.value;
    	b.value = v;
    },
    orientation: 'top',
    editable: true,
    start: moment().locale('de').toDate(),
    dataAttributes: ['id'],
    onAdd: function(item, callback) {
      var d = moment(item.start).hour(0).minute(0),
          nd = d.clone().day(d.day()+1);
      item.start = d.toDate();
      item.end = nd.toDate();
      item.type = 'range';
      callback(item);
    },
    hiddenDates: [
      {start: '2013-10-26 00:00:00', end: '2013-10-28 00:00:00', repeat: 'weekly'}, // daily weekly monthly yearly
    ],
    snap: function (date, scale, step) {
      var snapTo = moment(date).locale('de').hour(0).minute(0).second(0);

      return snapTo.toDate();
    },
    format: {
      minorLabels: {
        millisecond:'SSS',
        second:     's',
        minute:     'HH:mm',
        hour:       'HH:mm',
        weekday:    'ddd D.',
        day:        '[KW-]w D.',
        week:       '[KW-]w',
        month:      'MMM',
        year:       'YYYY'
      },
      majorLabels: {
        millisecond:'HH:mm:ss',
        second:     'D MMMM HH:mm',
        minute:     'ddd D MMMM',
        hour:       'ddd D MMMM',
        weekday:    'MMMM YYYY [KW-]w',
        day:        'MMMM YYYY',
        week:       'MMMM YYYY',
        month:      'YYYY',
        year:       ''
      }
    },
    timeAxis: {scale: 'weekday', step: 1},
    locales: {
      de: {
        current: 'Aktuelle',
        time: 'Zeit'
      }
    },
    locale: 'de',
    min: null,
    max: null,
    zoomMin: 1000 * 60 * 60 * 24 * 5,             // one day in milliseconds
    zoomMax: 1000 * 60 * 60 * 24 * 31 * 12     // about three months in milliseconds
  }
}

<commit_msg>set zoom on init, set minimum range on update<commit_after>var createResourceLink = function(id, name) {
  var textNode = document.createTextNode(name);
  var link = document.createElement('a');
  link.setAttribute('class', 'edit-resource');
  link.setAttribute('data-resource-id', id);
  link.appendChild(textNode);
  return link;
}

Template.timeline.onCreated(function() {
  self = this;

  Resources.find().observeChanges({
    added: function (id, fields) {
    },
    changed: function(id, fields) {
      if(undefined != fields.name) {
        // for some reason we need to clear content. otherwise it will append the new content
        timeline.groups.update({id: id, content: ''});
        var item = {id: id, content: createResourceLink(id, fields.name )};
        timeline.groups.update(item);
      }
    },
    removed: function (id) {
      timeline.groups.remove(id);
    }
  });
  Entries.find().observeChanges({
    added: function (id, fields) {
    },
    changed: function(id, fields) {
      if(undefined != fields.project) {
        // for some reason we need to clear content. otherwise it will append the new content
        var item = {id: id, className: fields.project};
        timeline.entries.update(item);
      }
    },
    removed: function (id) {
      timeline.entries.remove(id);
    }
  });

})

Template.timeline.onRendered(function() {
  var container = document.getElementById('timeline-chart');

  timeline.init(container);

  FlowRouter.subsReady("resources", function() {
    timeline.addGroups();
  });
  FlowRouter.subsReady("entries", function() {
    timeline.addEntries();
  });
});

Template.timeline.helpers({
  projects: ()=> {
    return Projects.find({active: true});
  }
});

Template.timeline.events({
  'click .edit-project': function(e, template) {
    var id = $(e.currentTarget).attr('data-project-id');
    Overlay.show('editProject', {project: Projects.findOne(id)});
  },
  'click .edit-resource': function(e, template) {
    var id = $(e.currentTarget).attr('data-resource-id');
    Overlay.show('editResource', {resource: Resources.findOne(id)});
  },
  'click .item': function(e, template) {
    var id = $(e.currentTarget).attr('data-id');
   // Overlay.show('editEntry', {entry: Entries.findOne(id)});
  }
});

var timeline = {
  container: null,
  entries: new vis.DataSet(),
  groups: new vis.DataSet(),
  timelineBeginAt: moment().locale('de').second(0).minute(0).hour(0).day(1).month(1).year(moment().locale('de').year()-1),
  timelineEndAt: moment().locale('de').second(59).minute(23).hour(59).day(31).month(12).year(moment().locale('de').year()+1),
  timeline: null,
  init: function(container) {
    var self = this;
    this.container = container;
    // set min, max
    this.options.min = this.timelineBeginAt.toDate();
    this.options.max = this.timelineEndAt.toDate();
    // hide sa, sar
    this.options.hiddenDates[0].start = this.timelineBeginAt.isoWeekday('Saturday').format("YYYY-MM-DD HH:mm:ss");
    this.options.hiddenDates[0].end = this.timelineBeginAt.day(8).isoWeekday('Monday').format("YYYY-MM-DD HH:mm:ss");
    this.timeline = new vis.Timeline(this.container);
    this.timeline.setOptions(this.options);
    this.timeline.setGroups(this.groups);
    this.timeline.setItems(this.entries);
    self.timeline.setWindow(
      moment().isoWeekday('Monday').hour(0).minute(0).toDate(),
      moment().day(30).hour(0).minute(0).toDate()
    );
    this.entries.on('update', function(event, properties, senderId) {
      var item = properties.data[0],
          end = moment(item.end),
          diff = end.diff(item.start);
      if((diff/1000/60/60/24) < 0.9) {
        self.entries.update({id: item.id, end: end.day(end.day()+1).toDate()});
      };
    });
  },
  addEntries: function() {
    var self = this;
    Entries.find().map(function(entry) {
      var item = {start: new Date(entry.from), end: new Date(entry.till), group: entry.resource, className: entry.project, id: entry._id};
      self.entries.add(item);
    });
  },
  addGroups: function() {
    var self = this;
    Resources.find().map(function(res) {
      self.groups.add({content: createResourceLink(res._id, res.name), id: res._id, value: res.name, className: res._id});
    });
  },
  options: {
    groupOrder: function (a, b) {
      return a.value - b.value;
    },
    groupOrderSwap: function (a, b, groups) {
    	var v = a.value;
    	a.value = b.value;
    	b.value = v;
    },
    orientation: 'top',
    editable: true,
    start: moment().locale('de').toDate(),
    dataAttributes: ['id'],
    onAdd: function(item, callback) {
      var d = moment(item.start).hour(0).minute(0),
          nd = d.clone().day(d.day()+1);
      item.start = d.toDate();
      item.end = nd.toDate();
      item.type = 'range';
      callback(item);
    },
    onUpdate: function(item, callback) {
      callback(item);
    },
    snap: function (date, scale, step) {
      var snapTo = moment(date).locale('de').hour(0).minute(0).second(0);
      return snapTo.toDate();
    },
    hiddenDates: [
      {start: '2013-10-26 00:00:00', end: '2013-10-28 00:00:00', repeat: 'weekly'}, // daily weekly monthly yearly
    ],
    format: {
      minorLabels: {
        millisecond:'SSS',
        second:     's',
        minute:     'HH:mm',
        hour:       'HH:mm',
        weekday:    'ddd D.',
        day:        '[KW-]w D.',
        week:       '[KW-]w',
        month:      'MMM',
        year:       'YYYY'
      },
      majorLabels: {
        millisecond:'HH:mm:ss',
        second:     'D MMMM HH:mm',
        minute:     'ddd D MMMM',
        hour:       'ddd D MMMM',
        weekday:    'MMMM YYYY [KW-]w',
        day:        'MMMM YYYY',
        week:       'MMMM YYYY',
        month:      'YYYY',
        year:       ''
      }
    },
    timeAxis: {scale: 'weekday', step: 1},
    locales: {
      de: {
        current: 'Aktuelle',
        time: 'Zeit'
      }
    },
    locale: 'de',
    min: null,
    max: null,
    zoomMin: 1000 * 60 * 60 * 24 * 5,             // one day in milliseconds
    zoomMax: 1000 * 60 * 60 * 24 * 31 * 12     // about three months in milliseconds
  }
}

<|endoftext|>


<commit_before>'use strict';

var ServiceWorkerBuilder = require('../lib/service-worker-builder');
var assert = require('chai').assert;
var fs = require('fs');
var path = require('path');
var helper = require('broccoli-test-helper');
var Babel = require('broccoli-babel-transpiler');
var EmberCliBabel = require('ember-cli-babel');

var fixturePath = path.join(__dirname, 'fixtures');
var createBuilder = helper.createBuilder;

var generatePlugin = function(name, projectPath) {
  return {
    pkg: { name: name },
    root: path.join(fixturePath, projectPath),
  };
};

describe('Service Worker Builder', () => {
  let app;
  let build;
  let output;

  beforeEach(() => {

    build = function(options, type) {
      let subject = new ServiceWorkerBuilder(options).build(type);
      output = createBuilder(subject);
      return output.build();
    };

    app = {
      treeGenerator: (dir) => dir,
      project: {
        addons: [
          {
            name: 'ember-cli-babel',
            transpileTree(tree, options) {
              return new Babel(tree, {
                annotation: "Babel: app-with-sw",
                babelrc: false,
                highlightCode: false,
                sourceMaps: false,
                presets: [
                  EmberCliBabel._getPresetEnvPlugins({ 'ember-cli-babel': { compileModules: false } }),
                ].filter(Boolean)
              });
            }
          }
        ]
      },
    };
  });

  afterEach(() => output.dispose());

  it('returns a tree with the sw.js file', () => {
    let plugins = [generatePlugin('test-project', 'builder-test')];
    return build({ app, plugins }, 'service-worker').then(() => {
      let files = output.read();
      assert.property(files, 'sw.js');
    });
  });

  it('returns a tree with the sw-registration.js file', () => {
    let plugins = [generatePlugin('test-project', 'builder-test')];
    return build({ app, plugins }, 'service-worker-registration').then(() => {
      let files = output.read();
      assert.property(files, 'sw-registration.js');
    });
  });

  it('transpiles code with babel', () => {
    let plugins = [generatePlugin('test-project', 'builder-test/babel')];
    return build({ app, plugins }, 'service-worker').then(() => {
      let expected = `
(function () {
  'use strict';

  var CONSTANT = 42;
  self.addEventListener('fetch', function () {
    var x = CONSTANT + 1;
  });

}());
`.trim();
      let files = output.read();
      assert.property(files, 'sw.js');
      assert.equal(files['sw.js'], expected);
    });
  });

  it('uses rollup to concat all modules in a file', () => {
    let plugins = [
      generatePlugin('plugin-a', 'builder-test/rollup/plugin-a'),
      generatePlugin('plugin-b', 'builder-test/rollup/plugin-b')
    ];

    return build({ app, plugins }, 'service-worker')
      .then((results) => {
        let expected = `
(function () {
  'use strict';

  var CONSTANT = 42;

  self.addEventListener('fetch', function () {
    var x = CONSTANT + 1;
  });

}());
`.trim();

        let files = output.read();
        assert.property(files, 'sw.js');
        assert.equal(files['sw.js'], expected);
      });
  });

  it('uglifies the code when `minifyJS` is enabled', () => {
    let plugins = [generatePlugin('test-project', 'builder-test')];
    return build({ app, plugins, minifyJS: { enabled: true } }, 'service-worker')
      .then((results) => {
        let expected = `
!function(){"use strict"}();
//# sourceMappingURL=sw.map
`.trim();

        let files = output.read();
        assert.property(files, 'sw.js');
        assert.equal(files['sw.js'], expected);
      });
  });
});
<commit_msg>tests: fix minifyJS test after uglifyjs upgrade<commit_after>'use strict';

var ServiceWorkerBuilder = require('../lib/service-worker-builder');
var assert = require('chai').assert;
var fs = require('fs');
var path = require('path');
var helper = require('broccoli-test-helper');
var Babel = require('broccoli-babel-transpiler');
var EmberCliBabel = require('ember-cli-babel');

var fixturePath = path.join(__dirname, 'fixtures');
var createBuilder = helper.createBuilder;

var generatePlugin = function(name, projectPath) {
  return {
    pkg: { name: name },
    root: path.join(fixturePath, projectPath),
  };
};

describe('Service Worker Builder', () => {
  let app;
  let build;
  let output;

  beforeEach(() => {

    build = function(options, type) {
      let subject = new ServiceWorkerBuilder(options).build(type);
      output = createBuilder(subject);
      return output.build();
    };

    app = {
      treeGenerator: (dir) => dir,
      project: {
        addons: [
          {
            name: 'ember-cli-babel',
            transpileTree(tree, options) {
              return new Babel(tree, {
                annotation: "Babel: app-with-sw",
                babelrc: false,
                highlightCode: false,
                sourceMaps: false,
                presets: [
                  EmberCliBabel._getPresetEnvPlugins({ 'ember-cli-babel': { compileModules: false } }),
                ].filter(Boolean)
              });
            }
          }
        ]
      },
    };
  });

  afterEach(() => output.dispose());

  it('returns a tree with the sw.js file', () => {
    let plugins = [generatePlugin('test-project', 'builder-test')];
    return build({ app, plugins }, 'service-worker').then(() => {
      let files = output.read();
      assert.property(files, 'sw.js');
    });
  });

  it('returns a tree with the sw-registration.js file', () => {
    let plugins = [generatePlugin('test-project', 'builder-test')];
    return build({ app, plugins }, 'service-worker-registration').then(() => {
      let files = output.read();
      assert.property(files, 'sw-registration.js');
    });
  });

  it('transpiles code with babel', () => {
    let plugins = [generatePlugin('test-project', 'builder-test/babel')];
    return build({ app, plugins }, 'service-worker').then(() => {
      let expected = `
(function () {
  'use strict';

  var CONSTANT = 42;
  self.addEventListener('fetch', function () {
    var x = CONSTANT + 1;
  });

}());
`.trim();
      let files = output.read();
      assert.property(files, 'sw.js');
      assert.equal(files['sw.js'], expected);
    });
  });

  it('uses rollup to concat all modules in a file', () => {
    let plugins = [
      generatePlugin('plugin-a', 'builder-test/rollup/plugin-a'),
      generatePlugin('plugin-b', 'builder-test/rollup/plugin-b')
    ];

    return build({ app, plugins }, 'service-worker')
      .then((results) => {
        let expected = `
(function () {
  'use strict';

  var CONSTANT = 42;

  self.addEventListener('fetch', function () {
    var x = CONSTANT + 1;
  });

}());
`.trim();

        let files = output.read();
        assert.property(files, 'sw.js');
        assert.equal(files['sw.js'], expected);
      });
  });

  it('uglifies the code when `minifyJS` is enabled', () => {
    let plugins = [generatePlugin('test-project', 'builder-test')];
    return build({ app, plugins, minifyJS: { enabled: true } }, 'service-worker')
      .then((results) => {
        let expected = '\n//# sourceMappingURL=sw.map';

        let files = output.read();
        assert.property(files, 'sw.js');
        assert.equal(files['sw.js'], expected);
      });
  });
});
<|endoftext|>


<commit_before>/* SQB
 ------------------------
 (c) 2017-present Panates
 SQB may be freely distributed under the MIT license.
 For details and documentation:
 https://panates.github.io/sqb/
 */
'use strict';

/**
 * Module dependencies.
 * @private
 */
const EventEmitter = require('events').EventEmitter;
const debug = require('debug')('sqb:Connection');
const PreparedQuery = require('./PreparedQuery');
const sqlObjects = require('../sqb_ns');
const merge = require('putil-merge');

/**
 *
 * @class
 */
class Connection extends EventEmitter {
  /**
   *
   * @param {Pool} pool
   * @param {Object} client
   * @param {Object} options
   * @param {Boolean} [options.autoCommit]
   * @constructor
   */
  constructor(pool, client, options) {
    super();
    this._pool = pool;
    this._sessionId = client.sessionId;
    this._client = client;
    this._refCount = 1;
    this._autoCommit = options.autoCommit;
    this.on('error', (e) => {
      if (process.env.DEBUG)
        debug('[%s] error | %o', this.sessionId, e);
    });
  }

  /**
   * Returns true if connection is released
   *
   * @return {Boolean}
   */
  get isClosed() {
    return !this._client;
  }

  /**
   * Returns session id
   *
   * @return {String|Integer}
   */
  get sessionId() {
    return this._sessionId;
  }

  /**
   * Returns reference counter value
   *
   * @return {number}
   */
  get referenceCount() {
    return this._refCount;
  }

  /**
   * Returns the Pool instance that connection owned by
   *
   * @return {Pool}
   */
  get pool() {
    return this._pool;
  }

  /**
   * Returns connection is in auto commit mode or not
   *
   * @return {Boolean}
   */
  get autoCommit() {
    return this._autoCommit;
  }

  /**
   * This call Executes a query
   *
   * @param {String|Query} query
   * @param {Object} [options]
   * @return {Promise}
   */
  execute(query, options) {
    /* Prepare query */
    try {
      const prepared = new PreparedQuery(this, query, options);
      return prepared.execute();
    } catch (e) {
      this.emitSafe('error', e);
      return Promise.reject(e);
    }
  }

  /**
   * Increases internal reference counter of connection instance
   *
   * @public
   */
  acquire() {
    this._refCount++;
    debug('[%s] acquired | refCount: %s', this.sessionId, this._refCount);
  }

  /**
   * Decreases the internal reference counter.
   * When reference count is 0, connection returns to the pool.
   * Returns true if connection released.
   *
   * @return {Boolean}
   * @public
   */
  release() {
    if (this.isClosed || !this._refCount)
      throw new Error('Connection closed');
    const ref = --this._refCount;
    if (!ref) {
      debug('[%s] close', this.sessionId);
      this._client = null;
      this.emitSafe('closing');
      return true;
    } else
      debug('[%s] release | refCount: %s', this.sessionId, ref);
    return false;
  }

  /**
   * Returns values of properties that connection adapter exposes.
   *
   * @param {String} param
   * @return {*}
   */
  get(param) {
    return this._client && (typeof this._client.get === 'function') &&
        this._client.get(param);
  }

  /**
   * Starts a transaction
   *
   * @return {Promise}
   * @public
   */
  startTransaction() {
    if (this.isClosed)
      return Promise.reject(new Error('Connection closed'));
    return this._client.startTransaction().then(() => {
      debug('[%s] start transaction', this.sessionId);
      this.emitSafe('start-transaction');
    }).catch(e => {
      this.emitSafe('error', e);
      throw e;
    });
  }

  /**
   * Commits the current transaction in progress on the connection.
   *
   * @return {Promise}
   * @public
   */
  commit() {
    if (this.isClosed)
      return Promise.reject(new Error('Connection closed'));
    return this._client.commit().then(() => {
      debug('[%s] commit', this.sessionId);
      this.emitSafe('commit');
    }).catch(e => {
      this.emitSafe('error', e);
      throw e;
    });
  }

  /**
   * Rolls back the current transaction in progress on the connection.
   *
   * @return {Promise}
   * @public
   */
  rollback() {
    if (this.isClosed)
      return Promise.reject(new Error('Connection closed'));
    return this._client.rollback().then(() => {
      debug('[%s] rollback', this.sessionId);
      this.emitSafe('rollback');
    }).catch(e => {
      this.emitSafe('error', e);
      throw e;
    });
  }

  /**
   * This call tests the connection.
   * @return {Promise}
   */
  test() {
    return this._client.test().catch(e => {
      this.emitSafe('error', e);
      throw e;
    });
  }

  /**
   * Creates an executable SelectQuery associated with this connection.
   * @param {...*} column
   * @return {SelectQuery}
   */
  select(...column) {
    const query = this.pool.select(...column);
    query._dbobj = this;
    return query;
  }

  /**
   * Creates an executable InsertQuery associated with this connection.
   * @param {string|Raw} tableName
   * @param {Object} values
   * @return {InsertQuery}
   */
  insert(tableName, values) {
    const query = this.pool.insert(tableName, values);
    query._dbobj = this;
    return query;
  }

  /**
   * Creates an executable UpdateQuery associated with this connection.
   * @param {String} tableName
   * @param {Object} values
   * @return {UpdateQuery}
   */
  update(tableName, values) {
    const query = this.pool.update(tableName, values);
    query._dbobj = this;
    return query;
  }

  /**
   * Creates an executable DeleteQuery associated with this connection.
   * @param {String} tableName
   * @return {DeleteQuery}
   */
  delete(tableName) {
    const query = this.pool.delete(tableName);
    query._dbobj = this;
    return query;
  }

  toString() {
    return '[object ' + Object.getPrototypeOf(this).constructor.name + '(' +
        this.sessionId + ')]';
  }

  inspect() {
    return this.toString();
  }

  /**
   *
   */
  emitSafe(...args) {
    try {
      this.emit(...args);
    } catch (ignored) {
      debug('emit-error', ignored);
    }
  }

}

merge(Connection.prototype, sqlObjects, {descriptor: true});

/**
 * Expose `Connection`.
 */
module.exports = Connection;

<commit_msg>Fixed: Pools "execute" event is not getting emitted if acquired Connection is used.<commit_after>/* SQB
 ------------------------
 (c) 2017-present Panates
 SQB may be freely distributed under the MIT license.
 For details and documentation:
 https://panates.github.io/sqb/
 */
'use strict';

/**
 * Module dependencies.
 * @private
 */
const EventEmitter = require('events').EventEmitter;
const debug = require('debug')('sqb:Connection');
const PreparedQuery = require('./PreparedQuery');
const sqlObjects = require('../sqb_ns');
const merge = require('putil-merge');

/**
 *
 * @class
 */
class Connection extends EventEmitter {
  /**
   *
   * @param {Pool} pool
   * @param {Object} client
   * @param {Object} options
   * @param {Boolean} [options.autoCommit]
   * @constructor
   */
  constructor(pool, client, options) {
    super();
    this._pool = pool;
    this._sessionId = client.sessionId;
    this._client = client;
    this._refCount = 1;
    this._autoCommit = options.autoCommit;
    this.on('error', (e) => {
      if (process.env.DEBUG)
        debug('[%s] error | %o', this.sessionId, e);
    });
    this.on('execute', (...args) => {
      pool.emitSafe('execute', ...args);
    });
  }

  /**
   * Returns true if connection is released
   *
   * @return {Boolean}
   */
  get isClosed() {
    return !this._client;
  }

  /**
   * Returns session id
   *
   * @return {String|Integer}
   */
  get sessionId() {
    return this._sessionId;
  }

  /**
   * Returns reference counter value
   *
   * @return {number}
   */
  get referenceCount() {
    return this._refCount;
  }

  /**
   * Returns the Pool instance that connection owned by
   *
   * @return {Pool}
   */
  get pool() {
    return this._pool;
  }

  /**
   * Returns connection is in auto commit mode or not
   *
   * @return {Boolean}
   */
  get autoCommit() {
    return this._autoCommit;
  }

  /**
   * This call Executes a query
   *
   * @param {String|Query} query
   * @param {Object} [options]
   * @return {Promise}
   */
  execute(query, options) {
    /* Prepare query */
    try {
      const prepared = new PreparedQuery(this, query, options);
      return prepared.execute();
    } catch (e) {
      this.emitSafe('error', e);
      return Promise.reject(e);
    }
  }

  /**
   * Increases internal reference counter of connection instance
   *
   * @public
   */
  acquire() {
    this._refCount++;
    debug('[%s] acquired | refCount: %s', this.sessionId, this._refCount);
  }

  /**
   * Decreases the internal reference counter.
   * When reference count is 0, connection returns to the pool.
   * Returns true if connection released.
   *
   * @return {Boolean}
   * @public
   */
  release() {
    if (this.isClosed || !this._refCount)
      throw new Error('Connection closed');
    const ref = --this._refCount;
    if (!ref) {
      debug('[%s] close', this.sessionId);
      this._client = null;
      this.emitSafe('closing');
      return true;
    } else
      debug('[%s] release | refCount: %s', this.sessionId, ref);
    return false;
  }

  /**
   * Returns values of properties that connection adapter exposes.
   *
   * @param {String} param
   * @return {*}
   */
  get(param) {
    return this._client && (typeof this._client.get === 'function') &&
        this._client.get(param);
  }

  /**
   * Starts a transaction
   *
   * @return {Promise}
   * @public
   */
  startTransaction() {
    if (this.isClosed)
      return Promise.reject(new Error('Connection closed'));
    return this._client.startTransaction().then(() => {
      debug('[%s] start transaction', this.sessionId);
      this.emitSafe('start-transaction');
    }).catch(e => {
      this.emitSafe('error', e);
      throw e;
    });
  }

  /**
   * Commits the current transaction in progress on the connection.
   *
   * @return {Promise}
   * @public
   */
  commit() {
    if (this.isClosed)
      return Promise.reject(new Error('Connection closed'));
    return this._client.commit().then(() => {
      debug('[%s] commit', this.sessionId);
      this.emitSafe('commit');
    }).catch(e => {
      this.emitSafe('error', e);
      throw e;
    });
  }

  /**
   * Rolls back the current transaction in progress on the connection.
   *
   * @return {Promise}
   * @public
   */
  rollback() {
    if (this.isClosed)
      return Promise.reject(new Error('Connection closed'));
    return this._client.rollback().then(() => {
      debug('[%s] rollback', this.sessionId);
      this.emitSafe('rollback');
    }).catch(e => {
      this.emitSafe('error', e);
      throw e;
    });
  }

  /**
   * This call tests the connection.
   * @return {Promise}
   */
  test() {
    return this._client.test().catch(e => {
      this.emitSafe('error', e);
      throw e;
    });
  }

  /**
   * Creates an executable SelectQuery associated with this connection.
   * @param {...*} column
   * @return {SelectQuery}
   */
  select(...column) {
    const query = this.pool.select(...column);
    query._dbobj = this;
    return query;
  }

  /**
   * Creates an executable InsertQuery associated with this connection.
   * @param {string|Raw} tableName
   * @param {Object} values
   * @return {InsertQuery}
   */
  insert(tableName, values) {
    const query = this.pool.insert(tableName, values);
    query._dbobj = this;
    return query;
  }

  /**
   * Creates an executable UpdateQuery associated with this connection.
   * @param {String} tableName
   * @param {Object} values
   * @return {UpdateQuery}
   */
  update(tableName, values) {
    const query = this.pool.update(tableName, values);
    query._dbobj = this;
    return query;
  }

  /**
   * Creates an executable DeleteQuery associated with this connection.
   * @param {String} tableName
   * @return {DeleteQuery}
   */
  delete(tableName) {
    const query = this.pool.delete(tableName);
    query._dbobj = this;
    return query;
  }

  toString() {
    return '[object ' + Object.getPrototypeOf(this).constructor.name + '(' +
        this.sessionId + ')]';
  }

  inspect() {
    return this.toString();
  }

  /**
   *
   */
  emitSafe(...args) {
    try {
      this.emit(...args);
    } catch (ignored) {
      debug('emit-error', ignored);
    }
  }

}

merge(Connection.prototype, sqlObjects, {descriptor: true});

/**
 * Expose `Connection`.
 */
module.exports = Connection;
<|endoftext|>