<commit_before>
/**
 * Module dependencies.
 */

var fs = require('fs')
  , path = require('path')
  , join = path.join
  , dirname = path.dirname
  , exists = fs.existsSync || path.existsSync
  , defaults = {
        arrow: process.env.NODE_BINDINGS_ARROW || ' → '
      , compiled: 'compiled'
      , platform: process.platform
      , arch: process.arch
      , version: parseVersion(process.versions.node)
      , bindings: 'bindings.node'
      , try: [
          // node-gyp's linked version in the "build" dir
          [ 'module_root', 'build', 'bindings' ]
          // node-waf and gyp_addon (a.k.a node-gyp)
        , [ 'module_root', 'build', 'Debug', 'bindings' ]
        , [ 'module_root', 'build', 'Release', 'bindings' ]
          // Debug files, for development (legacy behavior, remove for node v0.9)
        , [ 'module_root', 'out', 'Debug', 'bindings' ]
        , [ 'module_root', 'Debug', 'bindings' ]
          // Release files, but manually compiled (legacy behavior, remove for node v0.9)
        , [ 'module_root', 'out', 'Release', 'bindings' ]
        , [ 'module_root', 'Release', 'bindings' ]
          // Legacy from node-waf, node <= 0.4.x
        , [ 'module_root', 'build', 'default', 'bindings' ]
          // Production "Release" buildtype binary (meh...)
        , [ 'module_root', 'compiled', 'version', 'platform', 'arch', 'bindings' ]
        ]
    }

/**
 * The main `bindings()` function loads the compiled bindings for a given module.
 * It uses V8's Error API to determine the parent filename that this function is
 * being invoked from, which is then used to find the root directory.
 */

function bindings (opts) {

  // Argument surgery
  if (typeof opts == 'string') {
    opts = { bindings: opts }
  } else if (!opts) {
    opts = {}
  }
  opts.__proto__ = defaults

  // Get the module root
  if (!opts.module_root) {
    opts.module_root = exports.getRoot(exports.getFileName())
  }

  // Ensure the given bindings name ends with .node
  if (path.extname(opts.bindings) != '.node') {
    opts.bindings += '.node'
  }

  var tries = []
    , i = 0
    , l = opts.try.length
    , n

  for (; i<l; i++) {
    n = join.apply(null, opts.try[i].map(function (p) {
      return opts[p] || p
    }))
    tries.push(n)
    try {
      var b = require(n)
      b.path = n
      return b
    } catch (e) {
      if (!/not find/i.test(e.message)) {
        throw e
      }
    }
  }

  var err = new Error('Could not load the bindings file. Tried:\n'
    + tries.map(function (a) { return opts.arrow + a }).join('\n'))
  err.tries = tries
  throw err
}
module.exports = exports = bindings

/**
 * Gets the filename of the JavaScript file that invokes this function.
 * Used to help find the root directory of a module.
 */

exports.getFileName = function getFileName () {
  var origPST = Error.prepareStackTrace
    , dummy = {}
    , fileName

  Error.prepareStackTrace = function (e, st) {
    for (var i=0, l=st.length; i<l; i++) {
      fileName = st[i].getFileName()
      if (fileName !== __filename) {
        return
      }
    }
  }

  // run the 'prepareStackTrace' function above
  Error.captureStackTrace(dummy)
  dummy.stack

  // cleanup
  Error.prepareStackTrace = origPST

  return fileName
}

/**
 * Gets the root directory of a module, given an arbitrary filename
 * somewhere in the module tree. The "root directory" is the directory
 * containing the `package.json` file.
 *
 *   In:  /home/nate/node-native-module/lib/index.js
 *   Out: /home/nate/node-native-module
 */

exports.getRoot = function getRoot (file) {
  var dir = dirname(file)
    , prev
  while (true) {
    if (dir === '.') {
      // Avoids an infinite loop in rare cases, like the REPL
      dir = process.cwd()
    }
    if (exists(join(dir, 'package.json')) || exists(join(dir, 'node_modules'))) {
      // Found the 'package.json' file or 'node_modules' dir; we're done
      return dir
    }
    if (prev === dir) {
      // Got to the top
      throw new Error('Could not find module root given file: "' + file
                    + '". Do you have a `package.json` file? ')
    }
    // Try the parent dir next
    prev = dir
    dir = join(dir, '..')
  }
}

/**
 * Accepts a String like "v0.10.4" and returns a String
 * containing the major and minor versions ("0.10").
 */

function parseVersion (str) {
  var m = String(str).match(/(\d+)\.(\d+)/)
  return m ? m[0] : null
}
exports.parseVersion = parseVersion
<commit_msg>make the default "compiled" dir be overridable<commit_after>
/**
 * Module dependencies.
 */

var fs = require('fs')
  , path = require('path')
  , join = path.join
  , dirname = path.dirname
  , exists = fs.existsSync || path.existsSync
  , defaults = {
        arrow: process.env.NODE_BINDINGS_ARROW || ' → '
      , compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled'
      , platform: process.platform
      , arch: process.arch
      , version: parseVersion(process.versions.node)
      , bindings: 'bindings.node'
      , try: [
          // node-gyp's linked version in the "build" dir
          [ 'module_root', 'build', 'bindings' ]
          // node-waf and gyp_addon (a.k.a node-gyp)
        , [ 'module_root', 'build', 'Debug', 'bindings' ]
        , [ 'module_root', 'build', 'Release', 'bindings' ]
          // Debug files, for development (legacy behavior, remove for node v0.9)
        , [ 'module_root', 'out', 'Debug', 'bindings' ]
        , [ 'module_root', 'Debug', 'bindings' ]
          // Release files, but manually compiled (legacy behavior, remove for node v0.9)
        , [ 'module_root', 'out', 'Release', 'bindings' ]
        , [ 'module_root', 'Release', 'bindings' ]
          // Legacy from node-waf, node <= 0.4.x
        , [ 'module_root', 'build', 'default', 'bindings' ]
          // Production "Release" buildtype binary (meh...)
        , [ 'module_root', 'compiled', 'version', 'platform', 'arch', 'bindings' ]
        ]
    }

/**
 * The main `bindings()` function loads the compiled bindings for a given module.
 * It uses V8's Error API to determine the parent filename that this function is
 * being invoked from, which is then used to find the root directory.
 */

function bindings (opts) {

  // Argument surgery
  if (typeof opts == 'string') {
    opts = { bindings: opts }
  } else if (!opts) {
    opts = {}
  }
  opts.__proto__ = defaults

  // Get the module root
  if (!opts.module_root) {
    opts.module_root = exports.getRoot(exports.getFileName())
  }

  // Ensure the given bindings name ends with .node
  if (path.extname(opts.bindings) != '.node') {
    opts.bindings += '.node'
  }

  var tries = []
    , i = 0
    , l = opts.try.length
    , n

  for (; i<l; i++) {
    n = join.apply(null, opts.try[i].map(function (p) {
      return opts[p] || p
    }))
    tries.push(n)
    try {
      var b = require(n)
      b.path = n
      return b
    } catch (e) {
      if (!/not find/i.test(e.message)) {
        throw e
      }
    }
  }

  var err = new Error('Could not load the bindings file. Tried:\n'
    + tries.map(function (a) { return opts.arrow + a }).join('\n'))
  err.tries = tries
  throw err
}
module.exports = exports = bindings

/**
 * Gets the filename of the JavaScript file that invokes this function.
 * Used to help find the root directory of a module.
 */

exports.getFileName = function getFileName () {
  var origPST = Error.prepareStackTrace
    , dummy = {}
    , fileName

  Error.prepareStackTrace = function (e, st) {
    for (var i=0, l=st.length; i<l; i++) {
      fileName = st[i].getFileName()
      if (fileName !== __filename) {
        return
      }
    }
  }

  // run the 'prepareStackTrace' function above
  Error.captureStackTrace(dummy)
  dummy.stack

  // cleanup
  Error.prepareStackTrace = origPST

  return fileName
}

/**
 * Gets the root directory of a module, given an arbitrary filename
 * somewhere in the module tree. The "root directory" is the directory
 * containing the `package.json` file.
 *
 *   In:  /home/nate/node-native-module/lib/index.js
 *   Out: /home/nate/node-native-module
 */

exports.getRoot = function getRoot (file) {
  var dir = dirname(file)
    , prev
  while (true) {
    if (dir === '.') {
      // Avoids an infinite loop in rare cases, like the REPL
      dir = process.cwd()
    }
    if (exists(join(dir, 'package.json')) || exists(join(dir, 'node_modules'))) {
      // Found the 'package.json' file or 'node_modules' dir; we're done
      return dir
    }
    if (prev === dir) {
      // Got to the top
      throw new Error('Could not find module root given file: "' + file
                    + '". Do you have a `package.json` file? ')
    }
    // Try the parent dir next
    prev = dir
    dir = join(dir, '..')
  }
}

/**
 * Accepts a String like "v0.10.4" and returns a String
 * containing the major and minor versions ("0.10").
 */

function parseVersion (str) {
  var m = String(str).match(/(\d+)\.(\d+)/)
  return m ? m[0] : null
}
exports.parseVersion = parseVersion
<|endoftext|>


