<commit_before>(function($) {

$.widget('ui.dmWidget', {

  _init : function()
  {
    this.initialize();

    this.element.data('loaded', true);
  },

  openEditDialog: function()
  {
    var widget = this, restoreState = {}, dialogClass = widget.element.attr('id')+'_edit_dialog';

    $.dm.removeTipsy();

    if ($('body > div.'+dialogClass).length)
    {
      $('body > div.'+dialogClass).find('div.ui-dialog-content').dialog('moveToTop');
      return;
    }

    var $dialog = $.dm.ctrl.ajaxDialog({
      url:          $.dm.ctrl.getHref('+/dmWidget/edit'),
      data:         {widget_id: widget.getId()},
      title:        $('a.dm_widget_edit', widget.element).tipsyTitle(),
      width:        '600', // this should be revised and if possible - made dependant on content's width
      'class':      'dm_widget_edit_dialog_wrap '+dialogClass,
      resizable:    true,
      resize:       function(event, ui)
      {
        $dialog.maximizeContent('textarea.markItUpEditor');
      },
      beforeClose:  function()
      {
        if (!widget.deleted)
        {
          widget.reload(500);
        }
      }
    });

    $dialog.listenToFocusedInputs = function()
    {
      var $form = $('div.dm_widget_edit', $dialog);

      $form.find('.dm_form_elements :input').focus(function() {
        restoreState.activeElementName = $(this).attr('name');
      });
    }

    $dialog.saveState = function()
    {
      var $form = $('div.dm_widget_edit', $dialog);

      // Save active tab
      if ($tabbedFormActiveTab = $form.find('ul.ui-tabs-nav > li.ui-tabs-selected:first').orNot())
      {
        restoreState.activeTab = $tabbedFormActiveTab.find('>a').attr('href');
      }

      // Save focused element
      if (restoreState.activeElementName)
      {
        var s = ':input[name="'+restoreState.activeElementName+'"]:visible';
        var activeElement = $form.find(s).filter('input[type=text], textarea');
        if(activeElement.length > 0)
        {
          restoreState.activeSelection = [activeElement[0].selectionStart, activeElement[0].selectionEnd];
          restoreState.activeElementScrollTop = activeElement.scrollTop();
        }
      }
    };

    $dialog.restoreState = function()
    {
      var $form = $('div.dm_widget_edit', $dialog);

      // Restore active tab
      if(restoreState.activeTab)
      {
        $form.find('div.dm_tabbed_form').tabs('select', restoreState.activeTab);
      }

      // Maximize content
      $dialog.maximizeContent('textarea.markItUpEditor');

      // Restore focused element
      if (restoreState.activeElementName)
      {
        var s = ':input[name="'+restoreState.activeElementName+'"]:visible';
        var activeElement = $form.find(s);
        activeElement.focus();
        x = activeElement;
        if(activeElement.length > 0)
        {
          if (activeElement[0].setSelectionRange)
          {
            activeElement[0].setSelectionRange(restoreState.activeSelection[0], restoreState.activeSelection[1]);
          }
          activeElement.scrollTop(restoreState.activeElementScrollTop);
        }
      }

      $dialog.listenToFocusedInputs();
    };

    $dialog.maximizeContent = function(elToMaximize)
    {
      var $form = $dialog.find('form.dm_form');
      var formHeight = $form.height();
      var dialogHeight = $dialog.height();
      var $maximizable = $form.find(elToMaximize);
      $maximizable.height($maximizable.height() + dialogHeight - formHeight);
    };

    $dialog.bind('dmAjaxResponse', function()
    {
      $dialog.prepare();

      $('a.delete', $dialog).click(function()
      {
        if (confirm($(this).tipsyTitle()+" ?"))
        {
          $.dm.removeTipsy();
          widget._delete();
          $dialog.dialog('close');
        }
      });

      var $form = $('div.dm_widget_edit', $dialog);
      if (!$form.length)
      {
        return;
      }

      /*
       *Move cut & copy actions to the title
       */
      if ($cutCopy = $form.find('div.dm_cut_copy_actions').orNot())
      {
        $cutCopy.appendTo($dialog.parent().find('div.ui-dialog-titlebar')).show().find('a').click(function()
        {
          var $a = $(this).addClass('s16_gear');

          $.ajax({
            url:      $(this).attr('href'),
            success:  function()
            {
              $('#dm_tool_bar').dmFrontToolBar('reloadAddMenu', function()
              {
                $a.removeClass('s16_gear');
              });
            }
          });

          return false;
        });
      }
      /*
       * Apply generic front form abilities
       */
      $form.dmFrontForm();
      /*
       * Apply specific widget form abilities
       */
      if ((formClass = $form.metadata().form_class) && $.isFunction($form[formClass]))
      {
        $form[formClass](widget);
      }
      /*
       * Enable code editor link
       */
      $form.find('a.code_editor').each(function() {
        var $this = $(this).click(function() {
          $('#dm_tool_bar').dmFrontToolBar('openCodeEditor', function($codeEditor)
          {
            $codeEditor.find('#dm_code_editor_file_open a[href='+$this.attr('href')+']').trigger('click');
          });
        });
      });

      // Maximize content
      $dialog.restoreState();

      // enable tool tips
      $dialog.parent().find('a[title], input[title]').tipsy({gravity: $.fn.tipsy.autoSouth});

      $form.find('form').dmAjaxForm({
        beforeSubmit: function(data) {
          $dialog.block();
          $dialog.saveState();
        },
        error: function(xhr, textStatus, errorThrown)
        {
          $dialog.unblock();
          widget.element.unblock();
          $.dm.ctrl.errorDialog('Error when updating the widget', xhr.responseText);
        },
        success: function(data)
        {
          if('saved' == data)
          {
            $dialog.dialog('close');
            return;
          }

          parts = data.split(/\_\_DM\_SPLIT\_\_/);

          // update widget content
          if(parts[1])
          {
            widget.replace(parts[1]);
          }

          $form.trigger('submitSuccess');

          // update dialog content
          $dialog.html(parts[0]).trigger('dmAjaxResponse');
        }
      });
    });
  },

  _delete: function()
  {
    var self = this;
    self.deleted = true;

    $.ajax({
      url:      $.dm.ctrl.getHref('+/dmWidget/delete'),
      data:     {widget_id: self.getId()}
    });

    self.element.slideUp(500, function() {self.destroy();self.element.remove();$.dm.removeTipsy();});
  },

  reload: function(timeout)
  {
    var self = this;

    self.element.block();

    setTimeout(function()
    {
      $.ajax({
        url:      $.dm.ctrl.getHref('+/dmWidget/getFull'),
        data:     {widget_id: self.getId()},
        success:  function(html)
        {
          self.replace(html);
        }
      });
    }, timeout || 0);
  },

  replace: function(html)
  {
    if($encodedAssets = $('>div.dm_encoded_assets', '
'+html+'
'))
    {
      this.element.append($encodedAssets).dmExtractEncodedAssets();
    }

    this.element
    .attr('class', $('>div:first', '
'+html+'
').attr('class'))
    .find('div.dm_widget_inner')
    .html($('>div.dm_widget_inner', html).html())
    .attr('class', $('>div.dm_widget_inner', html).attr('class'))
    .end()
    .unblock()
    .trigger('dmWidgetLaunch');
  },

  openRecordEditDialog: function(widgetId)
  {
    var self = this, $button = self.element.find('a.dm_widget_record_edit');

    $.dm.removeTipsy();

    $button.block();

    $.ajax({
      url:      $.dm.ctrl.getHref('+/dmWidget/editRecord'),
      data:     {widget_id: self.getId(), dm_embed: 1},
      success:  function(html)
      {
        $('

')
        .html(html)
        .dmExtractEncodedAssets()
        .find('a')
        .colorbox({
          width:"90%",
          height:"90%",
          iframe: true,
          speed: 200,
          opacity: 0.5,
          onClosed: function()
          {
            self.reload();
          }
        })
        .trigger('click');

        $button.unblock();

        var $close = $('#cboxClose').attr('rel', ''), interval = setInterval(function()
        {
          if($close.attr('rel') == 'dm_close')
          {
            clearInterval(interval);
            $close.trigger('click');
          }
        }, 200);
      }
    });
  },

  initialize: function()
  {
    var self = this;

    if (this.element.attr('id') != undefined)
    {
      this.id = this.element.attr('id').substring(10);
    }

    $('> a.dm_widget_edit, > a.dm_widget_fast_edit', this.element).click(function() {
      if (!self.element.hasClass('dm_dragging')) {
        self.openEditDialog();
      }
    }).tipsy({gravity: $.fn.tipsy.autoSouth});

    $('a.dm_widget_record_edit', this.element).click(function() {
      self.openRecordEditDialog();
    }).tipsy({gravity: $.fn.tipsy.autoSouth});

    if($('div.dm_new_widget', this.element).length)
    {
      $('a.dm_widget_record_edit', this.element).hide();
    }
  },

  getId: function()
  {
    return this.id;
  }

});

})(jQuery);<commit_msg>fixed opening colorbox for fast record edit<commit_after>(function($) {

$.widget('ui.dmWidget', {

  _init : function()
  {
    this.initialize();

    this.element.data('loaded', true);
  },

  openEditDialog: function()
  {
    var widget = this, restoreState = {}, dialogClass = widget.element.attr('id')+'_edit_dialog';

    $.dm.removeTipsy();

    if ($('body > div.'+dialogClass).length)
    {
      $('body > div.'+dialogClass).find('div.ui-dialog-content').dialog('moveToTop');
      return;
    }

    var $dialog = $.dm.ctrl.ajaxDialog({
      url:          $.dm.ctrl.getHref('+/dmWidget/edit'),
      data:         {widget_id: widget.getId()},
      title:        $('a.dm_widget_edit', widget.element).tipsyTitle(),
      width:        '600', // this should be revised and if possible - made dependant on content's width
      'class':      'dm_widget_edit_dialog_wrap '+dialogClass,
      resizable:    true,
      resize:       function(event, ui)
      {
        $dialog.maximizeContent('textarea.markItUpEditor');
      },
      beforeClose:  function()
      {
        if (!widget.deleted)
        {
          widget.reload(500);
        }
      }
    });

    $dialog.listenToFocusedInputs = function()
    {
      var $form = $('div.dm_widget_edit', $dialog);

      $form.find('.dm_form_elements :input').focus(function() {
        restoreState.activeElementName = $(this).attr('name');
      });
    }

    $dialog.saveState = function()
    {
      var $form = $('div.dm_widget_edit', $dialog);

      // Save active tab
      if ($tabbedFormActiveTab = $form.find('ul.ui-tabs-nav > li.ui-tabs-selected:first').orNot())
      {
        restoreState.activeTab = $tabbedFormActiveTab.find('>a').attr('href');
      }

      // Save focused element
      if (restoreState.activeElementName)
      {
        var s = ':input[name="'+restoreState.activeElementName+'"]:visible';
        var activeElement = $form.find(s).filter('input[type=text], textarea');
        if(activeElement.length > 0)
        {
          restoreState.activeSelection = [activeElement[0].selectionStart, activeElement[0].selectionEnd];
          restoreState.activeElementScrollTop = activeElement.scrollTop();
        }
      }
    };

    $dialog.restoreState = function()
    {
      var $form = $('div.dm_widget_edit', $dialog);

      // Restore active tab
      if(restoreState.activeTab)
      {
        $form.find('div.dm_tabbed_form').tabs('select', restoreState.activeTab);
      }

      // Maximize content
      $dialog.maximizeContent('textarea.markItUpEditor');

      // Restore focused element
      if (restoreState.activeElementName)
      {
        var s = ':input[name="'+restoreState.activeElementName+'"]:visible';
        var activeElement = $form.find(s);
        activeElement.focus();
        x = activeElement;
        if(activeElement.length > 0)
        {
          if (activeElement[0].setSelectionRange)
          {
            activeElement[0].setSelectionRange(restoreState.activeSelection[0], restoreState.activeSelection[1]);
          }
          activeElement.scrollTop(restoreState.activeElementScrollTop);
        }
      }

      $dialog.listenToFocusedInputs();
    };

    $dialog.maximizeContent = function(elToMaximize)
    {
      var $form = $dialog.find('form.dm_form');
      var formHeight = $form.height();
      var dialogHeight = $dialog.height();
      var $maximizable = $form.find(elToMaximize);
      $maximizable.height($maximizable.height() + dialogHeight - formHeight);
    };

    $dialog.bind('dmAjaxResponse', function()
    {
      $dialog.prepare();

      $('a.delete', $dialog).click(function()
      {
        if (confirm($(this).tipsyTitle()+" ?"))
        {
          $.dm.removeTipsy();
          widget._delete();
          $dialog.dialog('close');
        }
      });

      var $form = $('div.dm_widget_edit', $dialog);
      if (!$form.length)
      {
        return;
      }

      /*
       *Move cut & copy actions to the title
       */
      if ($cutCopy = $form.find('div.dm_cut_copy_actions').orNot())
      {
        $cutCopy.appendTo($dialog.parent().find('div.ui-dialog-titlebar')).show().find('a').click(function()
        {
          var $a = $(this).addClass('s16_gear');

          $.ajax({
            url:      $(this).attr('href'),
            success:  function()
            {
              $('#dm_tool_bar').dmFrontToolBar('reloadAddMenu', function()
              {
                $a.removeClass('s16_gear');
              });
            }
          });

          return false;
        });
      }
      /*
       * Apply generic front form abilities
       */
      $form.dmFrontForm();
      /*
       * Apply specific widget form abilities
       */
      if ((formClass = $form.metadata().form_class) && $.isFunction($form[formClass]))
      {
        $form[formClass](widget);
      }
      /*
       * Enable code editor link
       */
      $form.find('a.code_editor').each(function() {
        var $this = $(this).click(function() {
          $('#dm_tool_bar').dmFrontToolBar('openCodeEditor', function($codeEditor)
          {
            $codeEditor.find('#dm_code_editor_file_open a[href='+$this.attr('href')+']').trigger('click');
          });
        });
      });

      // Maximize content
      $dialog.restoreState();

      // enable tool tips
      $dialog.parent().find('a[title], input[title]').tipsy({gravity: $.fn.tipsy.autoSouth});

      $form.find('form').dmAjaxForm({
        beforeSubmit: function(data) {
          $dialog.block();
          $dialog.saveState();
        },
        error: function(xhr, textStatus, errorThrown)
        {
          $dialog.unblock();
          widget.element.unblock();
          $.dm.ctrl.errorDialog('Error when updating the widget', xhr.responseText);
        },
        success: function(data)
        {
          if('saved' == data)
          {
            $dialog.dialog('close');
            return;
          }

          parts = data.split(/\_\_DM\_SPLIT\_\_/);

          // update widget content
          if(parts[1])
          {
            widget.replace(parts[1]);
          }

          $form.trigger('submitSuccess');

          // update dialog content
          $dialog.html(parts[0]).trigger('dmAjaxResponse');
        }
      });
    });
  },

  _delete: function()
  {
    var self = this;
    self.deleted = true;

    $.ajax({
      url:      $.dm.ctrl.getHref('+/dmWidget/delete'),
      data:     {widget_id: self.getId()}
    });

    self.element.slideUp(500, function() {self.destroy();self.element.remove();$.dm.removeTipsy();});
  },

  reload: function(timeout)
  {
    var self = this;

    self.element.block();

    setTimeout(function()
    {
      $.ajax({
        url:      $.dm.ctrl.getHref('+/dmWidget/getFull'),
        data:     {widget_id: self.getId()},
        success:  function(html)
        {
          self.replace(html);
        }
      });
    }, timeout || 0);
  },

  replace: function(html)
  {
    if($encodedAssets = $('>div.dm_encoded_assets', '
'+html+'
'))
    {
      this.element.append($encodedAssets).dmExtractEncodedAssets();
    }

    this.element
    .attr('class', $('>div:first', '
'+html+'
').attr('class'))
    .find('div.dm_widget_inner')
    .html($('>div.dm_widget_inner', html).html())
    .attr('class', $('>div.dm_widget_inner', html).attr('class'))
    .end()
    .unblock()
    .trigger('dmWidgetLaunch');
  },

  openRecordEditDialog: function(widgetId)
  {
    var self = this, $button = self.element.find('a.dm_widget_record_edit');

    $.dm.removeTipsy();

    $button.block();

    $.ajax({
      url:      $.dm.ctrl.getHref('+/dmWidget/editRecord'),
      data:     {widget_id: self.getId(), dm_embed: 1},
      success:  function(html)
      {
        $('

')
        .html(html)
        .dmExtractEncodedAssets()
        .find('a')
        .colorbox({
          width:"90%",
          height:"90%",
          iframe: true,
          speed: 200,
          opacity: 0.5,
          open: true,
          onClosed: function()
          {
            self.reload();
          }
        });

        $button.unblock();

        var $close = $('#cboxClose').attr('rel', ''), interval = setInterval(function()
        {
          if($close.attr('rel') == 'dm_close')
          {
            clearInterval(interval);
            $close.trigger('click');
          }
        }, 200);
      }
    });
  },

  initialize: function()
  {
    var self = this;

    if (this.element.attr('id') != undefined)
    {
      this.id = this.element.attr('id').substring(10);
    }

    $('> a.dm_widget_edit, > a.dm_widget_fast_edit', this.element).click(function() {
      if (!self.element.hasClass('dm_dragging')) {
        self.openEditDialog();
      }
    }).tipsy({gravity: $.fn.tipsy.autoSouth});

    $('a.dm_widget_record_edit', this.element).click(function() {
      self.openRecordEditDialog();
    }).tipsy({gravity: $.fn.tipsy.autoSouth});

    if($('div.dm_new_widget', this.element).length)
    {
      $('a.dm_widget_record_edit', this.element).hide();
    }
  },

  getId: function()
  {
    return this.id;
  }

});

})(jQuery);<|endoftext|>


<commit_before>import R from 'ramda';
import { Maybe, Either } from 'ramda-fantasy';
import * as types from './types';

export * from './types';

const def = types.def;

/**
 * DEPRECATED: Use of this function lead to meaningless error messages that
 * are nearly impossible to debug. Use `explodeMaybe` and `explodeEither`
 * to convert monads to unhandled exceptions.
 *
 * Function to rapidly extract a value from Maybe or Either monad.
 * But it should be used only when we're sure that we should have a value,
 * otherwise it will throw an exception.
 *
 * @private
 * @function explode
 * @param {Maybe|Either}
 * @returns {*}
 * @throws Error
 */
export const explode = R.cond([
  [Maybe.isJust, R.unnest],
  [Maybe.isNothing, () => { throw new Error('Maybe is expected to be Just, but its Nothing.'); }],
  [Either.isRight, R.unnest],
  [Either.isLeft, (val) => { throw new Error(`Either expected to be Right, but its Left with value: ${val}`); }],
  [R.T, (input) => { throw new Error(`Maybe or Either should be passed into explode function. Passed: ${input}`); }],
]);

/**
 * Function to rapidly extract a value from Maybe.Just
 * or throw a defined error message on Maybe.Nothing
 *
 * @private
 * @function explodeMaybe
 * @param {String} errorMessage
 * @param {Maybe} maybeObject
 * @returns {*}
 * @throws Error
 */
export const explodeMaybe = def(
  'explodeMaybe :: String -> Maybe a -> a',
  (errorMessage, maybeObject) => {
    if (maybeObject.isJust) return maybeObject.value;
    throw new Error(errorMessage);
  }
);

/**
 * Function to extract value from `Either` by providing a function
 * to handle the types of values contained in both `Left` and `Right`.
 *
 * This is hack to prevent errors on using `Either.either` with
 * cross-package Eithers (`Either.either` checks for instanceof and
 * it has other constuctors for some reason).
 *
 * @private
 * @function foldEither
 * @param {Function} leftFn
 * @param {Function} rightFn
 * @param {Either} eitherObject
 * @returns {*}
 * @throws Error
 */
export const foldEither = def(
  'foldEither :: (a -> c) -> (b -> c) -> Either a b -> c',
  (leftFn, rightFn, eitherObject) => (
    eitherObject.isLeft ? leftFn(eitherObject.value) : rightFn(eitherObject.value)
  )
);

/**
 * Unwraps Either monad and returns it’s value if it is Right and throws an Error
 * if it is Left.
 */
export const explodeEither = def(
  'explodeMaybe :: Either a b -> b',
  foldEither(
    (err) => { throw new Error(`Explosion failed: ${err}`); },
    R.identity
  )
);

/**
 * Returns a result of calling iterator function over argument list.
 * Use this function if iterator function returns some Monad.
 * Monad constructor should be passed as first argument, cause
 * we haven't got type deduction in JS. :-(
 * E.G.
 * assocLink :: Link -> Patch -> Either Error Patch
 * upsertLinks = reduceEither(assocLink, patch, [link0, link1, link2]);
 */
export const reduceM = def(
  'reduceM :: (b -> m b) -> (b -> a -> m b) -> b -> [a] -> m b',
  (m, fn, initial, list) => R.reduce(
    (acc, a) => R.chain(val => fn(val, a), acc),
    m(initial),
    list
  )
);

/**
 * Returns a result of calling iterator function over argument list.
 * Use this function if iterator function returns Either.
 * @see reduceM for more details
 */
export const reduceEither = def(
  'reduceEither :: (b -> a -> Either c b) -> b -> [a] -> Either c b',
  reduceM(Either.of)
);

/**
 * Returns a result of calling iterator function over argument list.
 * Use this function if iterator function returns Either.
 * @see reduceM for more details
 */
export const reduceMaybe = def(
  'reduceEither :: (b -> a -> Maybe b) -> b -> [a] -> Maybe b',
  reduceM(Maybe.of)
);

/**
 * Returns an object provided with all `null` and `undefined` values omitted
 */
export const omitNilValues = def(
  'omitNilValues :: Object -> Object',
  R.reject(R.isNil)
);

/**
 * Returns an object provided with all empty (in sense of R.isEmpty) values
 * omitted
 */
export const omitEmptyValues = def(
  'omitEmptyValues :: Object -> Object',
  R.reject(R.isEmpty)
);

/**
 * Checks if an element is among elements of a list
 */
export const isAmong = def(
  'isAmong :: [a] -> a -> Boolean',
  R.flip(R.contains)
);

/**
 * Like `R.objOf` but returns empty object {} if value is `null` or `undefined`
 */
export const optionalObjOf = def(
  // 'optionalObjOf :: String -> a -> StrMap a', // TODO fix hm-def
  'optionalObjOf :: String -> a -> Object',
  (key, val) => (val == null ? {} : { [key]: val })
);

export const notNil = R.complement(R.isNil);
export const notEmpty = R.complement(R.isEmpty);
export const hasNo = R.complement(R.has);

// Returns a copy of second object with omitted
// fields that are equal to fields from first object.
// Kind of the reverse of R.merge.
//
//   subtractObject(
//     { foo: 1, bar: 2 },
//     { foo: 1, bar: 'not 2', baz: 3 }
//   ) // => { bar: "not 2", baz: 3}
export const subtractObject = def(
  'subtractObject :: Object -> Object -> Object',
  R.uncurryN(2, objToSubstract => R.converge(
    R.omit,
    [
      R.compose(
        R.keys,
        R.pickBy(R.both(
          (value, key) => R.has(key, objToSubstract),
          (value, key) => R.propEq(key, value, objToSubstract)
        ))
      ),
      R.identity,
    ]
  ))
);

/**
 * Like `R.tap` but works with Promises.
 * @param {Function} promiseFn Function that returns Promise
 * @returns {Function} Run promiseFn with argument and returns the same argument on resolve
 */
export const tapP = promiseFn => arg => promiseFn(arg).then(R.always(arg));

// :: ERROR_CODE -> Error -> Promise.Reject Error
export const rejectWithCode = R.curry(
  (code, err) => Promise.reject(Object.assign(err, { errorCode: code }))
);

// :: [Promise a] -> Promise a
export const allPromises = promises => Promise.all(promises);

/**
 * Concatenates a list of lists into one list.
 */
// :: [[a]] -> [a]
export const concatAll = R.reduce(R.concat, []);

export default Object.assign(
  {
    explode,
    explodeMaybe,
    foldEither,
    hasNo,
    omitNilValues,
    omitEmptyValues,
    isAmong,
    optionalObjOf,
    notNil,
    notEmpty,
    rejectWithCode,
    allPromises,
    concatAll,
  },
  types
);
<commit_msg>feat(xod-func-tools): add ‘eitherToPromise’ util<commit_after>import R from 'ramda';
import { Maybe, Either } from 'ramda-fantasy';
import * as types from './types';

export * from './types';

const def = types.def;

/**
 * DEPRECATED: Use of this function lead to meaningless error messages that
 * are nearly impossible to debug. Use `explodeMaybe` and `explodeEither`
 * to convert monads to unhandled exceptions.
 *
 * Function to rapidly extract a value from Maybe or Either monad.
 * But it should be used only when we're sure that we should have a value,
 * otherwise it will throw an exception.
 *
 * @private
 * @function explode
 * @param {Maybe|Either}
 * @returns {*}
 * @throws Error
 */
export const explode = R.cond([
  [Maybe.isJust, R.unnest],
  [Maybe.isNothing, () => { throw new Error('Maybe is expected to be Just, but its Nothing.'); }],
  [Either.isRight, R.unnest],
  [Either.isLeft, (val) => { throw new Error(`Either expected to be Right, but its Left with value: ${val}`); }],
  [R.T, (input) => { throw new Error(`Maybe or Either should be passed into explode function. Passed: ${input}`); }],
]);

/**
 * Function to rapidly extract a value from Maybe.Just
 * or throw a defined error message on Maybe.Nothing
 *
 * @private
 * @function explodeMaybe
 * @param {String} errorMessage
 * @param {Maybe} maybeObject
 * @returns {*}
 * @throws Error
 */
export const explodeMaybe = def(
  'explodeMaybe :: String -> Maybe a -> a',
  (errorMessage, maybeObject) => {
    if (maybeObject.isJust) return maybeObject.value;
    throw new Error(errorMessage);
  }
);

/**
 * Function to extract value from `Either` by providing a function
 * to handle the types of values contained in both `Left` and `Right`.
 *
 * This is hack to prevent errors on using `Either.either` with
 * cross-package Eithers (`Either.either` checks for instanceof and
 * it has other constuctors for some reason).
 *
 * @private
 * @function foldEither
 * @param {Function} leftFn
 * @param {Function} rightFn
 * @param {Either} eitherObject
 * @returns {*}
 * @throws Error
 */
export const foldEither = def(
  'foldEither :: (a -> c) -> (b -> c) -> Either a b -> c',
  (leftFn, rightFn, eitherObject) => (
    eitherObject.isLeft ? leftFn(eitherObject.value) : rightFn(eitherObject.value)
  )
);

// :: Either a b -> Promise a b
export const eitherToPromise = foldEither(
  Promise.reject.bind(Promise),
  Promise.resolve.bind(Promise)
);

/**
 * Unwraps Either monad and returns it’s value if it is Right and throws an Error
 * if it is Left.
 */
export const explodeEither = def(
  'explodeMaybe :: Either a b -> b',
  foldEither(
    (err) => { throw new Error(`Explosion failed: ${err}`); },
    R.identity
  )
);

/**
 * Returns a result of calling iterator function over argument list.
 * Use this function if iterator function returns some Monad.
 * Monad constructor should be passed as first argument, cause
 * we haven't got type deduction in JS. :-(
 * E.G.
 * assocLink :: Link -> Patch -> Either Error Patch
 * upsertLinks = reduceEither(assocLink, patch, [link0, link1, link2]);
 */
export const reduceM = def(
  'reduceM :: (b -> m b) -> (b -> a -> m b) -> b -> [a] -> m b',
  (m, fn, initial, list) => R.reduce(
    (acc, a) => R.chain(val => fn(val, a), acc),
    m(initial),
    list
  )
);

/**
 * Returns a result of calling iterator function over argument list.
 * Use this function if iterator function returns Either.
 * @see reduceM for more details
 */
export const reduceEither = def(
  'reduceEither :: (b -> a -> Either c b) -> b -> [a] -> Either c b',
  reduceM(Either.of)
);

/**
 * Returns a result of calling iterator function over argument list.
 * Use this function if iterator function returns Either.
 * @see reduceM for more details
 */
export const reduceMaybe = def(
  'reduceEither :: (b -> a -> Maybe b) -> b -> [a] -> Maybe b',
  reduceM(Maybe.of)
);

/**
 * Returns an object provided with all `null` and `undefined` values omitted
 */
export const omitNilValues = def(
  'omitNilValues :: Object -> Object',
  R.reject(R.isNil)
);

/**
 * Returns an object provided with all empty (in sense of R.isEmpty) values
 * omitted
 */
export const omitEmptyValues = def(
  'omitEmptyValues :: Object -> Object',
  R.reject(R.isEmpty)
);

/**
 * Checks if an element is among elements of a list
 */
export const isAmong = def(
  'isAmong :: [a] -> a -> Boolean',
  R.flip(R.contains)
);

/**
 * Like `R.objOf` but returns empty object {} if value is `null` or `undefined`
 */
export const optionalObjOf = def(
  // 'optionalObjOf :: String -> a -> StrMap a', // TODO fix hm-def
  'optionalObjOf :: String -> a -> Object',
  (key, val) => (val == null ? {} : { [key]: val })
);

export const notNil = R.complement(R.isNil);
export const notEmpty = R.complement(R.isEmpty);
export const hasNo = R.complement(R.has);

// Returns a copy of second object with omitted
// fields that are equal to fields from first object.
// Kind of the reverse of R.merge.
//
//   subtractObject(
//     { foo: 1, bar: 2 },
//     { foo: 1, bar: 'not 2', baz: 3 }
//   ) // => { bar: "not 2", baz: 3}
export const subtractObject = def(
  'subtractObject :: Object -> Object -> Object',
  R.uncurryN(2, objToSubstract => R.converge(
    R.omit,
    [
      R.compose(
        R.keys,
        R.pickBy(R.both(
          (value, key) => R.has(key, objToSubstract),
          (value, key) => R.propEq(key, value, objToSubstract)
        ))
      ),
      R.identity,
    ]
  ))
);

/**
 * Like `R.tap` but works with Promises.
 * @param {Function} promiseFn Function that returns Promise
 * @returns {Function} Run promiseFn with argument and returns the same argument on resolve
 */
export const tapP = promiseFn => arg => promiseFn(arg).then(R.always(arg));

// :: ERROR_CODE -> Error -> Promise.Reject Error
export const rejectWithCode = R.curry(
  (code, err) => Promise.reject(Object.assign(err, { errorCode: code }))
);

// :: [Promise a] -> Promise a
export const allPromises = promises => Promise.all(promises);

/**
 * Concatenates a list of lists into one list.
 */
// :: [[a]] -> [a]
export const concatAll = R.reduce(R.concat, []);

export default Object.assign(
  {
    explode,
    explodeMaybe,
    foldEither,
    hasNo,
    omitNilValues,
    omitEmptyValues,
    isAmong,
    optionalObjOf,
    notNil,
    notEmpty,
    rejectWithCode,
    allPromises,
    concatAll,
  },
  types
);
<|endoftext|>


<commit_before>var app = require('app');
var BrowserWindow = require('browser-window');
var menu = require('./menu');

process.env.PATH += ':/usr/local/bin';

var mainWindow;

app.on('ready', function () {
    mainWindow = createWindow();
});

app.on('window-all-closed', function () {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate-with-no-open-windows', function () {
    mainWindow = createWindow();
});

function createWindow() {
    var workAreaSize = require('screen').getPrimaryDisplay().workAreaSize;
    var window = new BrowserWindow({
        'web-preferences': {
            'experimental-features': true,
            'experimental-canvas-features': true,
            'subpixel-font-scaling': true,
            'overlay-scrollbars': true
        },
        resizable: true,
        'min-width': 500,
        'min-height': 300,
        width: workAreaSize.width,
        height: workAreaSize.height,
        show: false
    });

    window.loadUrl('file://' + __dirname + '/../../index.html');
    menu.setMenu(app, window);

    window.on('closed', function () {
        window = null;
    });

    window.webContents.on('did-finish-load', function () {
        window.show();
        window.focus();
    });

    return window;
}
<commit_msg>Create main window differently.<commit_after>var app = require('app');
var BrowserWindow = require('browser-window');
var menu = require('./menu');

process.env.PATH += ':/usr/local/bin';

var mainWindow;
app.on('open-file', function (a, b) {
    console.log(a);
    console.log(b);
});

app.on('ready', createWindow);

app.on('mainWindow-all-closed', function () {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate-with-no-open-windows', createWindow);

function createWindow() {
    if (mainWindow) return;

    var workAreaSize = require('screen').getPrimaryDisplay().workAreaSize;
    mainWindow = new BrowserWindow({
        'web-preferences': {
            'experimental-features': true,
            'experimental-canvas-features': true,
            'subpixel-font-scaling': true,
            'overlay-scrollbars': true
        },
        resizable: true,
        'min-width': 500,
        'min-height': 300,
        width: workAreaSize.width,
        height: workAreaSize.height,
        show: false
    });

    mainWindow.loadUrl('file://' + __dirname + '/../../index.html');
    menu.setMenu(app, mainWindow);

    mainWindow.on('closed', function () {
        mainWindow = null;
    });

    mainWindow.webContents.on('did-finish-load', function () {
        mainWindow.show();
        mainWindow.focus();
    });
}
<|endoftext|>


<commit_before>with (exports) {

	/**
	 * Reference to the system print function.
	 * @type {Function}
	 * @const
	 */
	var SYSTEM_PRINT = print;

	/**
	 * Regular expression matches XML entity reference.
	 * @type {Object}
	 * @const
	 */
	var ENTITY_REGEXP = /&([a-z]+);/ig;

	/**
	 * List of predefined XML entities.
	 * @type {Array.<string>}
	 * @const
	 */
	var XML_ENTITIES = ['quot', 'amp', 'apos', 'lt', 'gt'];

	/**
	 * Keeps timestamp of last generated unique identifier.
	 * @type {Number}
	 */
	var UID_TIME_LAST = 0;

	/**
	 * Keeps time difference between last generated unique identifiers.
	 * @type {Number}
	 */
	var UID_TIME_DIFF = 0;

	/**
	 * Unique identifier prefix (see uniqueId).
	 * @type {string}
	 * @const
	 */
	var UNIQUE_IDENT_PREFIX = 'uip$';

	/**
	 * Returns true if the value is undefined.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is undefined.
	 */
	exports.isUndefined = function(value) {
		return (typeof(value) === 'undefined');
	};

	/**
	 * Returns true if the value is null.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is null.
	 */
	exports.isNull = function(value) {
		return (value === null);
	};

	/**
	 * Returns true if the value is boolean.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is boolean.
	 */
	exports.isBoolean = function(value) {
		return (typeof(value) === 'boolean');
	};

	/**
	 * Returns true if the value is number.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is number.
	 */
	exports.isNumber = function(value) {
		return (typeof(value) === 'number');
	};

	/**
	 * Returns true if the value is string.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is string.
	 */
	exports.isString = function(value) {
		return (typeof(value) === 'string');
	};

	/**
	 * Returns true if the value is array.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is array.
	 */
	exports.isArray = function(value) {
		return (isObject(value) && value.constructor === Array);
	};

	/**
	 * Returns true if the value is object.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is object.
	 */
	exports.isObject = function(value) {
		return (!isNull(value) && typeof(value) === 'object');
	};

	/**
	 * Returns true if the value is function.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is function.
	 */
	exports.isFunction = function(value) {
		return (typeof(value) === 'function');
	};

	/**
	 * Returns true if the value is E4X XML object.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is E4X XML object.
	 */
	exports.isXML = function(value) {
		return (typeof(value) === 'xml');
	};

	/**
	 * Returns true if the value is RegExp instance.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is RegExp instance.
	 */
	exports.isRegExp = function(value) {
		return (value instanceof RegExp);
	};

	/**
	 * Retrieves the list of function arguments.
	 * @param {Function} value Function object to process.
	 * @return {Array.<string>} Array of function arguments names.
	 */
	exports.getFunctionArguments = function(value) {
		if (!isFunction(value)) return [];
		var functionArity = value.length;
		var functionValue = value.toString();
		var functionArguments = functionValue.split(')', 2).shift();
		functionArguments = functionArguments.split('(', 2).pop();
		return functionArguments.split(/\s?,\s?/, functionArity);
	};

	/**
	 * Extracts function body from the function object
	 * and returns it as a string.
	 * @param {Function} value Function object to process.
	 * @return {string} Function body as a string.
	 */
	exports.getFunctionBody = function(value) {
		if (!isFunction(value)) return '';
		var functionBody = value.toString();
		functionBody = functionBody.split('{').slice(1).join('{');
		functionBody = functionBody.split('}').slice(0, -1).join('}');
		functionBody = functionBody.replace(/^\s+/, '').replace(/\s+$/, '');
		functionBody = functionBody.replace(/;$/, '');
		functionBody = ('\r\n' + functionBody + '\r\n');
		return functionBody;
	};

	/**
	 * Sets function arguments names.
	 * @param {Function} value Function object to process.
	 * @param {Array.<string>} args Array with argument names.
	 * @return {Function} New function object with updated arguments list.
	 */
	exports.setFunctionArguments = function(value, args) {
		if (!isFunction(value)) value = new Function();
		if (isUndefined(args)) argumentsList = [];
		if (!isArray(args)) argumentsList = [args];
		var functionBody = getFunctionBody(value);
		return new Function(args, functionBody);
	};

	/**
	 * Converts arguments object into array of arguments.
	 * @param {Object} args Arguments object to process.
	 * @param {number=} index First argument offset.
	 * @return {Array.<*>} Arguments array.
	 */
	exports.args2arr = function(args, index) {
		return Array.prototype.slice.call(args, index || 0);
	};

	/**
	 * Generates unique identifier based on UNIX timestamp.
	 * @return {string} Unique JavaScript identifier.
	 */
	exports.uniqueId = function(prefix) {
		if (!isString(prefix)) prefix = '';
		if (!prefix) prefix = UNIQUE_IDENT_PREFIX;
		var now = new Date().getTime();
		if (now > UID_TIME_LAST) UID_TIME_DIFF = 0;
		else now += (++UID_TIME_DIFF);
		UID_TIME_LAST = now;
		return (prefix + now.toString(36));
	};

	/**
	 * Formats the value using js_beautify.
	 * @param {*} value Value to format.
	 * @return {string} Formatted value.
	 */
	exports.beautify = function(value) {
		if (isXML(value)) return value.toXMLString();
		return (require('Beautify').js_beautify(
			isFunction(value) ?
			value.toString() :
			String(JSON.stringify(value))
		));
	};

	/**
	 * Beautifies the value and prints it.
	 * Shortcut for print(beautify(JSON.parse(value))).
	 * @param {*} value Value to print.
	 */
	exports.print = function(value) {
		var args = args2arr(arguments);
		args = args.map(beautify);
		SYSTEM_PRINT.apply(this, args);
	};

	/**
	 * Converts value to XML string.
	 * @param {*} value Value to convert.
	 * @return {string} Returns value converted to XML string.
	 */
	exports.value2xml = function(value) {
		return ('<root>' + (isObject(value) ? function(value) {
			var result = '';
			if (isArray(value)) {
				for (var c = 0; c < value.length; c++) {
					result += '<item index="' + c + '">';
					result += arguments.callee(value[c]);
					result += '</item>';
				}
			} else if (isObject(value)) {
				for (var key in value) {
					if (value.hasOwnProperty(key)) {
						result += '<' + key + '>';
						result += arguments.callee(value[key]);
						result += '</' + key + '>';
					}
				}
			} else {
				result = String(value || '');
			}
			return result;
		}(value) : value || '') + '</root>');
	};

	/**
	 * Preserves non - XML entities in the string.
	 * @param {string} value XML string.
	 * @return {string} Returns XML string with all undefined XML entities
	 * converted to their safe equivalents (& prefixed).
	 */
	exports.preserveEntities = function(value) {
		return value.replace(
			ENTITY_REGEXP, function(entity, name) {
				// skip predefined XML entities
				if (XML_ENTITIES.indexOf(name) !== -1) {
					return entity;
				} else {
					return ('&' + name + ';');
				}
			}
		);
	};

	/**
	 * Gets the value of the specified environment variable.
	 * @param {string | Object} name Environment variable name,
	 *     passed as string or RegExp object.
	 * @return {string | Object} Value as a string or map.
	 */
	exports.getEnv = function(name) {
		if (isString(name)) {
			var value = java.lang.System.getenv(name);
			return (value ? String(value) : null);
		}
		var result = {};
		var regExp = isRegExp(name);
		var value = java.lang.System.getenv();
		var keySet = value.keySet().toArray();
		for (var c = 0; c < keySet.length; c++) {
			var key = String(keySet[c]);
			if (regExp) {
				var matches = key.match(name);
				if (!matches) continue;
				var val = String(value.get(key));
				result[matches[1] || key] = val;
			} else {
				result[key] = String(value.get(key));
			}
		}
		return result;
	};

	/**
	 * Gets the value of the specified system property.
	 * @param {string | Object} name System property name,
	 *     passed as string or RegExp object.
	 * @return {string | Object} Value as a string or map.
	 */
	exports.getProp = function(name) {
		if (isString(name)) {
			var value = java.lang.System.getProperty(name);
			return (value ? String(value) : null);
		}
		var result = {};
		var regExp = isRegExp(name);
		var value = java.lang.System.getProperties();
		var keySet = value.keySet().toArray();
		for (var c = 0; c < keySet.length; c++) {
			var key = String(keySet[c]);
			if (regExp) {
				var matches = key.match(name);
				if (!matches) continue;
				var val = String(value.get(key));
				result[matches[1] || key] = val;
			} else {
				result[key] = String(value.get(key));
			}
		}
		return result;
	};

}<commit_msg>fixing Utils.setFunctionArguments<commit_after>with (exports) {

	/**
	 * Reference to the system print function.
	 * @type {Function}
	 * @const
	 */
	var SYSTEM_PRINT = print;

	/**
	 * Regular expression matches XML entity reference.
	 * @type {Object}
	 * @const
	 */
	var ENTITY_REGEXP = /&([a-z]+);/ig;

	/**
	 * List of predefined XML entities.
	 * @type {Array.<string>}
	 * @const
	 */
	var XML_ENTITIES = ['quot', 'amp', 'apos', 'lt', 'gt'];

	/**
	 * Keeps timestamp of last generated unique identifier.
	 * @type {Number}
	 */
	var UID_TIME_LAST = 0;

	/**
	 * Keeps time difference between last generated unique identifiers.
	 * @type {Number}
	 */
	var UID_TIME_DIFF = 0;

	/**
	 * Unique identifier prefix (see uniqueId).
	 * @type {string}
	 * @const
	 */
	var UNIQUE_IDENT_PREFIX = 'uip$';

	/**
	 * Returns true if the value is undefined.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is undefined.
	 */
	exports.isUndefined = function(value) {
		return (typeof(value) === 'undefined');
	};

	/**
	 * Returns true if the value is null.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is null.
	 */
	exports.isNull = function(value) {
		return (value === null);
	};

	/**
	 * Returns true if the value is boolean.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is boolean.
	 */
	exports.isBoolean = function(value) {
		return (typeof(value) === 'boolean');
	};

	/**
	 * Returns true if the value is number.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is number.
	 */
	exports.isNumber = function(value) {
		return (typeof(value) === 'number');
	};

	/**
	 * Returns true if the value is string.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is string.
	 */
	exports.isString = function(value) {
		return (typeof(value) === 'string');
	};

	/**
	 * Returns true if the value is array.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is array.
	 */
	exports.isArray = function(value) {
		return (isObject(value) && value.constructor === Array);
	};

	/**
	 * Returns true if the value is object.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is object.
	 */
	exports.isObject = function(value) {
		return (!isNull(value) && typeof(value) === 'object');
	};

	/**
	 * Returns true if the value is function.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is function.
	 */
	exports.isFunction = function(value) {
		return (typeof(value) === 'function');
	};

	/**
	 * Returns true if the value is E4X XML object.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is E4X XML object.
	 */
	exports.isXML = function(value) {
		return (typeof(value) === 'xml');
	};

	/**
	 * Returns true if the value is RegExp instance.
	 * @param {*} value Value to check.
	 * @return {boolean} Returns true if the value is RegExp instance.
	 */
	exports.isRegExp = function(value) {
		return (value instanceof RegExp);
	};

	/**
	 * Retrieves the list of function arguments.
	 * @param {Function} value Function object to process.
	 * @return {Array.<string>} Array of function arguments names.
	 */
	exports.getFunctionArguments = function(value) {
		if (!isFunction(value)) return [];
		var functionArity = value.length;
		var functionValue = value.toString();
		var functionArguments = functionValue.split(')', 2).shift();
		functionArguments = functionArguments.split('(', 2).pop();
		return functionArguments.split(/\s?,\s?/, functionArity);
	};

	/**
	 * Extracts function body from the function object
	 * and returns it as a string.
	 * @param {Function} value Function object to process.
	 * @return {string} Function body as a string.
	 */
	exports.getFunctionBody = function(value) {
		if (!isFunction(value)) return '';
		var functionBody = value.toString();
		functionBody = functionBody.split('{').slice(1).join('{');
		functionBody = functionBody.split('}').slice(0, -1).join('}');
		functionBody = functionBody.replace(/^\s+/, '').replace(/\s+$/, '');
		functionBody = functionBody.replace(/;$/, '');
		functionBody = ('\r\n' + functionBody + '\r\n');
		return functionBody;
	};

	/**
	 * Sets function arguments names.
	 * @param {Function} value Function object to process.
	 * @param {Array.<string>} args Array with argument names.
	 * @return {Function} New function object with updated arguments list.
	 */
	exports.setFunctionArguments = function(value, args) {
		if (!isFunction(value)) value = new Function();
		if (isUndefined(args)) argumentsList = [];
		if (!isArray(args)) argumentsList = [args];
		var functionBody = getFunctionBody(value);
		return new Function(argumentsList, functionBody);
	};

	/**
	 * Converts arguments object into array of arguments.
	 * @param {Object} args Arguments object to process.
	 * @param {number=} index First argument offset.
	 * @return {Array.<*>} Arguments array.
	 */
	exports.args2arr = function(args, index) {
		return Array.prototype.slice.call(args, index || 0);
	};

	/**
	 * Generates unique identifier based on UNIX timestamp.
	 * @return {string} Unique JavaScript identifier.
	 */
	exports.uniqueId = function(prefix) {
		if (!isString(prefix)) prefix = '';
		if (!prefix) prefix = UNIQUE_IDENT_PREFIX;
		var now = new Date().getTime();
		if (now > UID_TIME_LAST) UID_TIME_DIFF = 0;
		else now += (++UID_TIME_DIFF);
		UID_TIME_LAST = now;
		return (prefix + now.toString(36));
	};

	/**
	 * Formats the value using js_beautify.
	 * @param {*} value Value to format.
	 * @return {string} Formatted value.
	 */
	exports.beautify = function(value) {
		if (isXML(value)) return value.toXMLString();
		return (require('Beautify').js_beautify(
			isFunction(value) ?
			value.toString() :
			String(JSON.stringify(value))
		));
	};

	/**
	 * Beautifies the value and prints it.
	 * Shortcut for print(beautify(JSON.parse(value))).
	 * @param {*} value Value to print.
	 */
	exports.print = function(value) {
		var args = args2arr(arguments);
		args = args.map(beautify);
		SYSTEM_PRINT.apply(this, args);
	};

	/**
	 * Converts value to XML string.
	 * @param {*} value Value to convert.
	 * @return {string} Returns value converted to XML string.
	 */
	exports.value2xml = function(value) {
		return ('<root>' + (isObject(value) ? function(value) {
			var result = '';
			if (isArray(value)) {
				for (var c = 0; c < value.length; c++) {
					result += '<item index="' + c + '">';
					result += arguments.callee(value[c]);
					result += '</item>';
				}
			} else if (isObject(value)) {
				for (var key in value) {
					if (value.hasOwnProperty(key)) {
						result += '<' + key + '>';
						result += arguments.callee(value[key]);
						result += '</' + key + '>';
					}
				}
			} else {
				result = String(value || '');
			}
			return result;
		}(value) : value || '') + '</root>');
	};

	/**
	 * Preserves non - XML entities in the string.
	 * @param {string} value XML string.
	 * @return {string} Returns XML string with all undefined XML entities
	 * converted to their safe equivalents (& prefixed).
	 */
	exports.preserveEntities = function(value) {
		return value.replace(
			ENTITY_REGEXP, function(entity, name) {
				// skip predefined XML entities
				if (XML_ENTITIES.indexOf(name) !== -1) {
					return entity;
				} else {
					return ('&' + name + ';');
				}
			}
		);
	};

	/**
	 * Gets the value of the specified environment variable.
	 * @param {string | Object} name Environment variable name,
	 *     passed as string or RegExp object.
	 * @return {string | Object} Value as a string or map.
	 */
	exports.getEnv = function(name) {
		if (isString(name)) {
			var value = java.lang.System.getenv(name);
			return (value ? String(value) : null);
		}
		var result = {};
		var regExp = isRegExp(name);
		var value = java.lang.System.getenv();
		var keySet = value.keySet().toArray();
		for (var c = 0; c < keySet.length; c++) {
			var key = String(keySet[c]);
			if (regExp) {
				var matches = key.match(name);
				if (!matches) continue;
				var val = String(value.get(key));
				result[matches[1] || key] = val;
			} else {
				result[key] = String(value.get(key));
			}
		}
		return result;
	};

	/**
	 * Gets the value of the specified system property.
	 * @param {string | Object} name System property name,
	 *     passed as string or RegExp object.
	 * @return {string | Object} Value as a string or map.
	 */
	exports.getProp = function(name) {
		if (isString(name)) {
			var value = java.lang.System.getProperty(name);
			return (value ? String(value) : null);
		}
		var result = {};
		var regExp = isRegExp(name);
		var value = java.lang.System.getProperties();
		var keySet = value.keySet().toArray();
		for (var c = 0; c < keySet.length; c++) {
			var key = String(keySet[c]);
			if (regExp) {
				var matches = key.match(name);
				if (!matches) continue;
				var val = String(value.get(key));
				result[matches[1] || key] = val;
			} else {
				result[key] = String(value.get(key));
			}
		}
		return result;
	};

}<|endoftext|>


<commit_before>'use strict';

var chai = require('chai')
  , expect = chai.expect
  , resolver = require('../src/resolver')
  , helper = require('./helper')
  , sequelize = helper.sequelize
  , Sequelize = require('sequelize')
  , Promise = helper.Promise
  , attributeFields = require('../src/attributeFields');

import {
  GraphQLString,
  GraphQLInt,
  GraphQLFloat,
  GraphQLNonNull,
  GraphQLBoolean,
  GraphQLEnumType,
  GraphQLList,
  GraphQLObjectType,
  GraphQLSchema,
  graphql
} from 'graphql';

import {
  sequelizeNodeInterface
} from '../src/relay';

import {
  globalIdField,
  toGlobalId,
  connectionDefinitions,
  connectionArgs,
  connectionFromArray
} from 'graphql-relay';

function generateTask(id) {
  return {
    id: id,
    name: Math.random().toString()
  }
}

describe('relay', function () {
  var User
    , Task
    , userType
    , taskType
    , taskConnection
    , nodeInterface
    , Project
    , projectType
    , userConnection
    , nodeField
    , schema;

  before(function () {
    sequelize.modelManager.models = [];
    sequelize.models = {};
    User = sequelize.define('user', {
      name: {
        type: Sequelize.STRING
      }
    }, {
      timestamps: false
    });

    Task = sequelize.define('task', {
      name: {
        type: Sequelize.STRING
      }
    }, {
      timestamps: false
    });

    Project = sequelize.define('project', {
      name: {
        type: Sequelize.STRING
      }
    }, {
      timestamps: false
    });

    User.Tasks = User.hasMany(Task, {as: 'tasks'});
    Project.Users = Project.hasMany(User, {as: 'users'});

    var node = sequelizeNodeInterface(sequelize);
    nodeInterface = node.nodeInterface;
    nodeField = node.nodeField;
    var nodeTypeMapper = node.nodeTypeMapper;

    taskType = new GraphQLObjectType({
      name: 'Task',
      fields: {
        id: globalIdField('Task'),
        name: {
          type: GraphQLString
        }
      },
      interfaces: [nodeInterface]
    });

    var taskConnection = connectionDefinitions({name: 'Task', nodeType: taskType});

    userType = new GraphQLObjectType({
      name: 'User',
      fields: {
        id: globalIdField('User'),
        name: {
          type: GraphQLString
        },
        tasks: {
          type: taskConnection.connectionType,
          args: {
            test: {
              type: GraphQLBoolean
            }
          },
          resolve: resolver(User.Tasks)
        }
      },
      interfaces: [nodeInterface]
    });

    var userConnection = connectionDefinitions({name: 'User', nodeType: userType});

    projectType = new GraphQLObjectType({
      name: 'Project',
      fields: {
        id: globalIdField('User'),
        name: {
          type: GraphQLString
        },
        users: {
          type: userConnection.connectionType,
          args: connectionArgs,
          resolve: resolver(Project.Users)
        }
      }
    });

    nodeTypeMapper.mapTypes({
      [User.name]: userType,
      [Project.name]: projectType,
      [Task.name]: taskType
    });

    schema = new GraphQLSchema({
      query: new GraphQLObjectType({
        name: 'RootQueryType',
        fields: {
          user: {
            type: userType,
            args: {
              id: {
                type: new GraphQLNonNull(GraphQLInt)
              }
            },
            resolve: resolver(User)
          },
          users: {
            type: new GraphQLList(userType),
            args: {
              limit: {
                type: GraphQLInt
              },
              order: {
                type: GraphQLString
              }
            },
            resolve: resolver(User)
          },
          project: {
            type: projectType,
            args: {
              id: {
                type: new GraphQLNonNull(GraphQLInt)
              }
            },
            resolve: resolver(Project)
          },
          node: nodeField
        }
      })
    });

  });

  before(function () {
    var userId = 1
      , projectId = 1
      , taskId = 1;

    return this.sequelize.sync({force: true}).bind(this).then(function () {
      return Promise.join(
        Project.create({
          id: projectId++,
          name: 'project-' + Math.random().toString()
        }),
        User.create({
          id: userId++,
          name: 'a' + Math.random().toString(),
          tasks: [generateTask(taskId++), generateTask(taskId++), generateTask(taskId++)]
        }, {
          include: [User.Tasks]
        }),
        User.create({
          id: userId++,
          name: 'b' + Math.random().toString(),
          tasks: [generateTask(taskId++), generateTask(taskId++)]
        }, {
          include: [User.Tasks]
        })
      ).bind(this).spread(function (project, userA, userB) {
          this.project = project;
          this.userA = userA;
          this.userB = userB;
          this.users = [userA, userB];
        });
    });
  });

  before(function () {
    return this.project.setUsers([this.userA.id, this.userB.id]);
  });

  it('should return userA when running a node query', function() {
    var user = this.userA;

    var globalId = toGlobalId('User', user.id);

    return graphql(schema, `
      {
        node(id: "${globalId}") {
          id
          ... on User {
            name
          }
        }
      }
    `).then(result => {
      expect(result.data.node.id).to.equal(globalId);
      expect(result.data.node.name).to.equal(user.name);
    });

  });

  it('should resolve a plain result with a single connection', function () {
    var user = this.userB;

    return graphql(schema, `
      {
        user(id: ${user.id}) {
          name
          tasks {
            edges {
              node {
                name
              }
            }
          }
        }
      }
    `).then(function (result) {
      if (result.errors) throw new Error(result.errors[0].stack);

      expect(result.data).to.deep.equal({
        user: {
          name: user.name,
          tasks: {
            edges: [
              {
                node: {
                  name: user.tasks[0].name
                }
              },
              {
                node: {
                  name: user.tasks[1].name
                }
              }
            ]
          }
        }
      });
    });
  });

  it('should resolve an array of objects containing connections', function () {
    var users = this.users;

    return graphql(schema, `
      {
        users {
          name
          tasks {
            edges {
              node {
                name
              }
            }
          }
        }
      }
    `).then(function (result) {
      if (result.errors) throw new Error(result.errors[0].stack);

      expect(result.data.users.length).to.equal(users.length);
      result.data.users.forEach(function (user) {
        expect(user.tasks.edges).to.have.length.above(0);
      });

    });
  });

  it('should resolve nested connections', function () {
    var project = this.project;

    return graphql(schema, `
      {
        project(id: 1) {
          users {
            edges {
              node {
                name
                tasks(test: true) {
                  edges {
                    node {
                      name
                    }
                  }
                }
              }
            }
          }
        }
      }
    `).then(result => {
      if (result.errors) throw new Error(result.errors[0].stack);

      expect(result.data.project.users.edges).to.have.length(2);
      let [nodeA, nodeB] = result.data.project.users.edges;
      let userA = nodeA.node;
      let userB = nodeB.node;
      expect(userA).to.have.property('tasks');
      expect(userA.tasks.edges).to.have.length.above(0);
      expect(userB).to.have.property('tasks');
      expect(userB.tasks.edges).to.have.length.above(0);
    });
  });

});
<commit_msg>References #21 - adding tests for before/after first/last<commit_after>'use strict';

var chai = require('chai')
  , expect = chai.expect
  , resolver = require('../src/resolver')
  , helper = require('./helper')
  , sequelize = helper.sequelize
  , Sequelize = require('sequelize')
  , Promise = helper.Promise
  , attributeFields = require('../src/attributeFields');

import {
  GraphQLString,
  GraphQLInt,
  GraphQLFloat,
  GraphQLNonNull,
  GraphQLBoolean,
  GraphQLEnumType,
  GraphQLList,
  GraphQLObjectType,
  GraphQLSchema,
  graphql
} from 'graphql';

import {
  sequelizeNodeInterface
} from '../src/relay';

import {
  globalIdField,
  toGlobalId,
  connectionDefinitions,
  connectionArgs,
  connectionFromArray
} from 'graphql-relay';

function generateTask(id) {
  return {
    id: id,
    name: Math.random().toString()
  }
}

describe('relay', function () {
  var User
    , Task
    , userType
    , taskType
    , taskConnection
    , nodeInterface
    , Project
    , projectType
    , userConnection
    , nodeField
    , schema;

  before(function () {
    sequelize.modelManager.models = [];
    sequelize.models = {};
    User = sequelize.define('user', {
      name: {
        type: Sequelize.STRING
      }
    }, {
      timestamps: false
    });

    Task = sequelize.define('task', {
      name: {
        type: Sequelize.STRING
      }
    }, {
      timestamps: false
    });

    Project = sequelize.define('project', {
      name: {
        type: Sequelize.STRING
      }
    }, {
      timestamps: false
    });

    User.Tasks = User.hasMany(Task, {as: 'tasks'});
    Project.Users = Project.hasMany(User, {as: 'users'});

    var node = sequelizeNodeInterface(sequelize);
    nodeInterface = node.nodeInterface;
    nodeField = node.nodeField;
    var nodeTypeMapper = node.nodeTypeMapper;

    taskType = new GraphQLObjectType({
      name: 'Task',
      fields: {
        id: globalIdField('Task'),
        name: {
          type: GraphQLString
        }
      },
      interfaces: [nodeInterface]
    });

    var taskConnection = connectionDefinitions({name: 'Task', nodeType: taskType});

    userType = new GraphQLObjectType({
      name: 'User',
      fields: {
        id: globalIdField('User'),
        name: {
          type: GraphQLString
        },
        tasks: {
          type: taskConnection.connectionType,
          args: connectionArgs,
          resolve: resolver(User.Tasks)
        }
      },
      interfaces: [nodeInterface]
    });

    var userConnection = connectionDefinitions({name: 'User', nodeType: userType});

    projectType = new GraphQLObjectType({
      name: 'Project',
      fields: {
        id: globalIdField('User'),
        name: {
          type: GraphQLString
        },
        users: {
          type: userConnection.connectionType,
          args: connectionArgs,
          resolve: resolver(Project.Users)
        }
      }
    });

    nodeTypeMapper.mapTypes({
      [User.name]: userType,
      [Project.name]: projectType,
      [Task.name]: taskType
    });

    schema = new GraphQLSchema({
      query: new GraphQLObjectType({
        name: 'RootQueryType',
        fields: {
          user: {
            type: userType,
            args: {
              id: {
                type: new GraphQLNonNull(GraphQLInt)
              }
            },
            resolve: resolver(User)
          },
          users: {
            type: new GraphQLList(userType),
            args: {
              limit: {
                type: GraphQLInt
              },
              order: {
                type: GraphQLString
              }
            },
            resolve: resolver(User)
          },
          project: {
            type: projectType,
            args: {
              id: {
                type: new GraphQLNonNull(GraphQLInt)
              }
            },
            resolve: resolver(Project)
          },
          node: nodeField
        }
      })
    });

  });

  before(function () {
    var userId = 1
      , projectId = 1
      , taskId = 1;

    return this.sequelize.sync({force: true}).bind(this).then(function () {
      return Promise.join(
        Project.create({
          id: projectId++,
          name: 'project-' + Math.random().toString()
        }),
        User.create({
          id: userId++,
          name: 'a' + Math.random().toString(),
          tasks: [generateTask(taskId++), generateTask(taskId++), generateTask(taskId++)]
        }, {
          include: [User.Tasks]
        }),
        User.create({
          id: userId++,
          name: 'b' + Math.random().toString(),
          tasks: [generateTask(taskId++), generateTask(taskId++)]
        }, {
          include: [User.Tasks]
        })
      ).bind(this).spread(function (project, userA, userB) {
          this.project = project;
          this.userA = userA;
          this.userB = userB;
          this.users = [userA, userB];
        });
    });
  });

  before(function () {
    return this.project.setUsers([this.userA.id, this.userB.id]);
  });

  it('should return userA when running a node query', function() {
    var user = this.userA;

    var globalId = toGlobalId('User', user.id);

    return graphql(schema, `
      {
        node(id: "${globalId}") {
          id
          ... on User {
            name
          }
        }
      }
    `).then(result => {
      expect(result.data.node.id).to.equal(globalId);
      expect(result.data.node.name).to.equal(user.name);
    });

  });

  it('should support first queries on connections', function() {
    var user = this.userB;

    return graphql(schema, `
      {
        user(id: ${user.id}) {
          name
          tasks(first: 1) {
            edges {
              node {
                name
              }
            }
          }
        }
      }
    `).then(function (result) {
      if (result.errors) throw new Error(result.errors[0].stack);

      expect(result.data).to.deep.equal({
        user: {
          name: user.name,
          tasks: {
            edges: [
              {
                node: {
                  name: user.tasks[0].name
                }
              }
            ]
          }
        }
      });
    });
  });

  it('should support last queries on connections', function() {
    var user = this.userB;

    return graphql(schema, `
      {
        user(id: ${user.id}) {
          name
          tasks(last: 1) {
            edges {
              node {
                name
              }
            }
          }
        }
      }
    `).then(function (result) {
      if (result.errors) throw new Error(result.errors[0].stack);

      expect(result.data).to.deep.equal({
        user: {
          name: user.name,
          tasks: {
            edges: [
              {
                node: {
                  name: user.tasks[user.tasks.length - 1].name
                }
              }
            ]
          }
        }
      });
    });
  });

  it('should support after queries on connections', function() {
    var user = this.userA;

    return graphql(schema, `
      {
        user(id: ${user.id}) {
          name
          tasks(first: 1) {
            pageInfo {
              hasNextPage,
              startCursor
            },
            edges {
              node {
                name
              }
            }
          }
        }
      }
    `)
    .then(function (result) {
      return graphql(schema, `
        {
          user(id: ${user.id}) {
            name
            tasks(first: 1, after: "${result.data.user.tasks.pageInfo.startCursor}") {
              edges {
                node {
                  name
                }
              }
            }
          }
        }
      `)
    })
    .then(function (result) {
      expect(result.data.user.tasks.edges[0].node.name).to.equal(user.tasks[1].name);
    });
  });

  it('should support before queries on connections', function() {
    var user = this.userA;

    return graphql(schema, `
      {
        user(id: ${user.id}) {
          name
          tasks(last: 1) {
            pageInfo {
              hasNextPage,
              endCursor
            },
            edges {
              node {
                name
              }
            }
          }
        }
      }
    `)
      .then(function (result) {
        return graphql(schema, `
        {
          user(id: ${user.id}) {
            name
            tasks(last: 1, before: "${result.data.user.tasks.pageInfo.endCursor}") {
              edges {
                node {
                  name
                }
              }
            }
          }
        }
      `)
      })
      .then(function (result) {
        expect(result.data.user.tasks.edges[0].node.name).to.equal(user.tasks[1].name);
      });
  });

  it('should resolve a plain result with a single connection', function () {
    var user = this.userB;

    return graphql(schema, `
      {
        user(id: ${user.id}) {
          name
          tasks {
            edges {
              node {
                name
              }
            }
          }
        }
      }
    `).then(function (result) {
      if (result.errors) throw new Error(result.errors[0].stack);

      expect(result.data).to.deep.equal({
        user: {
          name: user.name,
          tasks: {
            edges: [
              {
                node: {
                  name: user.tasks[0].name
                }
              },
              {
                node: {
                  name: user.tasks[1].name
                }
              }
            ]
          }
        }
      });
    });
  });

  it('should resolve an array of objects containing connections', function () {
    var users = this.users;

    return graphql(schema, `
      {
        users {
          name
          tasks {
            edges {
              node {
                name
              }
            }
          }
        }
      }
    `).then(function (result) {
      if (result.errors) throw new Error(result.errors[0].stack);

      expect(result.data.users.length).to.equal(users.length);
      result.data.users.forEach(function (user) {
        expect(user.tasks.edges).to.have.length.above(0);
      });

    });
  });

  it('should resolve nested connections', function () {
    var project = this.project;

    return graphql(schema, `
      {
        project(id: 1) {
          users {
            edges {
              node {
                name
                tasks {
                  edges {
                    node {
                      name
                    }
                  }
                }
              }
            }
          }
        }
      }
    `).then(result => {
      if (result.errors) throw new Error(result.errors[0].stack);

      expect(result.data.project.users.edges).to.have.length(2);
      let [nodeA, nodeB] = result.data.project.users.edges;
      let userA = nodeA.node;
      let userB = nodeB.node;
      expect(userA).to.have.property('tasks');
      expect(userA.tasks.edges).to.have.length.above(0);
      expect(userB).to.have.property('tasks');
      expect(userB.tasks.edges).to.have.length.above(0);
    });
  });

});
<|endoftext|>


<commit_before>module.exports = {
  init,
  onPlayerClose,
  onPlayerOpen,
  onToggleAlwaysOnTop,
  onToggleFullScreen,
  onWindowBlur,
  onWindowFocus
}

var electron = require('electron')

var app = electron.app

var config = require('../config')
var dialog = require('./dialog')
var shell = require('./shell')
var windows = require('./windows')

var menu

function init () {
  menu = electron.Menu.buildFromTemplate(getMenuTemplate())
  electron.Menu.setApplicationMenu(menu)
}

function onPlayerClose () {
  getMenuItem('Play/Pause').enabled = false
  getMenuItem('Increase Volume').enabled = false
  getMenuItem('Decrease Volume').enabled = false
  getMenuItem('Step Forward').enabled = false
  getMenuItem('Step Backward').enabled = false
  getMenuItem('Increase Speed').enabled = false
  getMenuItem('Decrease Speed').enabled = false
  getMenuItem('Add Subtitles File...').enabled = false
}

function onPlayerOpen () {
  getMenuItem('Play/Pause').enabled = true
  getMenuItem('Increase Volume').enabled = true
  getMenuItem('Decrease Volume').enabled = true
  getMenuItem('Step Forward').enabled = true
  getMenuItem('Step Backward').enabled = true
  getMenuItem('Increase Speed').enabled = true
  getMenuItem('Decrease Speed').enabled = true
  getMenuItem('Add Subtitles File...').enabled = true
}

function onToggleAlwaysOnTop (flag) {
  getMenuItem('Float on Top').checked = flag
}

function onToggleFullScreen (flag) {
  getMenuItem('Full Screen').checked = flag
}

function onWindowBlur () {
  getMenuItem('Full Screen').enabled = false
  getMenuItem('Float on Top').enabled = false
}

function onWindowFocus () {
  getMenuItem('Full Screen').enabled = true
  getMenuItem('Float on Top').enabled = true
}

function getMenuItem (label) {
  for (var i = 0; i < menu.items.length; i++) {
    var menuItem = menu.items[i].submenu.items.find(function (item) {
      return item.label === label
    })
    if (menuItem) return menuItem
  }
}

function getMenuTemplate () {
  var template = [
    {
      label: 'File',
      submenu: [
        {
          label: process.platform === 'darwin'
            ? 'Create New Torrent...'
            : 'Create New Torrent from Folder...',
          accelerator: 'CmdOrCtrl+N',
          click: () => dialog.openSeedDirectory()
        },
        {
          label: 'Open Torrent File...',
          accelerator: 'CmdOrCtrl+O',
          click: () => dialog.openTorrentFile()
        },
        {
          label: 'Open Torrent Address...',
          accelerator: 'CmdOrCtrl+U',
          click: () => dialog.openTorrentAddress()
        },
        {
          type: 'separator'
        },
        {
          role: 'close'
        }
      ]
    },
    {
      label: 'Edit',
      submenu: [
        {
          role: 'undo'
        },
        {
          role: 'redo'
        },
        {
          type: 'separator'
        },
        {
          role: 'cut'
        },
        {
          role: 'copy'
        },
        {
          label: 'Paste Torrent Address',
          role: 'paste'
        },
        {
          role: 'delete'
        },
        {
          role: 'selectall'
        },
        {
          type: 'separator'
        },
        {
          label: 'Preferences',
          accelerator: 'CmdOrCtrl+,',
          click: () => windows.main.dispatch('preferences')
        }
      ]
    },
    {
      label: 'View',
      submenu: [
        {
          label: 'Full Screen',
          type: 'checkbox',
          accelerator: process.platform === 'darwin'
            ? 'Ctrl+Command+F'
            : 'F11',
          click: () => windows.main.toggleFullScreen()
        },
        {
          label: 'Float on Top',
          type: 'checkbox',
          click: () => windows.main.toggleAlwaysOnTop()
        },
        {
          type: 'separator'
        },
        {
          label: 'Go Back',
          accelerator: 'Esc',
          click: () => windows.main.dispatch('escapeBack')
        },
        {
          type: 'separator'
        },
        {
          label: 'Developer',
          submenu: [
            {
              label: 'Developer Tools',
              accelerator: process.platform === 'darwin'
                ? 'Alt+Command+I'
                : 'Ctrl+Shift+I',
              click: () => windows.main.toggleDevTools()
            },
            {
              label: 'Show WebTorrent Process',
              accelerator: process.platform === 'darwin'
                ? 'Alt+Command+P'
                : 'Ctrl+Shift+P',
              click: () => windows.webtorrent.toggleDevTools()
            }
          ]
        }
      ]
    },
    {
      label: 'Playback',
      submenu: [
        {
          label: 'Play/Pause',
          accelerator: 'Space',
          click: () => windows.main.dispatch('playPause'),
          enabled: false
        },
        {
          type: 'separator'
        },
        {
          label: 'Increase Volume',
          accelerator: 'CmdOrCtrl+Up',
          click: () => windows.main.dispatch('changeVolume', 0.1),
          enabled: false
        },
        {
          label: 'Decrease Volume',
          accelerator: 'CmdOrCtrl+Down',
          click: () => windows.main.dispatch('changeVolume', -0.1),
          enabled: false
        },
        {
          type: 'separator'
        },
        {
          label: 'Step Forward',
          accelerator: process.platform === 'darwin'
            ? 'CmdOrCtrl+Alt+Right'
            : 'Alt+Right',
          click: () => windows.main.dispatch('skip', 10),
          enabled: false
        },
        {
          label: 'Step Backward',
          accelerator: process.platform === 'darwin'
            ? 'CmdOrCtrl+Alt+Left'
            : 'Alt+Left',
          click: () => windows.main.dispatch('skip', -10),
          enabled: false
        },
        {
          type: 'separator'
        },
        {
          label: 'Increase Speed',
          accelerator: 'CmdOrCtrl+=',
          click: () => windows.main.dispatch('changePlaybackRate', 1),
          enabled: false
        },
        {
          label: 'Decrease Speed',
          accelerator: 'CmdOrCtrl+-',
          click: () => windows.main.dispatch('changePlaybackRate', -1),
          enabled: false
        },
        {
          type: 'separator'
        },
        {
          label: 'Add Subtitles File...',
          click: () => windows.main.dispatch('openSubtitles'),
          enabled: false
        }
      ]
    },
    {
      label: 'Help',
      role: 'help',
      submenu: [
        {
          label: 'Learn more about ' + config.APP_NAME,
          click: () => shell.openExternal(config.HOME_PAGE_URL)
        },
        {
          label: 'Contribute on GitHub',
          click: () => shell.openExternal(config.GITHUB_URL)
        },
        {
          type: 'separator'
        },
        {
          label: 'Report an Issue...',
          click: () => shell.openExternal(config.GITHUB_URL_ISSUES)
        }
      ]
    }
  ]

  if (process.platform === 'darwin') {
    // Add WebTorrent app menu (OS X)
    template.unshift({
      label: config.APP_NAME,
      submenu: [
        {
          role: 'about'
        },
        {
          type: 'separator'
        },
        {
          label: 'Preferences',
          accelerator: 'Cmd+,',
          click: () => windows.main.dispatch('preferences')
        },
        {
          type: 'separator'
        },
        {
          role: 'services',
          submenu: []
        },
        {
          type: 'separator'
        },
        {
          role: 'hide'
        },
        {
          role: 'hideothers'
        },
        {
          role: 'unhide'
        },
        {
          type: 'separator'
        },
        {
          label: 'Quit',
          accelerator: 'Command+Q',
          click: () => app.quit()
        }
      ]
    })

    // Add Window menu (OS X)
    template.splice(5, 0, {
      role: 'window',
      submenu: [
        {
          role: 'minimize'
        },
        {
          type: 'separator'
        },
        {
          role: 'front'
        }
      ]
    })
  }

  // On Windows and Linux, open dialogs do not support selecting both files and
  // folders and files, so add an extra menu item so there is one for each type.
  if (process.platform === 'linux' || process.platform === 'win32') {
    // File menu (Windows, Linux)
    template[0].submenu.unshift({
      label: 'Create New Torrent from File...',
      click: () => dialog.openSeedFile()
    })

    // Help menu (Windows, Linux)
    template[4].submenu.push(
      {
        type: 'separator'
      },
      {
        label: 'About ' + config.APP_NAME,
        click: () => windows.about.init()
      }
    )
  }
  // Add "File > Quit" menu item so Linux distros where the system tray icon is
  // missing will have a way to quit the app.
  if (process.platform === 'linux') {
    // File menu (Linux)
    template[0].submenu.push({
      label: 'Quit',
      click: () => app.quit()
    })
  }

  return template
}
<commit_msg>Electron: Use 'quit' role<commit_after>module.exports = {
  init,
  onPlayerClose,
  onPlayerOpen,
  onToggleAlwaysOnTop,
  onToggleFullScreen,
  onWindowBlur,
  onWindowFocus
}

var electron = require('electron')

var app = electron.app

var config = require('../config')
var dialog = require('./dialog')
var shell = require('./shell')
var windows = require('./windows')

var menu

function init () {
  menu = electron.Menu.buildFromTemplate(getMenuTemplate())
  electron.Menu.setApplicationMenu(menu)
}

function onPlayerClose () {
  getMenuItem('Play/Pause').enabled = false
  getMenuItem('Increase Volume').enabled = false
  getMenuItem('Decrease Volume').enabled = false
  getMenuItem('Step Forward').enabled = false
  getMenuItem('Step Backward').enabled = false
  getMenuItem('Increase Speed').enabled = false
  getMenuItem('Decrease Speed').enabled = false
  getMenuItem('Add Subtitles File...').enabled = false
}

function onPlayerOpen () {
  getMenuItem('Play/Pause').enabled = true
  getMenuItem('Increase Volume').enabled = true
  getMenuItem('Decrease Volume').enabled = true
  getMenuItem('Step Forward').enabled = true
  getMenuItem('Step Backward').enabled = true
  getMenuItem('Increase Speed').enabled = true
  getMenuItem('Decrease Speed').enabled = true
  getMenuItem('Add Subtitles File...').enabled = true
}

function onToggleAlwaysOnTop (flag) {
  getMenuItem('Float on Top').checked = flag
}

function onToggleFullScreen (flag) {
  getMenuItem('Full Screen').checked = flag
}

function onWindowBlur () {
  getMenuItem('Full Screen').enabled = false
  getMenuItem('Float on Top').enabled = false
}

function onWindowFocus () {
  getMenuItem('Full Screen').enabled = true
  getMenuItem('Float on Top').enabled = true
}

function getMenuItem (label) {
  for (var i = 0; i < menu.items.length; i++) {
    var menuItem = menu.items[i].submenu.items.find(function (item) {
      return item.label === label
    })
    if (menuItem) return menuItem
  }
}

function getMenuTemplate () {
  var template = [
    {
      label: 'File',
      submenu: [
        {
          label: process.platform === 'darwin'
            ? 'Create New Torrent...'
            : 'Create New Torrent from Folder...',
          accelerator: 'CmdOrCtrl+N',
          click: () => dialog.openSeedDirectory()
        },
        {
          label: 'Open Torrent File...',
          accelerator: 'CmdOrCtrl+O',
          click: () => dialog.openTorrentFile()
        },
        {
          label: 'Open Torrent Address...',
          accelerator: 'CmdOrCtrl+U',
          click: () => dialog.openTorrentAddress()
        },
        {
          type: 'separator'
        },
        {
          role: 'close'
        }
      ]
    },
    {
      label: 'Edit',
      submenu: [
        {
          role: 'undo'
        },
        {
          role: 'redo'
        },
        {
          type: 'separator'
        },
        {
          role: 'cut'
        },
        {
          role: 'copy'
        },
        {
          label: 'Paste Torrent Address',
          role: 'paste'
        },
        {
          role: 'delete'
        },
        {
          role: 'selectall'
        },
        {
          type: 'separator'
        },
        {
          label: 'Preferences',
          accelerator: 'CmdOrCtrl+,',
          click: () => windows.main.dispatch('preferences')
        }
      ]
    },
    {
      label: 'View',
      submenu: [
        {
          label: 'Full Screen',
          type: 'checkbox',
          accelerator: process.platform === 'darwin'
            ? 'Ctrl+Command+F'
            : 'F11',
          click: () => windows.main.toggleFullScreen()
        },
        {
          label: 'Float on Top',
          type: 'checkbox',
          click: () => windows.main.toggleAlwaysOnTop()
        },
        {
          type: 'separator'
        },
        {
          label: 'Go Back',
          accelerator: 'Esc',
          click: () => windows.main.dispatch('escapeBack')
        },
        {
          type: 'separator'
        },
        {
          label: 'Developer',
          submenu: [
            {
              label: 'Developer Tools',
              accelerator: process.platform === 'darwin'
                ? 'Alt+Command+I'
                : 'Ctrl+Shift+I',
              click: () => windows.main.toggleDevTools()
            },
            {
              label: 'Show WebTorrent Process',
              accelerator: process.platform === 'darwin'
                ? 'Alt+Command+P'
                : 'Ctrl+Shift+P',
              click: () => windows.webtorrent.toggleDevTools()
            }
          ]
        }
      ]
    },
    {
      label: 'Playback',
      submenu: [
        {
          label: 'Play/Pause',
          accelerator: 'Space',
          click: () => windows.main.dispatch('playPause'),
          enabled: false
        },
        {
          type: 'separator'
        },
        {
          label: 'Increase Volume',
          accelerator: 'CmdOrCtrl+Up',
          click: () => windows.main.dispatch('changeVolume', 0.1),
          enabled: false
        },
        {
          label: 'Decrease Volume',
          accelerator: 'CmdOrCtrl+Down',
          click: () => windows.main.dispatch('changeVolume', -0.1),
          enabled: false
        },
        {
          type: 'separator'
        },
        {
          label: 'Step Forward',
          accelerator: process.platform === 'darwin'
            ? 'CmdOrCtrl+Alt+Right'
            : 'Alt+Right',
          click: () => windows.main.dispatch('skip', 10),
          enabled: false
        },
        {
          label: 'Step Backward',
          accelerator: process.platform === 'darwin'
            ? 'CmdOrCtrl+Alt+Left'
            : 'Alt+Left',
          click: () => windows.main.dispatch('skip', -10),
          enabled: false
        },
        {
          type: 'separator'
        },
        {
          label: 'Increase Speed',
          accelerator: 'CmdOrCtrl+=',
          click: () => windows.main.dispatch('changePlaybackRate', 1),
          enabled: false
        },
        {
          label: 'Decrease Speed',
          accelerator: 'CmdOrCtrl+-',
          click: () => windows.main.dispatch('changePlaybackRate', -1),
          enabled: false
        },
        {
          type: 'separator'
        },
        {
          label: 'Add Subtitles File...',
          click: () => windows.main.dispatch('openSubtitles'),
          enabled: false
        }
      ]
    },
    {
      label: 'Help',
      role: 'help',
      submenu: [
        {
          label: 'Learn more about ' + config.APP_NAME,
          click: () => shell.openExternal(config.HOME_PAGE_URL)
        },
        {
          label: 'Contribute on GitHub',
          click: () => shell.openExternal(config.GITHUB_URL)
        },
        {
          type: 'separator'
        },
        {
          label: 'Report an Issue...',
          click: () => shell.openExternal(config.GITHUB_URL_ISSUES)
        }
      ]
    }
  ]

  if (process.platform === 'darwin') {
    // Add WebTorrent app menu (OS X)
    template.unshift({
      label: config.APP_NAME,
      submenu: [
        {
          role: 'about'
        },
        {
          type: 'separator'
        },
        {
          label: 'Preferences',
          accelerator: 'Cmd+,',
          click: () => windows.main.dispatch('preferences')
        },
        {
          type: 'separator'
        },
        {
          role: 'services',
          submenu: []
        },
        {
          type: 'separator'
        },
        {
          role: 'hide'
        },
        {
          role: 'hideothers'
        },
        {
          role: 'unhide'
        },
        {
          type: 'separator'
        },
        {
          role: 'quit'
        }
      ]
    })

    // Add Window menu (OS X)
    template.splice(5, 0, {
      role: 'window',
      submenu: [
        {
          role: 'minimize'
        },
        {
          type: 'separator'
        },
        {
          role: 'front'
        }
      ]
    })
  }

  // On Windows and Linux, open dialogs do not support selecting both files and
  // folders and files, so add an extra menu item so there is one for each type.
  if (process.platform === 'linux' || process.platform === 'win32') {
    // File menu (Windows, Linux)
    template[0].submenu.unshift({
      label: 'Create New Torrent from File...',
      click: () => dialog.openSeedFile()
    })

    // Help menu (Windows, Linux)
    template[4].submenu.push(
      {
        type: 'separator'
      },
      {
        label: 'About ' + config.APP_NAME,
        click: () => windows.about.init()
      }
    )
  }
  // Add "File > Quit" menu item so Linux distros where the system tray icon is
  // missing will have a way to quit the app.
  if (process.platform === 'linux') {
    // File menu (Linux)
    template[0].submenu.push({
      label: 'Quit',
      click: () => app.quit()
    })
  }

  return template
}
<|endoftext|>