<commit_before>(function(wysihtml5) {
  var undef,
      NODE_NAME = "A",
      dom       = wysihtml5.dom;

  function _removeFormat(element, anchors) {
    var length = anchors.length,
        i      = 0,
        anchor,
        codeElement,
        textContent;
    for (; i<length; i++) {
      anchor      = anchors[i];
      codeElement = dom.getParentElement(anchor, { nodeName: "code" });
      textContent = dom.getTextContent(anchor);

      // if <a> contains url-like text content, rename it to <code> to prevent re-autolinking
      // else replace <a> with its childNodes
      if (textContent.match(dom.autoLink.URL_REG_EXP) && !codeElement) {
        // <code> element is used to prevent later auto-linking of the content
        codeElement = dom.renameElement(anchor, "code");
      } else {
        dom.replaceWithChildNodes(anchor);
      }
    }
  }

  function _format(element, attributes) {
    var doc             = element.ownerDocument,
        tempClass       = "_wysihtml5-temp-" + (+new Date()),
        tempClassRegExp = /non-matching-class/g,
        i               = 0,
        length,
        anchors,
        anchor,
        hasElementChild,
        isEmpty,
        elementToSetCaretAfter,
        textContent,
        whiteSpace,
        j;
    wysihtml5.commands.formatInline.exec(element, undef, NODE_NAME, tempClass, tempClassRegExp);
    anchors = doc.querySelectorAll(NODE_NAME + "." + tempClass);
    length  = anchors.length;
    for (; i<length; i++) {
      anchor = anchors[i];
      anchor.removeAttribute("class");
      for (j in attributes) {
        anchor.setAttribute(j, attributes[j]);
      }
    }

    elementToSetCaretAfter = anchor;
    if (length === 1) {
      textContent = dom.getTextContent(anchor);
      hasElementChild = !!anchor.querySelector("*");
      isEmpty = textContent === "" || textContent === wysihtml5.INVISIBLE_SPACE;
      if (!hasElementChild && isEmpty) {
        dom.setTextContent(anchor, anchor.href);
        whiteSpace = doc.createTextNode(" ");
        wysihtml5.selection.setAfter(anchor);
        wysihtml5.selection.insertNode(whiteSpace);
        elementToSetCaretAfter = whiteSpace;
      }
    }
    wysihtml5.selection.setAfter(elementToSetCaretAfter);
  }

  wysihtml5.commands.createLink = {
    /**
     * TODO: Use HTMLApplier or formatInline here
     *
     * Turns selection into a link
     * If selection is already a link, it removes the link and wraps it with a <code> element
     * The <code> element is needed to avoid auto linking
     *
     * @example
     *    // either ...
     *    wysihtml5.commands.createLink.exec(element, "createLink", "http://www.google.de");
     *    // ... or ...
     *    wysihtml5.commands.createLink.exec(element, "createLink", { href: "http://www.google.de", target: "_blank" });
     */
    exec: function(element, command, value) {
      var doc           = element.ownerDocument,
          anchors       = this.state(element, command);

      if (anchors) {
        // Selection contains links
        wysihtml5.selection.executeAndRestore(function() {
          _removeFormat(element, anchors);
        });
      } else {
        // Create links
        value = typeof(value) === "object" ? value : { href: value };
        _format(element, value);
      }
    },

    state: function(element, command) {
      return wysihtml5.commands.formatInline.state(element, command, "A");
    },

    value: function() {
      return undef;
    }
  };
})(wysihtml5);<commit_msg>#42 Specify text of a hyper link when using the createLink command<commit_after>(function(wysihtml5) {
  var undef,
      NODE_NAME = "A",
      dom       = wysihtml5.dom;

  function _removeFormat(element, anchors) {
    var length = anchors.length,
        i      = 0,
        anchor,
        codeElement,
        textContent;
    for (; i<length; i++) {
      anchor      = anchors[i];
      codeElement = dom.getParentElement(anchor, { nodeName: "code" });
      textContent = dom.getTextContent(anchor);

      // if <a> contains url-like text content, rename it to <code> to prevent re-autolinking
      // else replace <a> with its childNodes
      if (textContent.match(dom.autoLink.URL_REG_EXP) && !codeElement) {
        // <code> element is used to prevent later auto-linking of the content
        codeElement = dom.renameElement(anchor, "code");
      } else {
        dom.replaceWithChildNodes(anchor);
      }
    }
  }

  function _format(element, attributes) {
    var doc             = element.ownerDocument,
        tempClass       = "_wysihtml5-temp-" + (+new Date()),
        tempClassRegExp = /non-matching-class/g,
        i               = 0,
        length,
        anchors,
        anchor,
        hasElementChild,
        isEmpty,
        elementToSetCaretAfter,
        textContent,
        whiteSpace,
        j;
    wysihtml5.commands.formatInline.exec(element, undef, NODE_NAME, tempClass, tempClassRegExp);
    anchors = doc.querySelectorAll(NODE_NAME + "." + tempClass);
    length  = anchors.length;
    for (; i<length; i++) {
      anchor = anchors[i];
      anchor.removeAttribute("class");
      for (j in attributes) {
        anchor.setAttribute(j, attributes[j]);
      }
    }

    elementToSetCaretAfter = anchor;
    if (length === 1) {
      textContent = dom.getTextContent(anchor);
      hasElementChild = !!anchor.querySelector("*");
      isEmpty = textContent === "" || textContent === wysihtml5.INVISIBLE_SPACE;
      if (!hasElementChild && isEmpty) {
        dom.setTextContent(anchor, attributes.text || anchor.href);
        whiteSpace = doc.createTextNode(" ");
        wysihtml5.selection.setAfter(anchor);
        wysihtml5.selection.insertNode(whiteSpace);
        elementToSetCaretAfter = whiteSpace;
      }
    }
    wysihtml5.selection.setAfter(elementToSetCaretAfter);
  }

  wysihtml5.commands.createLink = {
    /**
     * TODO: Use HTMLApplier or formatInline here
     *
     * Turns selection into a link
     * If selection is already a link, it removes the link and wraps it with a <code> element
     * The <code> element is needed to avoid auto linking
     *
     * @example
     *    // either ...
     *    wysihtml5.commands.createLink.exec(element, "createLink", "http://www.google.de");
     *    // ... or ...
     *    wysihtml5.commands.createLink.exec(element, "createLink", { href: "http://www.google.de", target: "_blank" });
     */
    exec: function(element, command, value) {
      var doc           = element.ownerDocument,
          anchors       = this.state(element, command);

      if (anchors) {
        // Selection contains links
        wysihtml5.selection.executeAndRestore(function() {
          _removeFormat(element, anchors);
        });
      } else {
        // Create links
        value = typeof(value) === "object" ? value : { href: value };
        _format(element, value);
      }
    },

    state: function(element, command) {
      return wysihtml5.commands.formatInline.state(element, command, "A");
    },

    value: function() {
      return undef;
    }
  };
})(wysihtml5);<|endoftext|>


<commit_before>// Generated by CoffeeScript 1.6.3
/*
jQuery.Turbolinks ~ https://github.com/kossnocorp/jquery.turbolinks
jQuery plugin for drop-in fix binded events problem caused by Turbolinks

The MIT License
Copyright (c) 2012-2013 Sasha Koss & Rico Sta. Cruz
*/

(function() {
  var $, $document;

  $ = window.jQuery || (typeof require === "function" ? require('jquery') : void 0);

  $document = $(document);

  $.turbo = {
    version: '2.0.0',
    isReady: false,
    use: function(load, fetch) {
      return $document.off('.turbo').on("" + load + ".turbo", this.onLoad).on("" + fetch + ".turbo", this.onFetch);
    },
    addCallback: function(callback) {
      if ($.turbo.isReady) {
        return callback($);
      } else {
        return $document.on('turbo:ready', function() {
          return callback($);
        });
      }
    },
    onLoad: function() {
      $.turbo.isReady = true;
      return $document.trigger('turbo:ready');
    },
    onFetch: function() {
      return $.turbo.isReady = false;
    },
    register: function() {
      $(this.onLoad);
      return $.fn.ready = this.addCallback;
    }
  };

  $.turbo.register();

  $.turbo.use('page:load', 'page:fetch');

}).call(this);
<commit_msg>Rebuild js.<commit_after>// Generated by CoffeeScript 1.7.1

/*
jQuery.Turbolinks ~ https://github.com/kossnocorp/jquery.turbolinks
jQuery plugin for drop-in fix binded events problem caused by Turbolinks

The MIT License
Copyright (c) 2012-2013 Sasha Koss & Rico Sta. Cruz
 */

(function() {
  var $, $document;

  $ = window.jQuery || (typeof require === "function" ? require('jquery') : void 0);

  $document = $(document);

  $.turbo = {
    version: '2.0.1',
    isReady: false,
    use: function(load, fetch) {
      return $document.off('.turbo').on("" + load + ".turbo", this.onLoad).on("" + fetch + ".turbo", this.onFetch);
    },
    addCallback: function(callback) {
      if ($.turbo.isReady) {
        return callback($);
      } else {
        return $document.on('turbo:ready', function() {
          return callback($);
        });
      }
    },
    onLoad: function() {
      $.turbo.isReady = true;
      return $document.trigger('turbo:ready');
    },
    onFetch: function() {
      return $.turbo.isReady = false;
    },
    register: function() {
      $(this.onLoad);
      return $.fn.ready = this.addCallback;
    }
  };

  $.turbo.register();

  $.turbo.use('page:load', 'page:fetch');

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


<commit_before>import Promise from 'bluebird';
import cheerio from 'cheerio';
import _request from 'request';
import R from 'ramda';

import sentry from '../../sentry';

const request = Promise.promisify(_request);

const help_text = `Get random web comic strips

**\`!comic random\`**
    Returns a random comic from any of the artists
**\`!comic asp\`** or **\`!comic amazing super powers\`**
    Returns a Amazing Super Powers comic
**\`!comic az\`** or **\`!comic awkward zombie\`**
    Returns a Awkward Zombie comic
**\`!comic cah\`** or **\`!comic cyanide and happiness\`**
    Returns a Cyanide and Happimess comic
**\`!comic css\`** or **\`!comic chainsawsuit\`**
    Returns a chainsawsuit comic
**\`!comic dhd\`** or **\`!comic dog house diaries\`**
    Returns a Dog House Diaries comic
**\`!comic smbc\`** or ** \`!comic saturday morning breakfast cereal\`**
    Returns a Saturday Morning Breakfast Cereal comic
**\`!comic to\`** or **\`!comic the oatmeal\`**
    Returns a The Oatmeal comic
**\`!comic xkcd\`**
    Returns a xkcd comic`;

// Cyanide and Happiness
function cah(bot, msg) {
  request('http://explosm.net/comics/random/')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#main-comic').attr('src'))
    .then(url => bot.sendMessage(msg.channel, `http:${url}`))
    .catch(err => {
      sentry(err, 'comics', 'cah');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// Saturday Morning Breakfast Cereal
function smbc(bot, msg) {
  request('http://www.smbc-comics.com/')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('.random').first().attr('href'))
    .then(url => request(`http://www.smbc-comics.com/${url}`))
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#comic').attr('src'))
    .then(url => bot.sendMessage(msg.channel, `http://www.smbc-comics.com/${url}`))
    .catch(err => {
      sentry(err, 'comics', 'smbc');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// Amazing Super Powers
function amazingSuperPowers(bot, msg) {
  request('http://www.amazingsuperpowers.com/?randomcomic&nocache=1')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#comic-1').find('img').first().attr('src'))
    .then(url => bot.sendMessage(msg.channel, url))
    .catch(err => {
      sentry(err, 'comics', 'amazingSuperPowers');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// Awkward Zombie
function awkwardZombie(bot, msg) {
  function _makeRequest(url) {
    let options = {
      url: `http://www.awkwardzombie.com/${url}`,
      headers: {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36'
      }
    };

    return request(options)
      .then(R.prop('body'))
      .then(cheerio.load);
  }

  _makeRequest('index.php?page=1')
    .then($ => {
      let table = $('#archive_table').find('tr');
      let rand = Math.floor(Math.random() * table.length);
      let url = table.eq(rand).find('a').first().attr('href');
      return _makeRequest(url);
    })
    .then($ => $('#comic').find('img').first().attr('src'))
    .then(url => bot.sendMessage(msg.channel, url))
    .catch(err => {
      sentry(err, 'comics', 'awkwardZombie');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// Chain Saw Suit
function chainsawsuit(bot, msg) {
  request('http://chainsawsuit.com/random/?random&nocache=1')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#comic').find('img').first().attr('src'))
    .then(url => bot.sendMessage(msg.channel, url))
    .catch(err => {
      sentry(err, 'comics', 'chainsawsuit');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// Dog House Diaries
function dogHouseDiaries(bot, msg) {
  request('http://thedoghousediaries.com/')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#randomlink').attr('href'))
    .then(url => request(url))
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#imgdiv').find('img').first().attr('src').replace('\n', ''))
    .then(url => bot.sendMessage(msg.channel, `http://thedoghousediaries.com/${url}`))
    .catch(err => {
      sentry(err, 'comics', 'dogHouseDiaries');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// The Oatmeal
function theOatmeal(bot, msg) {
  request('http://theoatmeal.com/feed/random')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#comic').find('img').first().attr('src'))
    .then(url => bot.sendMessage(msg.channel, url))
    .catch(err => {
      sentry(err, 'comics', 'theOatmeal');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// xkcd
function xkcd(bot, msg) {
  let options = {
    url: 'https://c.xkcd.com/random/comic/',
    rejectUnauthorized: false,
    headers: {
      'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36'
    }
  };

  request(options)
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#comic').find('img').first().attr('src'))
    .then(url => bot.sendMessage(msg.channel, `https:${url}`))
    .catch(err => {
      sentry(err, 'comics', 'xkcd');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

function randomComic(bot, msg) {
  // Add all commands here so a random one can be selected
  const commands = [
    cah,
    smbc,
    amazingSuperPowers,
    awkwardZombie,
    chainsawsuit,
    dogHouseDiaries,
    theOatmeal,
    xkcd
  ];
  const rand = Math.floor(Math.random() * commands.length);
  commands[rand](bot, msg);
}

function comics(bot, msg, suffix) {
  suffix = suffix.toLowerCase();
  if (suffix === 'random') return randomComic(bot, msg);

  // Cyanide and Happiness
  if (suffix === 'cah') return cah(bot, msg);
  if (suffix === 'cyanide') return cah(bot, msg);
  if (suffix === 'cyanide and happiness') return cah(bot, msg);

  // Saturday Morning Breakfast Cereal
  if (suffix === 'smbc') return smbc(bot, msg);
  if (suffix === 'saturday morning breakfast cereal') return smbc(bot, msg);

  // Amazing Super Powers
  if (suffix === 'asp') return amazingSuperPowers(bot, msg);
  if (suffix === 'amazing super powers') return amazingSuperPowers(bot, msg);

  // Awkward Zombie
  if (suffix === 'az') return awkwardZombie(bot, msg);
  if (suffix === 'awkward zombie') return awkwardZombie(bot, msg);

  // chainsawsuit
  if (suffix === 'css') return chainsawsuit(bot, msg);
  if (suffix === 'chainsawsuit') return chainsawsuit(bot, msg);
  if (suffix === 'chain saw suit') return chainsawsuit(bot, msg);

  // Dog House Diaries
  if (suffix === 'dhd') return dogHouseDiaries(bot, msg);
  if (suffix === 'doghousdiaries') return dogHouseDiaries(bot, msg);
  if (suffix === 'dog house diaries') return dogHouseDiaries(bot, msg);

  // The Oatmeal
  if (suffix === 'to') return theOatmeal(bot, msg);
  if (suffix.indexOf('oatmeal') > -1) return theOatmeal(bot, msg);

  // xkcd
  if (suffix === 'xkcd') return xkcd(bot, msg);

  return bot.sendMessage(msg.channel, help_text);
}

export default {
  comic: comics,
  comics
};
<commit_msg>temporarily disable awkwardZombie and dogHouseDiaries<commit_after>import Promise from 'bluebird';
import cheerio from 'cheerio';
import _request from 'request';
import R from 'ramda';

import sentry from '../../sentry';

const request = Promise.promisify(_request);

const help_text = `Get random web comic strips

**\`!comic random\`**
    Returns a random comic from any of the artists
**\`!comic asp\`** or **\`!comic amazing super powers\`**
    Returns a Amazing Super Powers comic
**\`!comic az\`** or **\`!comic awkward zombie\`**
    Returns a Awkward Zombie comic
**\`!comic cah\`** or **\`!comic cyanide and happiness\`**
    Returns a Cyanide and Happimess comic
**\`!comic css\`** or **\`!comic chainsawsuit\`**
    Returns a chainsawsuit comic
**\`!comic dhd\`** or **\`!comic dog house diaries\`**
    Returns a Dog House Diaries comic
**\`!comic smbc\`** or ** \`!comic saturday morning breakfast cereal\`**
    Returns a Saturday Morning Breakfast Cereal comic
**\`!comic to\`** or **\`!comic the oatmeal\`**
    Returns a The Oatmeal comic
**\`!comic xkcd\`**
    Returns a xkcd comic`;

// Cyanide and Happiness
function cah(bot, msg) {
  request('http://explosm.net/comics/random/')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#main-comic').attr('src'))
    .then(url => bot.sendMessage(msg.channel, `http:${url}`))
    .catch(err => {
      sentry(err, 'comics', 'cah');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// Saturday Morning Breakfast Cereal
function smbc(bot, msg) {
  request('http://www.smbc-comics.com/')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('.random').first().attr('href'))
    .then(url => request(`http://www.smbc-comics.com/${url}`))
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#comic').attr('src'))
    .then(url => bot.sendMessage(msg.channel, `http://www.smbc-comics.com/${url}`))
    .catch(err => {
      sentry(err, 'comics', 'smbc');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// Amazing Super Powers
function amazingSuperPowers(bot, msg) {
  request('http://www.amazingsuperpowers.com/?randomcomic&nocache=1')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#comic-1').find('img').first().attr('src'))
    .then(url => bot.sendMessage(msg.channel, url))
    .catch(err => {
      sentry(err, 'comics', 'amazingSuperPowers');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// Awkward Zombie
function awkwardZombie(bot, msg) {
  function _makeRequest(url) {
    let options = {
      url: `http://www.awkwardzombie.com/${url}`,
      headers: {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36'
      }
    };

    return request(options)
      .then(R.prop('body'))
      .then(cheerio.load);
  }

  _makeRequest('index.php?page=1')
    .then($ => {
      let table = $('#archive_table').find('tr');
      let rand = Math.floor(Math.random() * table.length);
      let url = table.eq(rand).find('a').first().attr('href');
      return _makeRequest(url);
    })
    .then($ => $('#comic').find('img').first().attr('src'))
    .then(url => bot.sendMessage(msg.channel, url))
    .catch(err => {
      sentry(err, 'comics', 'awkwardZombie');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// Chain Saw Suit
function chainsawsuit(bot, msg) {
  request('http://chainsawsuit.com/random/?random&nocache=1')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#comic').find('img').first().attr('src'))
    .then(url => bot.sendMessage(msg.channel, url))
    .catch(err => {
      sentry(err, 'comics', 'chainsawsuit');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// Dog House Diaries
function dogHouseDiaries(bot, msg) {
  request('http://thedoghousediaries.com/')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#randomlink').attr('href'))
    .then(url => request(url))
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#imgdiv').find('img').first().attr('src').replace('\n', ''))
    .then(url => bot.sendMessage(msg.channel, `http://thedoghousediaries.com/${url}`))
    .catch(err => {
      sentry(err, 'comics', 'dogHouseDiaries');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// The Oatmeal
function theOatmeal(bot, msg) {
  request('http://theoatmeal.com/feed/random')
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#comic').find('img').first().attr('src'))
    .then(url => bot.sendMessage(msg.channel, url))
    .catch(err => {
      sentry(err, 'comics', 'theOatmeal');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

// xkcd
function xkcd(bot, msg) {
  let options = {
    url: 'https://c.xkcd.com/random/comic/',
    rejectUnauthorized: false,
    headers: {
      'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36'
    }
  };

  request(options)
    .then(R.prop('body'))
    .then(cheerio.load)
    .then($ => $('#comic').find('img').first().attr('src'))
    .then(url => bot.sendMessage(msg.channel, `https:${url}`))
    .catch(err => {
      sentry(err, 'comics', 'xkcd');
      bot.sendMessage(msg.channel, `Error: ${err.message}`);
    });
}

function randomComic(bot, msg) {
  // Add all commands here so a random one can be selected
  const commands = [
    cah,
    smbc,
    amazingSuperPowers,
    // awkwardZombie,
    chainsawsuit,
    // dogHouseDiaries,
    theOatmeal,
    xkcd
  ];
  const rand = Math.floor(Math.random() * commands.length);
  commands[rand](bot, msg);
}

function comics(bot, msg, suffix) {
  suffix = suffix.toLowerCase();
  if (suffix === 'random') return randomComic(bot, msg);

  // Cyanide and Happiness
  if (suffix === 'cah') return cah(bot, msg);
  if (suffix === 'cyanide') return cah(bot, msg);
  if (suffix === 'cyanide and happiness') return cah(bot, msg);

  // Saturday Morning Breakfast Cereal
  if (suffix === 'smbc') return smbc(bot, msg);
  if (suffix === 'saturday morning breakfast cereal') return smbc(bot, msg);

  // Amazing Super Powers
  if (suffix === 'asp') return amazingSuperPowers(bot, msg);
  if (suffix === 'amazing super powers') return amazingSuperPowers(bot, msg);

  // Awkward Zombie
  if (suffix === 'az') return awkwardZombie(bot, msg);
  if (suffix === 'awkward zombie') return awkwardZombie(bot, msg);

  // chainsawsuit
  if (suffix === 'css') return chainsawsuit(bot, msg);
  if (suffix === 'chainsawsuit') return chainsawsuit(bot, msg);
  if (suffix === 'chain saw suit') return chainsawsuit(bot, msg);

  // Dog House Diaries
  if (suffix === 'dhd') return dogHouseDiaries(bot, msg);
  if (suffix === 'doghousdiaries') return dogHouseDiaries(bot, msg);
  if (suffix === 'dog house diaries') return dogHouseDiaries(bot, msg);

  // The Oatmeal
  if (suffix === 'to') return theOatmeal(bot, msg);
  if (suffix.indexOf('oatmeal') > -1) return theOatmeal(bot, msg);

  // xkcd
  if (suffix === 'xkcd') return xkcd(bot, msg);

  return bot.sendMessage(msg.channel, help_text);
}

export default {
  comic: comics,
  comics
};
<|endoftext|>


<commit_before>// ===================================================================================================
//                           _  __     _ _
//                          | |/ /__ _| | |_ _  _ _ _ __ _
//                          | ' </ _` | |  _| || | '_/ _` |
//                          |_|\_\__,_|_|\__|\_,_|_| \__,_|
//
// This file is part of the Kaltura Collaborative Media Suite which allows users
// to do with audio, video, and animation what Wiki platfroms allow them to do with
// text.
//
// Copyright (C) 2006-2021  Kaltura Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// @ignore
// ===================================================================================================
/**
 * The Kaltura Client - this is the facade through which all service actions should be called.
 * @param config the Kaltura configuration object holding partner credentials (type: KalturaConfiguration).
 */
var util = require('util');
var kaltura = require('./KalturaClientBase');
kaltura.services = require('./KalturaServices');
kaltura.objects = require('./KalturaModel');
kaltura.enums = require('./KalturaTypes');

class Client extends kaltura.ClientBase {

	/**
	 * @param Configuration config
	 */
	constructor(config) {
		super(config);
		this.setApiVersion('16.19.0');
		this.setClientTag('node:21-03-28');
	}
}

module.exports = kaltura;
module.exports.Client = Client;

<commit_msg>New clientlib ver<commit_after>// ===================================================================================================
//                           _  __     _ _
//                          | |/ /__ _| | |_ _  _ _ _ __ _
//                          | ' </ _` | |  _| || | '_/ _` |
//                          |_|\_\__,_|_|\__|\_,_|_| \__,_|
//
// This file is part of the Kaltura Collaborative Media Suite which allows users
// to do with audio, video, and animation what Wiki platfroms allow them to do with
// text.
//
// Copyright (C) 2006-2021  Kaltura Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// @ignore
// ===================================================================================================
/**
 * The Kaltura Client - this is the facade through which all service actions should be called.
 * @param config the Kaltura configuration object holding partner credentials (type: KalturaConfiguration).
 */
var util = require('util');
var kaltura = require('./KalturaClientBase');
kaltura.services = require('./KalturaServices');
kaltura.objects = require('./KalturaModel');
kaltura.enums = require('./KalturaTypes');

class Client extends kaltura.ClientBase {

	/**
	 * @param Configuration config
	 */
	constructor(config) {
		super(config);
		this.setApiVersion('16.19.0');
		this.setClientTag('node:21-03-29');
	}
}

module.exports = kaltura;
module.exports.Client = Client;

<|endoftext|>


<commit_before>'use strict';

(function(app) {

  /**
   *
   * @param {type} $scope
   * @param {type} $locale
   * @param {type} $timeout
   * @param {type} $location
   * @returns {VideoController_L3.VideoController}
   */
  function VideoController($scope,
                           $http,
                           $locale,
                           $timeout,
                           $location,
                           $analytics,
                           videoService,
                           searchService,
                           $sce,
                           webServiceBasePath) {
    $scope.defaultMode = 'both';
    if ($scope.video.metadata) {
      var template = $scope.video.metadata.template || '';
      if (template.match(/^mix-/))
        $scope.defaultMode = 'media';
    }
    if ($scope.video.category) {
      searchService.getCategoryName($scope.video.category).then(function(val) {
        $scope.categoryName = val;
      });
    }

    $scope.dialIsOpen = false;
    $scope.language = $locale.id;
    $scope.shareOpen = false;
    $scope.shareItems = [
      {name: 'UI.LINK', icon: 'link', direction: 'bottom', action: function(video) {
        delete $scope.shareText;
        var port = ($location.port() != 80 && $location.port() != 443) ? ':' + $location.port() : '';
        $scope.shareLink = $location.protocol() + '://' + $location.host() + port + '/video/' + video.id;
        $scope.shareOpen = !$scope.shareOpen;
      }},
      {name: 'UI.CODE',
        icon: 'code',
        direction: 'bottom',
        action: function(video) {
          delete $scope.shareLink;
          var port = ($location.port() != 80 && $location.port() != 443) ? ':' + $location.port() : '';
          $scope.shareText =
                  '';

          $scope.shareOpen = !$scope.shareOpen;
        }}
    ];
    $scope.showFullInfos = false;
    $scope.toggleFullInfosLabel = 'UI.MORE';

    $scope.toggleFullInfos = function() {
      $scope.showFullInfos = !$scope.showFullInfos;
      $scope.toggleFullInfosLabel = $scope.showFullInfos ? 'UI.LESS' : 'UI.MORE';
    };

    var myPlayer = document.getElementById('openveo-player');
    var playerController;
    var videoDuration;

    angular.element(myPlayer).on('needPoiConversion', function(event, duration) {
      $http
        .post(webServiceBasePath + 'videos/' + $scope.video.id + '/poi/convert', {duration: duration})
        .then(function(response) {
          $scope.video = response.data.entity;
        });
    });

    /**
     * Executes, safely, the given function in AngularJS process.
     *
     * @param {Function} functionToExecute The function to execute as part of
     * the angular digest process.
     */
    function safeApply(functionToExecute) {

      // Execute each apply on a different loop
      $timeout(function() {

        // Make sure we're not on a digestion cycle
        var phase = $scope.$root.$$phase;

        if (phase === '$apply' || phase === '$digest')
          functionToExecute();
        else
          $scope.$apply(functionToExecute);
      }, 1);
    }

    angular.element(myPlayer).on('durationChange', function(event, duration) {
      safeApply(function() {
        videoDuration = duration;

        // only gets called once
        if (!playerController || duration) {
          playerController = angular.element(myPlayer).controller('ovPlayer');
        }
      });
    });

    angular.element(myPlayer).on('play', function(event) {
      $analytics.pageTrack($location.path());
      $analytics.eventTrack('play', {value: $scope.video.id});
      videoService.increaseVideoView($scope.video.id, videoDuration);
    });

    // Listen to player errors
    // If an error occurs go back to catalog with an alert
    angular.element(myPlayer).on('error', function(event, error) {
      $scope.$emit('setAlert', 'danger', error && error.message, 8000);
    });

    $scope.$watch('vctl.dialIsOpen', function(newValue, oldValue) {
      if (newValue === true) {
        $scope.shareOpen = false;
      }
    });

    $scope.trustedHTML = function(string) {
      return $sce.trustAsHtml(string);
    };
  }

  app.controller('VideoController', VideoController);
  VideoController.$inject = [
    '$scope',
    '$http',
    '$locale',
    '$timeout',
    '$location',
    '$analytics',
    'videoService',
    'searchService',
    '$sce',
    'webServiceBasePath'
  ];

})(angular.module('ov.portal'));
<commit_msg>Remove unused code<commit_after>'use strict';

(function(app) {

  /**
   *
   * @param {type} $scope
   * @param {type} $locale
   * @param {type} $timeout
   * @param {type} $location
   * @returns {VideoController_L3.VideoController}
   */
  function VideoController($scope,
                           $http,
                           $locale,
                           $timeout,
                           $location,
                           $analytics,
                           videoService,
                           searchService,
                           $sce,
                           webServiceBasePath) {
    $scope.defaultMode = 'both';
    if ($scope.video.metadata) {
      var template = $scope.video.metadata.template || '';
      if (template.match(/^mix-/))
        $scope.defaultMode = 'media';
    }
    if ($scope.video.category) {
      searchService.getCategoryName($scope.video.category).then(function(val) {
        $scope.categoryName = val;
      });
    }

    $scope.dialIsOpen = false;
    $scope.language = $locale.id;
    $scope.shareOpen = false;
    $scope.shareItems = [
      {name: 'UI.LINK', icon: 'link', direction: 'bottom', action: function(video) {
        delete $scope.shareText;
        var port = ($location.port() != 80 && $location.port() != 443) ? ':' + $location.port() : '';
        $scope.shareLink = $location.protocol() + '://' + $location.host() + port + '/video/' + video.id;
        $scope.shareOpen = !$scope.shareOpen;
      }},
      {name: 'UI.CODE',
        icon: 'code',
        direction: 'bottom',
        action: function(video) {
          delete $scope.shareLink;
          var port = ($location.port() != 80 && $location.port() != 443) ? ':' + $location.port() : '';
          $scope.shareText =
                  '';

          $scope.shareOpen = !$scope.shareOpen;
        }}
    ];
    $scope.showFullInfos = false;
    $scope.toggleFullInfosLabel = 'UI.MORE';

    $scope.toggleFullInfos = function() {
      $scope.showFullInfos = !$scope.showFullInfos;
      $scope.toggleFullInfosLabel = $scope.showFullInfos ? 'UI.LESS' : 'UI.MORE';
    };

    var myPlayer = document.getElementById('openveo-player');
    var videoDuration;

    angular.element(myPlayer).on('needPoiConversion', function(event, duration) {
      $http
        .post(webServiceBasePath + 'videos/' + $scope.video.id + '/poi/convert', {duration: duration})
        .then(function(response) {
          $scope.video = response.data.entity;
        });
    });

    /**
     * Executes, safely, the given function in AngularJS process.
     *
     * @param {Function} functionToExecute The function to execute as part of
     * the angular digest process.
     */
    function safeApply(functionToExecute) {

      // Execute each apply on a different loop
      $timeout(function() {

        // Make sure we're not on a digestion cycle
        var phase = $scope.$root.$$phase;

        if (phase === '$apply' || phase === '$digest')
          functionToExecute();
        else
          $scope.$apply(functionToExecute);
      }, 1);
    }

    angular.element(myPlayer).on('durationChange', function(event, duration) {
      safeApply(function() {
        videoDuration = duration;
      });
    });

    angular.element(myPlayer).on('play', function(event) {
      $analytics.pageTrack($location.path());
      $analytics.eventTrack('play', {value: $scope.video.id});
      videoService.increaseVideoView($scope.video.id, videoDuration);
    });

    // Listen to player errors
    // If an error occurs go back to catalog with an alert
    angular.element(myPlayer).on('error', function(event, error) {
      $scope.$emit('setAlert', 'danger', error && error.message, 8000);
    });

    $scope.$watch('vctl.dialIsOpen', function(newValue, oldValue) {
      if (newValue === true) {
        $scope.shareOpen = false;
      }
    });

    $scope.trustedHTML = function(string) {
      return $sce.trustAsHtml(string);
    };
  }

  app.controller('VideoController', VideoController);
  VideoController.$inject = [
    '$scope',
    '$http',
    '$locale',
    '$timeout',
    '$location',
    '$analytics',
    'videoService',
    'searchService',
    '$sce',
    'webServiceBasePath'
  ];

})(angular.module('ov.portal'));
<|endoftext|>


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

/**
 * External dependencies
 */
import styled, { css } from 'styled-components';
import PropTypes from 'prop-types';
import { forwardRef, createRef } from 'react';

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import {
  FULLBLEED_RATIO,
  PAGE_RATIO,
  ALLOWED_EDITOR_PAGE_WIDTHS,
  HEADER_HEIGHT,
  PAGE_NAV_WIDTH,
} from '../../constants';
import pointerEventsCss from '../../utils/pointerEventsCss';
import useResizeEffect from '../../utils/useResizeEffect';
import generatePatternStyles from '../../utils/generatePatternStyles';
import useCanvas from './useCanvas';

/**
 * @file See https://user-images.githubusercontent.com/726049/72654503-bfffe780-3944-11ea-912c-fc54d68b6100.png
 * for the layering details.
 */

export const Z_INDEX = {
  NAV: 2,
  EDIT: 3,
};

const MENU_HEIGHT = 48;

export const CAROUSEL_VERTICAL_PADDING = 24;
export const COMPACT_CAROUSEL_VERTICAL_PADDING = 32;

export const COMPACT_THUMB_WIDTH = 72;
export const COMPACT_THUMB_HEIGHT = 8;

const MAX_CAROUSEL_THUMB_HEIGHT = 128;
export const MIN_CAROUSEL_THUMB_HEIGHT = 52;

const MIN_CAROUSEL_HEIGHT =
  COMPACT_CAROUSEL_VERTICAL_PADDING * 2 + COMPACT_THUMB_HEIGHT;
const MAX_CAROUSEL_HEIGHT =
  MAX_CAROUSEL_THUMB_HEIGHT + CAROUSEL_VERTICAL_PADDING * 2;

// @todo: the menu height is not responsive
const Layer = styled.section`
  ${pointerEventsCss}

  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;

  ${({ zIndex }) => (typeof zIndex === 'number' ? `z-index: ${zIndex};` : '')}

  display: grid;
  grid:
    'head      head      head      head      head    ' ${HEADER_HEIGHT}px
    '.         .         .         .         .       ' minmax(16px, 1fr)
    '.         prev      page      next      .       ' var(
      --fullbleed-height-px
    )
    '.         .         menu      .         .       ' ${MENU_HEIGHT}px
    '.         .         .         .         .       ' 1fr
    'carousel  carousel  carousel  carousel  carousel' minmax(
      ${MIN_CAROUSEL_HEIGHT}px,
      ${MAX_CAROUSEL_HEIGHT}px
    )
    / 1fr ${PAGE_NAV_WIDTH}px var(--fullbleed-width-px)
    ${PAGE_NAV_WIDTH}px 1fr;
  height: 100%;
`;

const Area = styled.div`
  ${pointerEventsCss}

  grid-area: ${({ area }) => area};
  overflow: ${({ overflowAllowed }) =>
    overflowAllowed ? 'visible' : 'hidden'};
  position: relative;
  width: 100%;
  height: 100%;
  ${({ zIndex }) => (zIndex !== undefined ? `z-index: ${zIndex}` : null)};
`;

// Page area is not `overflow:hidden` by default to allow different clipping
// mechanisms.
const PageAreaFullbleedContainer = styled(Area).attrs({
  area: 'page',
  overflowAllowed: true,
})`
  display: flex;
  justify-content: center;
  align-items: center;
`;

// Overflow is not hidden for media edit layer.
const PageAreaWithOverflow = styled.div`
  ${({ background }) => generatePatternStyles(background)}
  overflow: ${({ showOverflow }) => (showOverflow ? 'initial' : 'hidden')};
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
`;

const PageAreaSafeZone = styled.div`
  width: 100%;
  height: var(--page-height-px);
  overflow: visible;
  position: relative;
  margin: auto 0;

  ${({ showSafeZone }) =>
    showSafeZone &&
    css`
      &::before,
      &::after {
        content: '';
        width: 12px;
        height: var(--page-height-px);
        position: absolute;
        border-top: 1px solid rgba(255, 255, 255, 0.4);
        border-bottom: 1px solid rgba(255, 255, 255, 0.4);
      }
      &::before {
        left: -12px;
      }
      &::after {
        right: -12px;
      }
    `}
`;

const HeadArea = styled(Area).attrs({ area: 'head', overflowAllowed: false })``;

const MenuArea = styled(Area).attrs({ area: 'menu', overflowAllowed: false })``;

const NavArea = styled(Area).attrs({ overflowAllowed: false })`
  display: flex;
  align-items: center;
  justify-content: center;
`;

const NavPrevArea = styled(NavArea).attrs({ area: 'prev' })``;

const NavNextArea = styled(NavArea).attrs({ area: 'next' })``;

const CarouselArea = styled(Area).attrs({
  area: 'carousel',
  overflowAllowed: true,
})``;

/**
 * @param {!{current: ?Element}} containerRef Container reference.
 */
function useLayoutParams(containerRef) {
  const { setPageSize } = useCanvas((state) => ({
    setPageSize: state.actions.setPageSize,
  }));

  useResizeEffect(containerRef, ({ width, height }) => {
    // See Layer's `grid` CSS above. Per the layout, the maximum available
    // space for the page is:
    const maxWidth = width - PAGE_NAV_WIDTH * 2;
    const maxHeight =
      height - HEADER_HEIGHT - MENU_HEIGHT - MIN_CAROUSEL_HEIGHT;

    let bestSize =
      ALLOWED_EDITOR_PAGE_WIDTHS.find(
        (size) => size <= maxWidth && size / FULLBLEED_RATIO <= maxHeight
      ) || ALLOWED_EDITOR_PAGE_WIDTHS[ALLOWED_EDITOR_PAGE_WIDTHS.length - 1];
    setPageSize({ width: bestSize, height: bestSize / PAGE_RATIO });
  });
}

function useLayoutParamsCssVars() {
  const { pageSize } = useCanvas((state) => ({
    pageSize: state.state.pageSize,
  }));
  return {
    '--page-width-px': `${pageSize.width}px`,
    '--page-height-px': `${pageSize.height}px`,
    '--fullbleed-width-px': `${pageSize.width}px`,
    '--fullbleed-height-px': `${pageSize.width / FULLBLEED_RATIO}px`,
  };
}

const PageArea = forwardRef(function PageArea(
  {
    children,
    showSafeZone = false,
    showOverflow = false,
    fullbleedRef = createRef(),
    overlay = [],
    background,
  },
  ref
) {
  return (
    <PageAreaFullbleedContainer
      ref={fullbleedRef}
      data-testid="fullbleed"
      aria-label={__('Fullbleed area', 'web-stories')}
      role="region"
    >
      <PageAreaWithOverflow showOverflow={showOverflow} background={background}>
        <PageAreaSafeZone
          ref={ref}
          data-testid="safezone"
          showSafeZone={showSafeZone}
        >
          {children}
        </PageAreaSafeZone>
      </PageAreaWithOverflow>
      {overlay}
    </PageAreaFullbleedContainer>
  );
});

PageArea.propTypes = {
  children: PropTypes.node,
  showSafeZone: PropTypes.bool,
  showOverflow: PropTypes.bool,
  fullbleedRef: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.shape({ current: PropTypes.elementType }),
  ]),
  overlay: PropTypes.node,
  background: PropTypes.object,
};

export {
  Layer,
  PageArea,
  HeadArea,
  MenuArea,
  NavPrevArea,
  NavNextArea,
  CarouselArea,
  useLayoutParams,
  useLayoutParamsCssVars,
};
<commit_msg>Update fullbleedRef type to be less verbose<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import styled, { css } from 'styled-components';
import PropTypes from 'prop-types';
import { forwardRef, createRef } from 'react';

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import {
  FULLBLEED_RATIO,
  PAGE_RATIO,
  ALLOWED_EDITOR_PAGE_WIDTHS,
  HEADER_HEIGHT,
  PAGE_NAV_WIDTH,
} from '../../constants';
import pointerEventsCss from '../../utils/pointerEventsCss';
import useResizeEffect from '../../utils/useResizeEffect';
import generatePatternStyles from '../../utils/generatePatternStyles';
import useCanvas from './useCanvas';

/**
 * @file See https://user-images.githubusercontent.com/726049/72654503-bfffe780-3944-11ea-912c-fc54d68b6100.png
 * for the layering details.
 */

export const Z_INDEX = {
  NAV: 2,
  EDIT: 3,
};

const MENU_HEIGHT = 48;

export const CAROUSEL_VERTICAL_PADDING = 24;
export const COMPACT_CAROUSEL_VERTICAL_PADDING = 32;

export const COMPACT_THUMB_WIDTH = 72;
export const COMPACT_THUMB_HEIGHT = 8;

const MAX_CAROUSEL_THUMB_HEIGHT = 128;
export const MIN_CAROUSEL_THUMB_HEIGHT = 52;

const MIN_CAROUSEL_HEIGHT =
  COMPACT_CAROUSEL_VERTICAL_PADDING * 2 + COMPACT_THUMB_HEIGHT;
const MAX_CAROUSEL_HEIGHT =
  MAX_CAROUSEL_THUMB_HEIGHT + CAROUSEL_VERTICAL_PADDING * 2;

// @todo: the menu height is not responsive
const Layer = styled.section`
  ${pointerEventsCss}

  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;

  ${({ zIndex }) => (typeof zIndex === 'number' ? `z-index: ${zIndex};` : '')}

  display: grid;
  grid:
    'head      head      head      head      head    ' ${HEADER_HEIGHT}px
    '.         .         .         .         .       ' minmax(16px, 1fr)
    '.         prev      page      next      .       ' var(
      --fullbleed-height-px
    )
    '.         .         menu      .         .       ' ${MENU_HEIGHT}px
    '.         .         .         .         .       ' 1fr
    'carousel  carousel  carousel  carousel  carousel' minmax(
      ${MIN_CAROUSEL_HEIGHT}px,
      ${MAX_CAROUSEL_HEIGHT}px
    )
    / 1fr ${PAGE_NAV_WIDTH}px var(--fullbleed-width-px)
    ${PAGE_NAV_WIDTH}px 1fr;
  height: 100%;
`;

const Area = styled.div`
  ${pointerEventsCss}

  grid-area: ${({ area }) => area};
  overflow: ${({ overflowAllowed }) =>
    overflowAllowed ? 'visible' : 'hidden'};
  position: relative;
  width: 100%;
  height: 100%;
  ${({ zIndex }) => (zIndex !== undefined ? `z-index: ${zIndex}` : null)};
`;

// Page area is not `overflow:hidden` by default to allow different clipping
// mechanisms.
const PageAreaFullbleedContainer = styled(Area).attrs({
  area: 'page',
  overflowAllowed: true,
})`
  display: flex;
  justify-content: center;
  align-items: center;
`;

// Overflow is not hidden for media edit layer.
const PageAreaWithOverflow = styled.div`
  ${({ background }) => generatePatternStyles(background)}
  overflow: ${({ showOverflow }) => (showOverflow ? 'initial' : 'hidden')};
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
`;

const PageAreaSafeZone = styled.div`
  width: 100%;
  height: var(--page-height-px);
  overflow: visible;
  position: relative;
  margin: auto 0;

  ${({ showSafeZone }) =>
    showSafeZone &&
    css`
      &::before,
      &::after {
        content: '';
        width: 12px;
        height: var(--page-height-px);
        position: absolute;
        border-top: 1px solid rgba(255, 255, 255, 0.4);
        border-bottom: 1px solid rgba(255, 255, 255, 0.4);
      }
      &::before {
        left: -12px;
      }
      &::after {
        right: -12px;
      }
    `}
`;

const HeadArea = styled(Area).attrs({ area: 'head', overflowAllowed: false })``;

const MenuArea = styled(Area).attrs({ area: 'menu', overflowAllowed: false })``;

const NavArea = styled(Area).attrs({ overflowAllowed: false })`
  display: flex;
  align-items: center;
  justify-content: center;
`;

const NavPrevArea = styled(NavArea).attrs({ area: 'prev' })``;

const NavNextArea = styled(NavArea).attrs({ area: 'next' })``;

const CarouselArea = styled(Area).attrs({
  area: 'carousel',
  overflowAllowed: true,
})``;

/**
 * @param {!{current: ?Element}} containerRef Container reference.
 */
function useLayoutParams(containerRef) {
  const { setPageSize } = useCanvas((state) => ({
    setPageSize: state.actions.setPageSize,
  }));

  useResizeEffect(containerRef, ({ width, height }) => {
    // See Layer's `grid` CSS above. Per the layout, the maximum available
    // space for the page is:
    const maxWidth = width - PAGE_NAV_WIDTH * 2;
    const maxHeight =
      height - HEADER_HEIGHT - MENU_HEIGHT - MIN_CAROUSEL_HEIGHT;

    let bestSize =
      ALLOWED_EDITOR_PAGE_WIDTHS.find(
        (size) => size <= maxWidth && size / FULLBLEED_RATIO <= maxHeight
      ) || ALLOWED_EDITOR_PAGE_WIDTHS[ALLOWED_EDITOR_PAGE_WIDTHS.length - 1];
    setPageSize({ width: bestSize, height: bestSize / PAGE_RATIO });
  });
}

function useLayoutParamsCssVars() {
  const { pageSize } = useCanvas((state) => ({
    pageSize: state.state.pageSize,
  }));
  return {
    '--page-width-px': `${pageSize.width}px`,
    '--page-height-px': `${pageSize.height}px`,
    '--fullbleed-width-px': `${pageSize.width}px`,
    '--fullbleed-height-px': `${pageSize.width / FULLBLEED_RATIO}px`,
  };
}

const PageArea = forwardRef(function PageArea(
  {
    children,
    showSafeZone = false,
    showOverflow = false,
    fullbleedRef = createRef(),
    overlay = [],
    background,
  },
  ref
) {
  return (
    <PageAreaFullbleedContainer
      ref={fullbleedRef}
      data-testid="fullbleed"
      aria-label={__('Fullbleed area', 'web-stories')}
      role="region"
    >
      <PageAreaWithOverflow showOverflow={showOverflow} background={background}>
        <PageAreaSafeZone
          ref={ref}
          data-testid="safezone"
          showSafeZone={showSafeZone}
        >
          {children}
        </PageAreaSafeZone>
      </PageAreaWithOverflow>
      {overlay}
    </PageAreaFullbleedContainer>
  );
});

PageArea.propTypes = {
  children: PropTypes.node,
  showSafeZone: PropTypes.bool,
  showOverflow: PropTypes.bool,
  fullbleedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
  overlay: PropTypes.node,
  background: PropTypes.object,
};

export {
  Layer,
  PageArea,
  HeadArea,
  MenuArea,
  NavPrevArea,
  NavNextArea,
  CarouselArea,
  useLayoutParams,
  useLayoutParamsCssVars,
};
<|endoftext|>


<commit_before>/**
*
* CollapsingSection
*
*/

import React from 'react';
import styled from 'styled-components';

import ArrowIcon from 'assets/images/icons/arrow.svg';
import Isvg from 'react-inlinesvg';

const Container = styled.div`
  border-bottom: 2px solid black;
`;

const HeaderContainer = styled.div`
  padding: 4px 0 3px;
`;
const Header = styled.div`
  display: inline-block;
  padding-right: 20px;
`;
const ArrowContainer = styled.span`
  display: inline-block;
  transform: ${p=>!p.showing ? 'rotate(180deg)' : 'rotate(270deg)'};
  transition: transform 0.5s ease;
  position: absolute;
  right: 20px;
  top: 10px;
  svg {
    width: 12px !important;
  }
`;
const Content = styled.div`
  max-height: ${p=>p.show?'100vh':'0'};
  overflow: hidden;
  transition: max-height 0.7s ease;
`;

const ActionButton = styled.button`
  outline: none;
  padding: 0;
  cursor: pointer;
  width: 100%;
  padding-right: 20px;
  position: relative;
`;

class CollapsingSection extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
  constructor() {
    super();
    this.state = {
      showContent: false
    }
  }

  toggleContent() {
    this.setState({showContent: !this.state.showContent} );
  }

  render() {
    return (
      <Container>
        <HeaderContainer>
          <ActionButton onClick={this.toggleContent.bind(this)}>
            { this.props.header }
            <ArrowContainer showing={this.state.showContent}>
              <Isvg src={ArrowIcon} />
            </ArrowContainer>
          </ActionButton>
        </HeaderContainer>
        <Content show={this.state.showContent}>
          {React.Children.toArray(this.props.children)}
        </Content>
      </Container>
    );
  }
}

CollapsingSection.propTypes = {
  // header: React.PropTypes.object.required
};

export default CollapsingSection;
<commit_msg>Fixed When activated arrow should point downwards and turn black. Also horizontal line should remain under the words -- //3.basecamp.com/3161453/buckets/3412210/todos/610469026<commit_after>/**
*
* CollapsingSection
*
*/

import React from 'react';
import styled from 'styled-components';

import ArrowIcon from 'assets/images/icons/arrow.svg';
import Isvg from 'react-inlinesvg';

const Container = styled.div`

`;

const HeaderContainer = styled.div`
  padding: 4px 0 3px;
  border-bottom: 2px solid black;
`;
const Header = styled.div`
  display: inline-block;
  padding-right: 20px;
`;
const ArrowContainer = styled.span`
  display: inline-block;
  transform: ${p=>!p.showing ? 'rotate(180deg)' : 'rotate(270deg)'};
  transition: transform 0.5s ease;
  position: absolute;
  right: 20px;
  top: 10px;
  svg {
    width: 12px !important;
    * {
      fill: ${p=>!p.showing ? '#afafaf' : '#000000'};
      transition: fill 0.5s ease;
    }
  }
`;
const Content = styled.div`
  max-height: ${p=>p.show?'100vh':'0'};
  overflow: hidden;
  transition: max-height 0.7s ease;
`;

const ActionButton = styled.button`
  outline: none;
  padding: 0;
  cursor: pointer;
  width: 100%;
  padding-right: 20px;
  position: relative;
`;

class CollapsingSection extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
  constructor() {
    super();
    this.state = {
      showContent: false
    }
  }

  toggleContent() {
    this.setState({showContent: !this.state.showContent} );
  }

  render() {
    return (
      <Container>
        <HeaderContainer>
          <ActionButton onClick={this.toggleContent.bind(this)}>
            { this.props.header }
            <ArrowContainer showing={this.state.showContent}>
              <Isvg src={ArrowIcon} />
            </ArrowContainer>
          </ActionButton>
        </HeaderContainer>
        <Content show={this.state.showContent}>
          {React.Children.toArray(this.props.children)}
        </Content>
      </Container>
    );
  }
}

CollapsingSection.propTypes = {
  // header: React.PropTypes.object.required
};

export default CollapsingSection;
<|endoftext|>


<commit_before>var http = require('http');
var util = require('../lib/util');

module.exports = function init(proxy, callback) {
  var config = proxy.config;
  var count = 2;
  var execCallback = function() {
    if (--count === 0) {
      callback();
    }
  };
  if (config.customUIPort) {
    var server = http.createServer();
    require(config.uipath)(server, proxy);
    server.listen(config.uiport, execCallback);
  } else {
    util.getServer(function(server, port) {
      config.uiport = port;
      require(config.uipath)(server, proxy);
      execCallback();
    }, config.uiport);
  }
  util.getServer(function(server, port) {
    config.weinreport = port;
    require('./weinre')(server);
    execCallback();
  }, config.weinreport);
};
<commit_msg>refactor: bind host<commit_after>var http = require('http');
var util = require('../lib/util');

var LOCALHOST = '127.0.0.1';

module.exports = function init(proxy, callback) {
  var config = proxy.config;
  var count = 2;
  var execCallback = function() {
    if (--count === 0) {
      callback();
    }
  };
  if (config.customUIPort) {
    var server = http.createServer();
    require(config.uipath)(server, proxy);
    if (config.host === LOCALHOST) {
      server.listen(config.uiport, LOCALHOST, execCallback);
    } else {
      server.listen(config.uiport, execCallback);
    }
  } else {
    util.getServer(function(server, port) {
      config.uiport = port;
      require(config.uipath)(server, proxy);
      execCallback();
    }, config.uiport);
  }
  util.getServer(function(server, port) {
    config.weinreport = port;
    require('./weinre')(server);
    execCallback();
  }, config.weinreport);
};
<|endoftext|>


<commit_before>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import React from 'react';
import SwipeableViews from 'react-swipeable-views';
import Button from '@material-ui/core/Button';
import BulletIcon from '@material-ui/icons/StarRate';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import withMobileDialog from '@material-ui/core/withMobileDialog';
import MobileStepper from '@material-ui/core/MobileStepper';
import GenericDialog, { onEnterKeyHandler } from './GenericDialog';
import WelcomeImage from '../../assets/images/onboarding.jpg';
import BrowserExtension from '../../assets/images/browser-extensions.png';
import i18n from '../../services/i18n';
import PlatformIO from '../../services/platform-io';

type Props = {
  open: boolean,
  fullScreen: boolean,
  onClose: () => void
};

type State = {
  activeStep: number
};

class ProTeaserDialog extends React.Component<Props, State> {
  state = {
    activeStep: 0,
  };

  maxSteps = 6;

  /* static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.show) {
      return {
        ...prevState,
        activeStep: 0,
      };
    }
    return null;
  } */

  handleNext = () => {
    this.setState(prevState => ({
      activeStep: prevState.activeStep + 1
    }));
  };

  handleBack = () => {
    this.setState(prevState => ({
      activeStep: prevState.activeStep - 1
    }));
  };

  handleStepChange = activeStep => {
    this.setState({ activeStep });
  };

  renderTitle = () => (
    <DialogTitle style={{ justifyContent: 'center', textAlign: 'center' }}>
      TagSpaces PRO Features Overview
    </DialogTitle>
  );

  renderContent = () => {
    const { activeStep } = this.state;
    const slideStyles = {
      height: 480,
      padding: 5,
      overflow: 'hidden',
      textAlign: 'left'
    };

    return (
      <DialogContent
        style={{ height: 550, overflow: 'hidden', paddingBottom: 0 }}
      >
        <SwipeableViews
          index={activeStep}
          onChangeIndex={this.handleStepChange}
          enableMouseEvents
        >

            <Typography variant="h5" style={{ textAlign: 'center' }}>Key Features</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Typography variant="subtitle1"><BulletIcon /> Generating Persistent Thumbnails</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Advanced Search by file type and tags</Typography>
            <Typography variant="subtitle1"><BulletIcon /> File and Folder Description</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Connecting S3 Object Store Network Locations</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Typography variant="h5" style={{ textAlign: 'center' }}>
              <Button
                onClick={() => {
                  // this.props.onClose();
                  PlatformIO.openUrl('https://www.tagspaces.org/products/');
                }}
                variant="contained"
                color="primary"
              >
                Open Product Comparison Page
              </Button>
            </Typography>

            <Typography variant="h5" style={{ textAlign: 'center' }}>Generating Persistent Thumbnails</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Typography variant="subtitle1"><BulletIcon /> Images: JPG, PNG, GIF, ... (supported also in the free version)</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Videos: WEBM, OGV, MP4, M4V, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Notes: HTML, MD, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Text files: TXT, source code files</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Bookmarks: URL</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Ebooks: EPUB</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Archives: ZIP</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Portable Documents: PDF</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Office Documents: ODT, ODP, ODS, DOCX, XLSX, PPTX</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Button
              onClick={() => {
                // this.props.onClose();
                PlatformIO.openUrl('https://www.tagspaces.org/products/pro/#thumbnailsGeneration');
              }}
              variant="contained"
              color="primary"
            >
              Learn more
            </Button>

            <Typography variant="h5" style={{ textAlign: 'center' }}>Advanced Search Options</Typography>
            <Typography variant="h6">Search by file type</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Documents: PDF, ODF, DOCX, EXL, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Notes: MD, TXT, HTML, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Audio files: OGG, MP3, WAV, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Video files: WEBM, OGV, MP4, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Archives: ZIP, RAR, TGZ, 7Z, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Bookmarks: URL, LNK, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> eBook EPUB, MOBI, AZW, PRC, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Filter for files or folders without tags</Typography>
            <Typography variant="h6">Searching types for tagged entries</Typography>
            <Typography variant="subtitle1"><BulletIcon /> At least one tag - logical OR search</Typography>
            <Typography variant="subtitle1"><BulletIcon /> All tag - logical AND search</Typography>
            <Typography variant="subtitle1" style={{ textAlign: 'right' }}>
              <Button
                onClick={() => {
                  // this.props.onClose();
                  PlatformIO.openUrl('https://www.tagspaces.org/products/pro/#advancedSearch');
                }}
                variant="contained"
                color="primary"
              >
                Learn more
              </Button>
            </Typography>

            <Typography variant="h5" style={{ textAlign: 'center' }}>Description for File and Folders</Typography>
            <Typography variant="h6"> </Typography>
            <Typography variant="subtitle1">In addition to the tagging in the TagSpaces PRO you have the ability to annotate every file or folder with a description. This is useful in particular if you want to add some more textual content to given a file or folder.</Typography>
            <Typography variant="h6"> </Typography>
            <Typography variant="subtitle1" style={{ textAlign: 'center' }}>
              <Button
                onClick={() => {
                  // this.props.onClose();
                  PlatformIO.openUrl('https://www.tagspaces.org/products/pro/#fileFolderMeta');
                }}
                variant="contained"
                color="primary"
              >
                Learn more
              </Button>
            </Typography>

            <Typography variant="h5" style={{ textAlign: 'center' }}>Connecting S3 Object Store Network Locations</Typography>
            <Typography variant="h6"> </Typography>
            <Typography variant="subtitle1">With this feature, TagSpaces PRO is going into direction of supporting the Cloud as file storage. In general it enables you to creating a location pointing to a remote object storages also knows as buckets on AWS S3 infrastructure. By doing this you get a full-fledged file organizer, browser and navigator for this bucket, directly in TagSpaces. You don not have to download separately and eventually to upload them back in order to preview, edit or annotate them.
            </Typography>
            <Typography variant="h6"> </Typography>
            <Typography variant="subtitle1" style={{ textAlign: 'center' }}>
              <Button
                onClick={() => {
                  // this.props.onClose();
                  PlatformIO.openUrl('https://www.tagspaces.org/products/pro/#s3objectStores');
                }}
                variant="contained"
                color="primary"
              >
                Learn more
              </Button>
            </Typography>

            <Typography variant="h5" style={{ textAlign: 'center' }}>Other advanced features</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Typography variant="subtitle1"><BulletIcon /> Location monitoring for changes</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Persistent manual index</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Signed installers for Windows</Typography>
            <Typography variant="subtitle1"><BulletIcon /> White label package (available in TagSpaces Enterprise)</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Custom extension development (available in TagSpaces Enterprise)</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Typography variant="subtitle1">
              If you want more information, click on the following button to visit the product comparison page on our website.
            </Typography>
            <Typography variant="subtitle1" style={{ textAlign: 'center' }}> </Typography>
            <Typography variant="subtitle1" style={{ textAlign: 'center' }}>
              <Button
                onClick={() => {
                  // this.props.onClose();
                  PlatformIO.openUrl('https://www.tagspaces.org/products/');
                }}
                variant="contained"
                color="primary"
              >
                Open Product Comparison
              </Button>
            </Typography>

        </SwipeableViews>
        <MobileStepper
          style={{ marginTop: 10, backgroundColor: 'transparent' }}
          steps={this.maxSteps}
          position="static"
          activeStep={activeStep}
          nextButton={
            (activeStep === this.maxSteps - 1) ? (
              <Button
                size="small"
                onClick={this.props.onClose}
                // variant="contained"
                // color="primary"
              >
                Close
              </Button>

            ) : (
              <Button
                size="small"
                onClick={this.handleNext}
              >
                Next
              </Button>
            )
          }
          backButton={
            <Button
              size="small"
              onClick={this.handleBack}
              disabled={activeStep === 0}
            >
              Back
            </Button>
          }
        />
      </DialogContent>
    );
  };
  renderActions = () => (
    <DialogActions style={{ justifyContent: 'center' }}>
      <Button
        data-tid="confirmLicenseDialog"
        onClick={this.props.onClose}
        variant={
          this.state.activeStep === this.maxSteps - 1 ? 'contained' : 'text'
        }
        color="primary"
      >
        {this.state.activeStep === this.maxSteps - 1
          ? 'Start using TagSpaces'
          : i18n.t('core:closeButton')}
      </Button>
    </DialogActions>
  );

  render() {
    const { fullScreen, open, onClose } = this.props;
    return (
      <GenericDialog
        fullScreen={fullScreen}
        open={open}
        onClose={onClose}
        onEnterKey={(event) => onEnterKeyHandler(event, this.onConfirm)}
        renderTitle={this.renderTitle}
        renderContent={this.renderContent}
        // renderActions={this.renderActions}
      />
    );
  }
}

export default (withMobileDialog()(ProTeaserDialog));
<commit_msg>clarifying a PRO feature #882<commit_after>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import React from 'react';
import SwipeableViews from 'react-swipeable-views';
import Button from '@material-ui/core/Button';
import BulletIcon from '@material-ui/icons/StarRate';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import withMobileDialog from '@material-ui/core/withMobileDialog';
import MobileStepper from '@material-ui/core/MobileStepper';
import GenericDialog, { onEnterKeyHandler } from './GenericDialog';
import WelcomeImage from '../../assets/images/onboarding.jpg';
import BrowserExtension from '../../assets/images/browser-extensions.png';
import i18n from '../../services/i18n';
import PlatformIO from '../../services/platform-io';

type Props = {
  open: boolean,
  fullScreen: boolean,
  onClose: () => void
};

type State = {
  activeStep: number
};

class ProTeaserDialog extends React.Component<Props, State> {
  state = {
    activeStep: 0,
  };

  maxSteps = 6;

  /* static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.show) {
      return {
        ...prevState,
        activeStep: 0,
      };
    }
    return null;
  } */

  handleNext = () => {
    this.setState(prevState => ({
      activeStep: prevState.activeStep + 1
    }));
  };

  handleBack = () => {
    this.setState(prevState => ({
      activeStep: prevState.activeStep - 1
    }));
  };

  handleStepChange = activeStep => {
    this.setState({ activeStep });
  };

  renderTitle = () => (
    <DialogTitle style={{ justifyContent: 'center', textAlign: 'center' }}>
      TagSpaces PRO Features Overview
    </DialogTitle>
  );

  renderContent = () => {
    const { activeStep } = this.state;
    const slideStyles = {
      height: 480,
      padding: 5,
      overflow: 'hidden',
      textAlign: 'left'
    };

    return (
      <DialogContent
        style={{ height: 550, overflow: 'hidden', paddingBottom: 0 }}
      >
        <SwipeableViews
          index={activeStep}
          onChangeIndex={this.handleStepChange}
          enableMouseEvents
        >

            <Typography variant="h5" style={{ textAlign: 'center' }}>Key Features</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Typography variant="subtitle1"><BulletIcon /> Generating Persistent Thumbnails</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Advanced Search by file type and tags</Typography>
            <Typography variant="subtitle1"><BulletIcon /> File and Folder Description</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Connecting S3 Object Store Network Locations</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Typography variant="h5" style={{ textAlign: 'center' }}>
              <Button
                onClick={() => {
                  // this.props.onClose();
                  PlatformIO.openUrl('https://www.tagspaces.org/products/');
                }}
                variant="contained"
                color="primary"
              >
                Open Product Comparison Page
              </Button>
            </Typography>

            <Typography variant="h5" style={{ textAlign: 'center' }}>Generating Persistent Thumbnails</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Typography variant="subtitle1"><BulletIcon /> Images: JPG, PNG, GIF, ... (supported also in the free version)</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Videos: WEBM, OGV, MP4, M4V, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Notes: HTML, MD, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Text files: TXT, source code files</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Bookmarks: URL</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Ebooks: EPUB</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Archives: ZIP</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Portable Documents: PDF</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Office Documents: ODT, ODP, ODS, DOCX, XLSX, PPTX</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Button
              onClick={() => {
                // this.props.onClose();
                PlatformIO.openUrl('https://www.tagspaces.org/products/pro/#thumbnailsGeneration');
              }}
              variant="contained"
              color="primary"
            >
              Learn more
            </Button>

            <Typography variant="h5" style={{ textAlign: 'center' }}>Advanced Search Options</Typography>
            <Typography variant="h6">Search by file type</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Documents: PDF, ODF, DOCX, EXL, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Notes: MD, TXT, HTML, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Audio files: OGG, MP3, WAV, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Video files: WEBM, OGV, MP4, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Archives: ZIP, RAR, TGZ, 7Z, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Bookmarks: URL, LNK, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> eBook EPUB, MOBI, AZW, PRC, etc.</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Filter for files or folders without tags</Typography>
            <Typography variant="h6">Searching types for tagged entries</Typography>
            <Typography variant="subtitle1"><BulletIcon /> At least one tag - logical OR search</Typography>
            <Typography variant="subtitle1"><BulletIcon /> All tag - logical AND search</Typography>
            <Typography variant="subtitle1" style={{ textAlign: 'right' }}>
              <Button
                onClick={() => {
                  // this.props.onClose();
                  PlatformIO.openUrl('https://www.tagspaces.org/products/pro/#advancedSearch');
                }}
                variant="contained"
                color="primary"
              >
                Learn more
              </Button>
            </Typography>

            <Typography variant="h5" style={{ textAlign: 'center' }}>Description for File and Folders</Typography>
            <Typography variant="h6"> </Typography>
            <Typography variant="subtitle1">In addition to the tagging in the TagSpaces PRO you have the ability to annotate every file or folder with a description. This is useful in particular if you want to add some more textual content to given a file or folder.</Typography>
            <Typography variant="h6"> </Typography>
            <Typography variant="subtitle1" style={{ textAlign: 'center' }}>
              <Button
                onClick={() => {
                  // this.props.onClose();
                  PlatformIO.openUrl('https://www.tagspaces.org/products/pro/#fileFolderMeta');
                }}
                variant="contained"
                color="primary"
              >
                Learn more
              </Button>
            </Typography>

            <Typography variant="h5" style={{ textAlign: 'center' }}>Connecting S3 Object Store Network Locations</Typography>
            <Typography variant="h6"> </Typography>
            <Typography variant="subtitle1">With this feature, TagSpaces PRO is going into direction of supporting the Cloud as file storage. In general it enables you to creating a location pointing to a remote object storages also knows as buckets on AWS S3 infrastructure. By doing this, you get a full-fledged file organizer, browser and navigator for this bucket, directly in TagSpaces. You do not have to download every file separately and eventually upload it back in order to preview, edit or annotate it.
            </Typography>
            <Typography variant="h6"> </Typography>
            <Typography variant="subtitle1" style={{ textAlign: 'center' }}>
              <Button
                onClick={() => {
                  // this.props.onClose();
                  PlatformIO.openUrl('https://www.tagspaces.org/products/pro/#s3objectStores');
                }}
                variant="contained"
                color="primary"
              >
                Learn more
              </Button>
            </Typography>

            <Typography variant="h5" style={{ textAlign: 'center' }}>Other advanced features</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Typography variant="subtitle1"><BulletIcon /> Location monitoring for changes</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Persistent manual index</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Signed installers for Windows</Typography>
            <Typography variant="subtitle1"><BulletIcon /> White label package (available in TagSpaces Enterprise)</Typography>
            <Typography variant="subtitle1"><BulletIcon /> Custom extension development (available in TagSpaces Enterprise)</Typography>
            <Typography variant="subtitle1"> </Typography>
            <Typography variant="subtitle1">
              If you want more information, click on the following button to visit the product comparison page on our website.
            </Typography>
            <Typography variant="subtitle1" style={{ textAlign: 'center' }}> </Typography>
            <Typography variant="subtitle1" style={{ textAlign: 'center' }}>
              <Button
                onClick={() => {
                  // this.props.onClose();
                  PlatformIO.openUrl('https://www.tagspaces.org/products/');
                }}
                variant="contained"
                color="primary"
              >
                Open Product Comparison
              </Button>
            </Typography>

        </SwipeableViews>
        <MobileStepper
          style={{ marginTop: 10, backgroundColor: 'transparent' }}
          steps={this.maxSteps}
          position="static"
          activeStep={activeStep}
          nextButton={
            (activeStep === this.maxSteps - 1) ? (
              <Button
                size="small"
                onClick={this.props.onClose}
                // variant="contained"
                // color="primary"
              >
                Close
              </Button>

            ) : (
              <Button
                size="small"
                onClick={this.handleNext}
              >
                Next
              </Button>
            )
          }
          backButton={
            <Button
              size="small"
              onClick={this.handleBack}
              disabled={activeStep === 0}
            >
              Back
            </Button>
          }
        />
      </DialogContent>
    );
  };
  renderActions = () => (
    <DialogActions style={{ justifyContent: 'center' }}>
      <Button
        data-tid="confirmLicenseDialog"
        onClick={this.props.onClose}
        variant={
          this.state.activeStep === this.maxSteps - 1 ? 'contained' : 'text'
        }
        color="primary"
      >
        {this.state.activeStep === this.maxSteps - 1
          ? 'Start using TagSpaces'
          : i18n.t('core:closeButton')}
      </Button>
    </DialogActions>
  );

  render() {
    const { fullScreen, open, onClose } = this.props;
    return (
      <GenericDialog
        fullScreen={fullScreen}
        open={open}
        onClose={onClose}
        onEnterKey={(event) => onEnterKeyHandler(event, this.onConfirm)}
        renderTitle={this.renderTitle}
        renderContent={this.renderContent}
        // renderActions={this.renderActions}
      />
    );
  }
}

export default (withMobileDialog()(ProTeaserDialog));
<|endoftext|>


<commit_before>const r = require("rethinkdb");
const meta = require("./meta.json");
const data = require("./data.json");

module.exports = function (message) {
	//Split message into keywords
	let input = message.content.replace(/\n/g, "").split(" ");

	//If there is no input, send a list of languages
	if (!input[2]) {
		let print = meta[message.data.lang] && meta[message.data.lang].message_choose_lang || "message_choose_lang";
		let langs = [];
		print += "\n```\n"
		Object.keys(data.languages).forEach(function(item) {
			langs.push(`${item} | ${data.languages[message.data.lang][item]}`);
		});

		print += langs.join("\n");
		print += "```";
		return message.reply(print);
	}

	//Get only the first 2 characters.
	input[2] = input[2].replace(/^[a-z0-9]+$/i, "").substr(0,2)
	console.dir(input);

	if(data.languages[input[2]]) return message.reply(meta[message.data.lang] && meta[message.data.lang].err_not_found || "err_not_found");

	//ONLY EXCEPTION FOR LANGUAGE
	//Gets the language code from the message,
	//as it is the one that was just set by
	//the (l)user
	message.reply(meta[input[2]] && meta[input[2]].success_found || "success_found");

	r.table("users").insert({
		id: message.member.id,
		lang: input[2]
	},{
		conflict: "replace"
	}).run(message.client.rethonk);
}
<commit_msg>more regex<commit_after>const r = require("rethinkdb");
const meta = require("./meta.json");
const data = require("./data.json");

module.exports = function (message) {
	//Split message into keywords
	let input = message.content.replace(/\n/g, "").split(" ");

	//If there is no input, send a list of languages
	if (!input[2]) {
		let print = meta[message.data.lang] && meta[message.data.lang].message_choose_lang || "message_choose_lang";
		let langs = [];
		print += "\n```\n"
		Object.keys(data.languages).forEach(function(item) {
			langs.push(`${item} | ${data.languages[message.data.lang][item]}`);
		});

		print += langs.join("\n");
		print += "```";
		return message.reply(print);
	}

	//Get only the first 2 characters.
	input[2] = input[2].replace(/[^a-z0-9]/gi, "").substr(0,2);
	console.dir(input);

	if(data.languages[input[2]]) return message.reply(meta[message.data.lang] && meta[message.data.lang].err_not_found || "err_not_found");

	//ONLY EXCEPTION FOR LANGUAGE
	//Gets the language code from the message,
	//as it is the one that was just set by
	//the (l)user
	message.reply(meta[input[2]] && meta[input[2]].success_found || "success_found");

	r.table("users").insert({
		id: message.member.id,
		lang: input[2]
	},{
		conflict: "replace"
	}).run(message.client.rethonk);
}
<|endoftext|>