<commit_before>// ==========================================================================
//                          DG.OpenSaveDialog
//
//  Implements a primitive open/save dialog which allows the user to type
//  the name of the document to open/save.
//
//  Authors:  Kirk Swenson
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  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
//
//    http://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.
// ==========================================================================

/** @class

  @extends SC.PalettePane
*/
DG.OpenSaveDialog = SC.PalettePane.extend(
/** @scope DG.OpenSaveDialog.prototype */ {

  isModal: true,

  dialogType: null, // { DG.OpenSaveDialog.kOpenDialog, DG.OpenSaveDialog.kSaveDialog }

  layout: { width: 400, height: 120, centerX: 0, centerY: 0 },

  contentView: SC.View.extend({
    dialogType: null, // { DG.OpenSaveDialog.kOpenDialog, DG.OpenSaveDialog.kSaveDialog }

    childViews: ['promptView', 'documentListView', 'documentNameView',
                  'documentPermissionsView', 'okButton', 'cancelButton'],
    promptView: SC.LabelView.extend({
      layout: { top: 10, left: 5, right: 5, height:24 },
      localize: true,
      value: 'DG.OpenSaveDialog.promptView.value'   // "Choose a document/name"
    }),
    documentListView: SC.SelectFieldView.design({
      layout: { top: 40, left: 5, right: 5, height:24 },
      nameKey: 'name',
      valueKey: 'id',
      disableSort: true // clients should pass the array sorted appropriately
//       isVisibleBinding: SC.Binding.from('.parentView.dialogType')
    }),
    documentNameView: SC.TextFieldView.design({
      layout: { top: 40, left: 5, right: 5, height:24 },
      value: '',
      spellCheckEnabled: false,
      leftAccessoryView: SC.LabelView.create( {
                          layout: { left: 1, width: 105, height:22, centerY: 0 },
                          localize: true,
                          value: 'DG.OpenSaveDialog.documentNameView.prompt', // "Document Name:"
                          backgroundColor: 'lightgray'
                        })
//       isVisibleBinding: SC.Binding.from('.parentView.dialogType')
    }),

    /**IMPORTANT**/
    //This has been disabled until further design decisions have been made about how to inform the user of this feature
    //and how it will look in both the DG App, and Drupal site.  As far as I've tested, it is fully functional and need
    //only be uncommented, and childView added in the SC.View.extend, and uncommenting the tDocumentPermissions in
    //DG.CreateOpenSaveDialog at the end of this file to allow the shared checkbox to appear again and function.
    //Until then, permissions will still be in place, but essentially all documents will be set to private
    //which is the default, and the user will have no way of changing the permissions on it as the Drupal documents
    //manager will no longer have permissions showing also.

    //permissions for documents consists of private(0), or public(1), more permission levels possible at later
    //date, e.g. group-share.
    documentPermissionsView: SC.CheckboxView.design({
      value: 0,
      toggleOnValue: 1,
      toggleOffValue: 0,
      localize: true,
      title: 'DG.OpenSaveDialog.documentPermissions.title',
      toolTip: 'DG.OpenSaveDialog.documentPermissions.toolTip',
      layout: {bottom: 10, left: 15 , width: 100, height: 17}
    }),
    okButton: SC.ButtonView.design({
      layout: { bottom:5, right: 5, height:24, width: 90 },
      titleMinWidth: 0,
      localize: true,
      title: 'DG.OpenSaveDialog.okButton.title',  // "Open/Save"
      target: null,
      action: null,
      toolTip: 'DG.OpenSaveDialog.okButton.toolTip',  // "Open/Save the specified document",
      // Ideally, we should disable the OK button when the document name field is empty.
      //isEnabledBinding: SC.Binding.oneWay('*parentView.documentNameView.value').notEmpty(),
      isDefault: true
    }),
    cancelButton: SC.ButtonView.design({
      layout: { bottom:5, right: 115, height:24, width: 90 },
      titleMinWidth: 0,
      localize: true,
      title: 'DG.OpenSaveDialog.cancelButton.title',  // "Cancel"
      target: null,
      action: null,
      toolTip: 'DG.OpenSaveDialog.cancelButton.toolTip',  // "Dismiss the dialog without making any changes"
      isCancel: true
    })
  }),

  documentName: function() {
    switch( this.get('dialogType')) {
    case DG.OpenSaveDialog.kOpenDialog:
      var docList = this.getPath('contentView.documentListView.objects'),
          i, docCount = docList && docList.get('length'),
          docID = this.getPath('contentView.documentListView.fieldValue');
      for( i = 0; i < docCount; ++i) {
        var docEntry = docList.objectAt( i);
        if( docEntry.id === docID)
          return docEntry.name;
      }
      return null;
    case DG.OpenSaveDialog.kSaveDialog:
      return this.getPath('contentView.documentNameView.value');
    default:
    }
    return '';
  }.property(),

  documentID: function() {
    switch( this.get('dialogType')) {
    case DG.OpenSaveDialog.kOpenDialog:
      return this.getPath('contentView.documentListView.fieldValue');
    default:
    }
    return null;
  }.property(),

  documentPermissions: function() {
    return this.getPath('contentView.documentPermissionsView.value');
  }.property(),

  init: function() {
    sc_super();

    // By default, cancel simply closes the dialog
    this.setPath('contentView.cancelButton.target', this);
    this.setPath('contentView.cancelButton.action', 'close');
  },

  /**
    Close the open/save dialog.

    Call this function from your okButton/cancelButton action handlers.
    (The default cancelButton handler simply calls this method directly.)
   */
  close: function() {
    // Not clear what the best way to close/destroy is.
    this.remove();
    this.destroy();
  }
});

DG.OpenSaveDialog.kOpenDialog = 1;
DG.OpenSaveDialog.kSaveDialog = 2;

/**
  Brings up the open/save dialog with the properties specified by the client.

  @param {Object} iProperties   Configuration properties for open/save dialog.
                                Clients should set the 'documentNameValue' to the default name
                                to display in the dialog, and the 'okTarget' and 'okAction'
                                to the method to be called when a name is entered.
                                Additional options include the titles of the buttons, tooltips
                                displayed for the dialog items, etc.
                                See the kParamMap below for details.

  @returns  {DG.OpenSaveDialog} the created dialog
 */
DG.CreateOpenSaveDialog = function( iProperties) {
  var tDialog = DG.OpenSaveDialog.create( iProperties),
      kParamMap = {
        dialogType: 'dialogType', // { DG.OpenSaveDialog.kOpenDialog, DG.OpenSaveDialog.kSaveDialog }

        prompt: 'promptView.value',

        documentList: 'documentListView.objects',

        documentNamePrompt: 'documentNameView.leftAccessoryView.value',
        documentNameValue: 'documentNameView.value',
        documentNameHint: 'documentNameView.hint',
        documentNameIsEnabled: 'documentNameView.isEnabled',
        documentPermissionValue: 'documentPermissionsView.value',

        okTitle: 'okButton.title',
        okTarget: 'okButton.target',
        okAction: 'okButton.action',
        okTooltip: 'okButton.toolTip',

        cancelTitle: 'cancelButton.title',
        cancelTooltip: 'cancelButton.toolTip'
      },
      tContentView = tDialog.get('contentView'),
      tDocListView = tContentView.get('documentListView'),
      tDocNameView = tContentView.get('documentNameView'),
      tDocPermissionsView = tContentView.get('documentPermissionsView'),
      // "Shared" check-box shown on save dialog for supported configurations
      permissionsVisibility = DG.supports('docSavePermissions') &&
                              (tDialog.get('dialogType') === DG.OpenSaveDialog.kSaveDialog);

  // Loop through client-specified properties, applying them to the
  // appropriate property via its path given in the kParamMap.
  DG.ObjectMap.forEach( iProperties,
              function( iKey, iValue) {
                var tParamPath = kParamMap[ iKey];
                if( !SC.empty( tParamPath))
                  tContentView.setPath( tParamPath, iValue);
              } );

  // Should be able to do this with bindings instead of code
  tDocListView.set('isVisible', tDialog.get('dialogType') === DG.OpenSaveDialog.kOpenDialog);
  tDocNameView.set('isVisible', tDialog.get('dialogType') === DG.OpenSaveDialog.kSaveDialog);
  tDocPermissionsView.set('isVisible', permissionsVisibility);

  // Put up the dialog
  tDialog.append();

  // Make the document name edit view the first responder
  switch( tDialog.get('dialogType')) {
  case DG.OpenSaveDialog.kOpenDialog:
    tDocListView.becomeFirstResponder();
    break;
  case DG.OpenSaveDialog.kSaveDialog:
    tDocNameView.becomeFirstResponder();
    break;
  default:
  }
  return tDialog;
};

<commit_msg>WIP: Switch to SC.SelectView since SC.SelectFieldView has been removed.<commit_after>// ==========================================================================
//                          DG.OpenSaveDialog
//
//  Implements a primitive open/save dialog which allows the user to type
//  the name of the document to open/save.
//
//  Authors:  Kirk Swenson
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  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
//
//    http://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.
// ==========================================================================

/** @class

  @extends SC.PalettePane
*/
DG.OpenSaveDialog = SC.PalettePane.extend(
/** @scope DG.OpenSaveDialog.prototype */ {

  isModal: true,

  dialogType: null, // { DG.OpenSaveDialog.kOpenDialog, DG.OpenSaveDialog.kSaveDialog }

  layout: { width: 400, height: 120, centerX: 0, centerY: 0 },

  contentView: SC.View.extend({
    dialogType: null, // { DG.OpenSaveDialog.kOpenDialog, DG.OpenSaveDialog.kSaveDialog }

    childViews: ['promptView', 'documentListView', 'documentNameView',
                  'documentPermissionsView', 'okButton', 'cancelButton'],
    promptView: SC.LabelView.extend({
      layout: { top: 10, left: 5, right: 5, height:24 },
      localize: true,
      value: 'DG.OpenSaveDialog.promptView.value'   // "Choose a document/name"
    }),
    documentListView: SC.SelectView.design({
      layout: { top: 40, left: 5, right: 5, height:24 },
      nameKey: 'name',
      valueKey: 'id',
      disableSort: true // clients should pass the array sorted appropriately
//       isVisibleBinding: SC.Binding.from('.parentView.dialogType')
    }),
    documentNameView: SC.TextFieldView.design({
      layout: { top: 40, left: 5, right: 5, height:24 },
      value: '',
      spellCheckEnabled: false,
      leftAccessoryView: SC.LabelView.create( {
                          layout: { left: 1, width: 105, height:22, centerY: 0 },
                          localize: true,
                          value: 'DG.OpenSaveDialog.documentNameView.prompt', // "Document Name:"
                          backgroundColor: 'lightgray'
                        })
//       isVisibleBinding: SC.Binding.from('.parentView.dialogType')
    }),

    /**IMPORTANT**/
    //This has been disabled until further design decisions have been made about how to inform the user of this feature
    //and how it will look in both the DG App, and Drupal site.  As far as I've tested, it is fully functional and need
    //only be uncommented, and childView added in the SC.View.extend, and uncommenting the tDocumentPermissions in
    //DG.CreateOpenSaveDialog at the end of this file to allow the shared checkbox to appear again and function.
    //Until then, permissions will still be in place, but essentially all documents will be set to private
    //which is the default, and the user will have no way of changing the permissions on it as the Drupal documents
    //manager will no longer have permissions showing also.

    //permissions for documents consists of private(0), or public(1), more permission levels possible at later
    //date, e.g. group-share.
    documentPermissionsView: SC.CheckboxView.design({
      value: 0,
      toggleOnValue: 1,
      toggleOffValue: 0,
      localize: true,
      title: 'DG.OpenSaveDialog.documentPermissions.title',
      toolTip: 'DG.OpenSaveDialog.documentPermissions.toolTip',
      layout: {bottom: 10, left: 15 , width: 100, height: 17}
    }),
    okButton: SC.ButtonView.design({
      layout: { bottom:5, right: 5, height:24, width: 90 },
      titleMinWidth: 0,
      localize: true,
      title: 'DG.OpenSaveDialog.okButton.title',  // "Open/Save"
      target: null,
      action: null,
      toolTip: 'DG.OpenSaveDialog.okButton.toolTip',  // "Open/Save the specified document",
      // Ideally, we should disable the OK button when the document name field is empty.
      //isEnabledBinding: SC.Binding.oneWay('*parentView.documentNameView.value').notEmpty(),
      isDefault: true
    }),
    cancelButton: SC.ButtonView.design({
      layout: { bottom:5, right: 115, height:24, width: 90 },
      titleMinWidth: 0,
      localize: true,
      title: 'DG.OpenSaveDialog.cancelButton.title',  // "Cancel"
      target: null,
      action: null,
      toolTip: 'DG.OpenSaveDialog.cancelButton.toolTip',  // "Dismiss the dialog without making any changes"
      isCancel: true
    })
  }),

  documentName: function() {
    switch( this.get('dialogType')) {
    case DG.OpenSaveDialog.kOpenDialog:
      var docList = this.getPath('contentView.documentListView.objects'),
          i, docCount = docList && docList.get('length'),
          docID = this.getPath('contentView.documentListView.fieldValue');
      for( i = 0; i < docCount; ++i) {
        var docEntry = docList.objectAt( i);
        if( docEntry.id === docID)
          return docEntry.name;
      }
      return null;
    case DG.OpenSaveDialog.kSaveDialog:
      return this.getPath('contentView.documentNameView.value');
    default:
    }
    return '';
  }.property(),

  documentID: function() {
    switch( this.get('dialogType')) {
    case DG.OpenSaveDialog.kOpenDialog:
      return this.getPath('contentView.documentListView.fieldValue');
    default:
    }
    return null;
  }.property(),

  documentPermissions: function() {
    return this.getPath('contentView.documentPermissionsView.value');
  }.property(),

  init: function() {
    sc_super();

    // By default, cancel simply closes the dialog
    this.setPath('contentView.cancelButton.target', this);
    this.setPath('contentView.cancelButton.action', 'close');
  },

  /**
    Close the open/save dialog.

    Call this function from your okButton/cancelButton action handlers.
    (The default cancelButton handler simply calls this method directly.)
   */
  close: function() {
    // Not clear what the best way to close/destroy is.
    this.remove();
    this.destroy();
  }
});

DG.OpenSaveDialog.kOpenDialog = 1;
DG.OpenSaveDialog.kSaveDialog = 2;

/**
  Brings up the open/save dialog with the properties specified by the client.

  @param {Object} iProperties   Configuration properties for open/save dialog.
                                Clients should set the 'documentNameValue' to the default name
                                to display in the dialog, and the 'okTarget' and 'okAction'
                                to the method to be called when a name is entered.
                                Additional options include the titles of the buttons, tooltips
                                displayed for the dialog items, etc.
                                See the kParamMap below for details.

  @returns  {DG.OpenSaveDialog} the created dialog
 */
DG.CreateOpenSaveDialog = function( iProperties) {
  var tDialog = DG.OpenSaveDialog.create( iProperties),
      kParamMap = {
        dialogType: 'dialogType', // { DG.OpenSaveDialog.kOpenDialog, DG.OpenSaveDialog.kSaveDialog }

        prompt: 'promptView.value',

        documentList: 'documentListView.objects',

        documentNamePrompt: 'documentNameView.leftAccessoryView.value',
        documentNameValue: 'documentNameView.value',
        documentNameHint: 'documentNameView.hint',
        documentNameIsEnabled: 'documentNameView.isEnabled',
        documentPermissionValue: 'documentPermissionsView.value',

        okTitle: 'okButton.title',
        okTarget: 'okButton.target',
        okAction: 'okButton.action',
        okTooltip: 'okButton.toolTip',

        cancelTitle: 'cancelButton.title',
        cancelTooltip: 'cancelButton.toolTip'
      },
      tContentView = tDialog.get('contentView'),
      tDocListView = tContentView.get('documentListView'),
      tDocNameView = tContentView.get('documentNameView'),
      tDocPermissionsView = tContentView.get('documentPermissionsView'),
      // "Shared" check-box shown on save dialog for supported configurations
      permissionsVisibility = DG.supports('docSavePermissions') &&
                              (tDialog.get('dialogType') === DG.OpenSaveDialog.kSaveDialog);

  // Loop through client-specified properties, applying them to the
  // appropriate property via its path given in the kParamMap.
  DG.ObjectMap.forEach( iProperties,
              function( iKey, iValue) {
                var tParamPath = kParamMap[ iKey];
                if( !SC.empty( tParamPath))
                  tContentView.setPath( tParamPath, iValue);
              } );

  // Should be able to do this with bindings instead of code
  tDocListView.set('isVisible', tDialog.get('dialogType') === DG.OpenSaveDialog.kOpenDialog);
  tDocNameView.set('isVisible', tDialog.get('dialogType') === DG.OpenSaveDialog.kSaveDialog);
  tDocPermissionsView.set('isVisible', permissionsVisibility);

  // Put up the dialog
  tDialog.append();

  // Make the document name edit view the first responder
  switch( tDialog.get('dialogType')) {
  case DG.OpenSaveDialog.kOpenDialog:
    tDocListView.becomeFirstResponder();
    break;
  case DG.OpenSaveDialog.kSaveDialog:
    tDocNameView.becomeFirstResponder();
    break;
  default:
  }
  return tDialog;
};

<|endoftext|>


<commit_before>import { moduleForComponent, test } from 'ember-qunit';

moduleForComponent('programyear-list', 'Unit | Component | programyear list', {
  unit: true
});

test('properties have default values', function(assert) {
  assert.expect(1);

  const expected = {
    program:      null,
    programYears: [],
    editorOn:     false,
    selection:    null
  };

  const component = this.subject();

  const actual = {
    program:      component.get('program'),
    programYears: component.get('programYears'),
    editorOn:     component.get('editorOn'),
    selection:    component.get('selection'),
  };

  assert.deepEqual(actual.program, expected.program, 'default values are correct');
});

test('`availableAcademicYears` computed property works properly', function(assert) {
  assert.expect(1);

  const programYears = [{
    academicYears: '2017 - 2018', startYear: '2017'
  }, {
    academicYears: '2018 - 2019', startYear: '2018'
  }];

  const currentYear = new Date().getFullYear();

  for (let i = 0; i < 10; i++) {
    if (i !== 5) {
      let start = (currentYear - 5 + i).toString();
      let end = (currentYear - 4 + i).toString();

      programYears.pushObject({ academicYears: `${start} - ${end}`, startYear: start });
    }
  }

  const startYear = currentYear.toString();
  const endYear = (currentYear + 1).toString();

  const expected = [{ value: currentYear, label: `${startYear} - ${endYear}` }];
  const component = this.subject({ programYears });
  const actual = component.get('availableAcademicYears');

  assert.deepEqual(actual, expected, 'computed property works');
});
<commit_msg>replaced hard-wired year values with calculated ones.<commit_after>import { moduleForComponent, test } from 'ember-qunit';

moduleForComponent('programyear-list', 'Unit | Component | programyear list', {
  unit: true
});

test('properties have default values', function(assert) {
  assert.expect(1);

  const expected = {
    program:      null,
    programYears: [],
    editorOn:     false,
    selection:    null
  };

  const component = this.subject();

  const actual = {
    program:      component.get('program'),
    programYears: component.get('programYears'),
    editorOn:     component.get('editorOn'),
    selection:    component.get('selection'),
  };

  assert.deepEqual(actual.program, expected.program, 'default values are correct');
});

test('`availableAcademicYears` computed property works properly', function(assert) {
  assert.expect(1);

  const currentYear = new Date().getFullYear();
  const nextYear = currentYear + 1;
  const twoYearsFromNow = nextYear + 1;
  const threeYearsFromNow = twoYearsFromNow + 1;

  const programYears = [{
    academicYears: `${nextYear} - ${twoYearsFromNow}`, startYear: `${nextYear}`
  }, {
    academicYears: `${twoYearsFromNow} - ${threeYearsFromNow}`, startYear: `${threeYearsFromNow}`
  }];

  for (let i = 0; i < 10; i++) {
    if (i !== 5) {
      let start = (currentYear - 5 + i).toString();
      let end = (currentYear - 4 + i).toString();

      programYears.pushObject({ academicYears: `${start} - ${end}`, startYear: start });
    }
  }

  const startYear = currentYear.toString();
  const endYear = (currentYear + 1).toString();

  const expected = [{ value: currentYear, label: `${startYear} - ${endYear}` }];
  const component = this.subject({ programYears });
  const actual = component.get('availableAcademicYears');

  assert.deepEqual(actual, expected, 'computed property works');
});
<|endoftext|>


<commit_before>/**
 * Select2 <Language> translation.
 *
 * Author: bigmihail <bigmihail@bigmir.net>
 */
(function ($) {
    "use strict";

    $.extend($.fn.select2.defaults, {
        formatNoMatches: function () { return "Нічого не знайдено"; },
        formatInputTooShort: function (input, min) { var n = min - input.length, s = ["", "и", "ів"], p = [2,0,1,1,1,2]; return "Введіть буль ласка ще " + n + " символ" + s[ (n%100>4 && n%100<=20)? 2 : p[Math.min(n%10, 5)] ]; },
        formatInputTooLong: function (input, max) { var n = input.length - max, s = ["", "и", "ів"], p = [2,0,1,1,1,2]; return "Введіть буль ласка на " + n + " символ" + s[ (n%100>4 && n%100<=20)? 2 : p[Math.min(n%10, 5)] ] + " менше"; },
        formatSelectionTooBig: function (limit) {var s = ["", "и", "ів"], p = [2,0,1,1,1,2];  return "Ви можете вибрати лише " + limit + " елемент" + s[ (limit%100>4 && limit%100<=20)? 2 : p[Math.min(limit%10, 5)] ]; },
        formatLoadMore: function (pageNumber) { return "Завантаження даних..."; },
        formatSearching: function () { return "Пошук..."; }
    });
})(jQuery);
<commit_msg>remove non use file<commit_after><|endoftext|>


<commit_before>import React from 'react';
import {shallow} from 'enzyme';
import {parse as parseDiff} from 'what-the-diff';

import {rawDiff, rawDiffWithPathPrefix} from '../fixtures/diffs/raw-diff';
import {buildMultiFilePatch} from '../../lib/models/patch';
import {getEndpoint} from '../../lib/models/endpoint';

import PullRequestChangedFilesContainer from '../../lib/containers/pr-changed-files-container';
import IssueishDetailItem from '../../lib/items/issueish-detail-item';

describe('PullRequestChangedFilesContainer', function() {
  let diffResponse;

  function buildApp(overrideProps = {}) {
    return (
      <PullRequestChangedFilesContainer
        owner="atom"
        repo="github"
        number={1804}
        token="1234"
        endpoint={getEndpoint('github.com')}
        itemType={IssueishDetailItem}
        destroy={() => {}}
        shouldRefetch={false}
        workspace={{}}
        commands={{}}
        keymaps={{}}
        tooltips={{}}
        config={{}}
        localRepository={{}}
        {...overrideProps}
      />
    );
  }

  function setDiffResponse(body) {
    diffResponse = new window.Response(body, {
      status: 200,
      headers: {'Content-type': 'text/plain'},
    });
  }

  describe('when the data is able to be fetched successfully', function() {
    beforeEach(function() {
      setDiffResponse(rawDiff);
      sinon.stub(window, 'fetch').callsFake(() => Promise.resolve(diffResponse));
    });
    it('renders a loading spinner if data has not yet been fetched', function() {
      const wrapper = shallow(buildApp());
      assert.isTrue(wrapper.find('LoadingView').exists());
    });
    it('passes extra props through to PullRequestChangedFilesController', async function() {
      const extraProp = Symbol('really really extra');

      const wrapper = shallow(buildApp({extraProp}));
      await assert.async.isTrue(wrapper.update().find('MultiFilePatchController').exists());

      const controller = wrapper.find('MultiFilePatchController');
      assert.strictEqual(controller.prop('extraProp'), extraProp);
    });

    it('builds the diff URL', function() {
      const wrapper = shallow(buildApp({
        owner: 'smashwilson',
        repo: 'pushbot',
        number: 12,
        endpoint: getEndpoint('github.com'),
      }));

      const diffURL = wrapper.instance().getDiffURL();
      assert.strictEqual(diffURL, 'https://api.github.com/repos/smashwilson/pushbot/pulls/12');
    });

    it('builds multifilepatch without the a/ and b/ prefixes in file paths', function() {
      const wrapper = shallow(buildApp());
      const {filePatches} = wrapper.instance().buildPatch(rawDiffWithPathPrefix);
      console.log(filePatches)
      assert.notMatch(filePatches[0].newFile.path, /^[a|b]\//);
      assert.notMatch(filePatches[0].oldFile.path, /^[a|b]\//);
    });

    it('passes loaded diff data through to the controller', async function() {
      const wrapper = shallow(buildApp({
        token: '4321',
      }));
      await assert.async.isTrue(wrapper.update().find('MultiFilePatchController').exists());

      const controller = wrapper.find('MultiFilePatchController');
      const expected = buildMultiFilePatch(parseDiff(rawDiff));
      assert.isTrue(controller.prop('multiFilePatch').isEqual(expected));

      assert.deepEqual(window.fetch.lastCall.args, [
        'https://api.github.com/repos/atom/github/pulls/1804',
        {
          headers: {
            Accept: 'application/vnd.github.v3.diff',
            Authorization: 'bearer 4321',
          },
        },
      ]);
    });
    it('re fetches data when shouldRefetch is true', async function() {
      const wrapper = shallow(buildApp());
      await assert.async.isTrue(wrapper.update().find('MultiFilePatchController').exists());
      assert.strictEqual(window.fetch.callCount, 1);
      wrapper.setProps({shouldRefetch: true});
      assert.isTrue(wrapper.instance().state.isLoading);
      await assert.async.strictEqual(window.fetch.callCount, 2);
    });
  });

  describe('error states', function() {
    async function assertErrorRendered(expectedErrorMessage, wrapper) {
      await assert.async.deepEqual(wrapper.update().instance().state.error, expectedErrorMessage);
      const errorView = wrapper.find('ErrorView');
      assert.deepEqual(errorView.prop('descriptions'), [expectedErrorMessage]);
    }
    it('renders an error if network request fails', async function() {
      sinon.stub(window, 'fetch').callsFake(() => Promise.reject(new Error('oh shit')));
      const wrapper = shallow(buildApp());
      await assertErrorRendered('Network error encountered at fetching https://api.github.com/repos/atom/github/pulls/1804', wrapper);
    });

    it('renders an error if diff parsing fails', async function() {
      setDiffResponse('bad diff no treat for you');
      sinon.stub(window, 'fetch').callsFake(() => Promise.resolve(diffResponse));
      const wrapper = shallow(buildApp());
      await assertErrorRendered('Unable to parse diff for this pull request.', wrapper);
    });

    it('renders an error if fetch returns a non-ok response', async function() {
      const badResponse = new window.Response(rawDiff, {
        status: 404,
        statusText: 'oh noes',
        headers: {'Content-type': 'text/plain'},
      });
      sinon.stub(window, 'fetch').callsFake(() => Promise.resolve(badResponse));
      const wrapper = shallow(buildApp());
      await assertErrorRendered('Unable to fetch diff for this pull request: oh noes.', wrapper);
    });
  });
});
<commit_msg>no console log!<commit_after>import React from 'react';
import {shallow} from 'enzyme';
import {parse as parseDiff} from 'what-the-diff';

import {rawDiff, rawDiffWithPathPrefix} from '../fixtures/diffs/raw-diff';
import {buildMultiFilePatch} from '../../lib/models/patch';
import {getEndpoint} from '../../lib/models/endpoint';

import PullRequestChangedFilesContainer from '../../lib/containers/pr-changed-files-container';
import IssueishDetailItem from '../../lib/items/issueish-detail-item';

describe('PullRequestChangedFilesContainer', function() {
  let diffResponse;

  function buildApp(overrideProps = {}) {
    return (
      <PullRequestChangedFilesContainer
        owner="atom"
        repo="github"
        number={1804}
        token="1234"
        endpoint={getEndpoint('github.com')}
        itemType={IssueishDetailItem}
        destroy={() => {}}
        shouldRefetch={false}
        workspace={{}}
        commands={{}}
        keymaps={{}}
        tooltips={{}}
        config={{}}
        localRepository={{}}
        {...overrideProps}
      />
    );
  }

  function setDiffResponse(body) {
    diffResponse = new window.Response(body, {
      status: 200,
      headers: {'Content-type': 'text/plain'},
    });
  }

  describe('when the data is able to be fetched successfully', function() {
    beforeEach(function() {
      setDiffResponse(rawDiff);
      sinon.stub(window, 'fetch').callsFake(() => Promise.resolve(diffResponse));
    });
    it('renders a loading spinner if data has not yet been fetched', function() {
      const wrapper = shallow(buildApp());
      assert.isTrue(wrapper.find('LoadingView').exists());
    });
    it('passes extra props through to PullRequestChangedFilesController', async function() {
      const extraProp = Symbol('really really extra');

      const wrapper = shallow(buildApp({extraProp}));
      await assert.async.isTrue(wrapper.update().find('MultiFilePatchController').exists());

      const controller = wrapper.find('MultiFilePatchController');
      assert.strictEqual(controller.prop('extraProp'), extraProp);
    });

    it('builds the diff URL', function() {
      const wrapper = shallow(buildApp({
        owner: 'smashwilson',
        repo: 'pushbot',
        number: 12,
        endpoint: getEndpoint('github.com'),
      }));

      const diffURL = wrapper.instance().getDiffURL();
      assert.strictEqual(diffURL, 'https://api.github.com/repos/smashwilson/pushbot/pulls/12');
    });

    it('builds multifilepatch without the a/ and b/ prefixes in file paths', function() {
      const wrapper = shallow(buildApp());
      const {filePatches} = wrapper.instance().buildPatch(rawDiffWithPathPrefix);
      assert.notMatch(filePatches[0].newFile.path, /^[a|b]\//);
      assert.notMatch(filePatches[0].oldFile.path, /^[a|b]\//);
    });

    it('passes loaded diff data through to the controller', async function() {
      const wrapper = shallow(buildApp({
        token: '4321',
      }));
      await assert.async.isTrue(wrapper.update().find('MultiFilePatchController').exists());

      const controller = wrapper.find('MultiFilePatchController');
      const expected = buildMultiFilePatch(parseDiff(rawDiff));
      assert.isTrue(controller.prop('multiFilePatch').isEqual(expected));

      assert.deepEqual(window.fetch.lastCall.args, [
        'https://api.github.com/repos/atom/github/pulls/1804',
        {
          headers: {
            Accept: 'application/vnd.github.v3.diff',
            Authorization: 'bearer 4321',
          },
        },
      ]);
    });
    it('re fetches data when shouldRefetch is true', async function() {
      const wrapper = shallow(buildApp());
      await assert.async.isTrue(wrapper.update().find('MultiFilePatchController').exists());
      assert.strictEqual(window.fetch.callCount, 1);
      wrapper.setProps({shouldRefetch: true});
      assert.isTrue(wrapper.instance().state.isLoading);
      await assert.async.strictEqual(window.fetch.callCount, 2);
    });
  });

  describe('error states', function() {
    async function assertErrorRendered(expectedErrorMessage, wrapper) {
      await assert.async.deepEqual(wrapper.update().instance().state.error, expectedErrorMessage);
      const errorView = wrapper.find('ErrorView');
      assert.deepEqual(errorView.prop('descriptions'), [expectedErrorMessage]);
    }
    it('renders an error if network request fails', async function() {
      sinon.stub(window, 'fetch').callsFake(() => Promise.reject(new Error('oh shit')));
      const wrapper = shallow(buildApp());
      await assertErrorRendered('Network error encountered at fetching https://api.github.com/repos/atom/github/pulls/1804', wrapper);
    });

    it('renders an error if diff parsing fails', async function() {
      setDiffResponse('bad diff no treat for you');
      sinon.stub(window, 'fetch').callsFake(() => Promise.resolve(diffResponse));
      const wrapper = shallow(buildApp());
      await assertErrorRendered('Unable to parse diff for this pull request.', wrapper);
    });

    it('renders an error if fetch returns a non-ok response', async function() {
      const badResponse = new window.Response(rawDiff, {
        status: 404,
        statusText: 'oh noes',
        headers: {'Content-type': 'text/plain'},
      });
      sinon.stub(window, 'fetch').callsFake(() => Promise.resolve(badResponse));
      const wrapper = shallow(buildApp());
      await assertErrorRendered('Unable to fetch diff for this pull request: oh noes.', wrapper);
    });
  });
});
<|endoftext|>


<commit_before>var less = require('less-middleware'),
    express = require('express'),
    path = require('path'),
    upload = require('jquery-file-upload-middleware');

module.exports = function(app, config) {
  app.configure(function() {
  app.set('port', process.env.PORT || 3000);
  app.set('views', config.root + '/app/views');
  app.set('view engine', 'jade');
  // app.use(express.favicon());
  // app.use(express.logger('dev'));
  app.use('/upload', upload.fileHandler());
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  // app.use(express.cookieParser('your secret here'));
  // app.use(express.session());
  app.use(app.router);
  app.use(less(
      config.root + '/public'
  ));
  app.use(express.static(path.join(config.root, 'public')));
  });

  app.configure('development', function() {
  app.use(express.errorHandler());
  });
}
<commit_msg>Fixed 404 error for favicon request<commit_after>var less = require('less-middleware'),
    express = require('express'),
    path = require('path'),
    upload = require('jquery-file-upload-middleware');

module.exports = function(app, config) {
  app.configure(function() {
  app.set('port', process.env.PORT || 3000);
  app.set('views', config.root + '/app/views');
  app.set('view engine', 'jade');
  app.use(express.favicon());
  // app.use(express.logger('dev'));
  app.use('/upload', upload.fileHandler());
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  // app.use(express.cookieParser('your secret here'));
  // app.use(express.session());
  app.use(app.router);
  app.use(less(
      config.root + '/public'
  ));
  app.use(express.static(path.join(config.root, 'public')));
  });

  app.configure('development', function() {
  app.use(express.errorHandler());
  });
}
<|endoftext|>


<commit_before>import React from 'react';
import {connectToStores} from 'fluxible-addons-react';
import UserProfileStore from '../../../../stores/UserProfileStore';
import AttachSubdeckModalStore from '../../../../stores/AttachSubdeckModalStore';
import AttachDeckList from './AttachDeckList';
import {  Segment, Loader,Label, Image,Dimmer,Grid, Button,TextArea} from 'semantic-ui-react';
import {Microservices} from '../../../../configs/microservices';
import updateSelectedSlides  from '../../../../actions/attachSubdeck/updateSelectedSlides';

const KEY_CODE = {
    LEFT:   37,
    UP:     38,
    RIGHT:  39,
    DOWN:   40
};

class AttachSlides extends React.Component{

    constructor(props){
        super(props);
        this.numColumns = props.numColumns?props.numColumns:4; //if numColumns is not provided, 4 is used as default
        this.state = {

            userDecks:this.props.AttachSubdeckModalStore.userDecks,
            selectedDeckTitle:  this.props.AttachSubdeckModalStore.selectedDeckTitle,
            selectedDeckId: this.props.AttachSubdeckModalStore.selectedDeckId,
            deckSlides: this.props.AttachSubdeckModalStore.deckSlides,
            selectedSlides:this.props.AttachSubdeckModalStore.selectedSlides,
            deckSlidesTitles:this.props.AttachSubdeckModalStore.deckSlidesTitles,
            selectedSlidesLabel: this.props.AttachSubdeckModalStore.selectedSlides.length +' of ' + this.props.AttachSubdeckModalStore.deckSlides.length

        };

        this.handleAllSlides = this.handleAllSlides.bind(this);
        this.handleNone = this.handleNone.bind(this);

    }
    componentWillReceiveProps(nextProps){

        this.setState({
            userDecks: nextProps.AttachSubdeckModalStore.userDecks,
            selectedDeckId: nextProps.AttachSubdeckModalStore.selectedDeckId,
            deckSlides: nextProps.AttachSubdeckModalStore.deckSlides,
            selectedSlides:nextProps.AttachSubdeckModalStore.selectedSlides,
            selectedDeckTitle:nextProps.AttachSubdeckModalStore.selectedDeckTitle,
            deckSlidesTitles:nextProps.AttachSubdeckModalStore.deckSlidesTitles,
            selectedSlidesLabel: nextProps.AttachSubdeckModalStore.selectedSlides.length +' of ' + this.props.AttachSubdeckModalStore.deckSlides.length

        });

    }
    componentDidUpdate(){
        if(this.state.deckSlides.length !== 0) //We have the slides rendered
            //$('#attachAllSlidesButtonId').focus();
            $('#selectedDeckTitleId').focus();

    }

    checkNoEmpty(element){
        return (element.toString().length>0);
    }

    handleOnclick(selectedSlide){
      /*This method:
       - adds the selectedSlide into the selectedSlides list if it was not selectedSlide
       - removes the selectedSlide from the selectedSlides list if it was
      */
        let slides = this.state.selectedSlides;
        let index = slides.indexOf(selectedSlide);
        if(index === -1){//It was not selected
            slides.push(selectedSlide);
        } else { //It was selected...remove from it
            slides[index]='';
            slides = slides.filter(this.checkNoEmpty);
        };

        this.setState({
            selectedSlides: slides
        });

        this.context.executeAction(updateSelectedSlides,{selectedSlides:slides},null);

    }
    handleAllSlides(){
        this.setState({
            selectedSlides:this.state.deckSlides,
        });
        this.context.executeAction(updateSelectedSlides,{selectedSlides:this.state.deckSlides},null);
    }

    handleNone(){
        this.setState({
            selectedSlides: [],
        });

        this.context.executeAction(updateSelectedSlides,{selectedSlides:[]},null);

    }

    handleKeyPress(selectedSlide,event){
        if(event.key === 'Enter'){
            event.preventDefault();
            this.handleOnclick(selectedSlide);

        }
    }

    getNextPos(pos,numSlides,numColumns,numRows,eventKeyCode){
        /*******************************************
            Returns next position in a grid, taking into account
            the arrow key pressed and the number of rows and columns
            of the grid
        ********************************************/
        let currentRow = Math.floor(pos/numColumns);

        let lastPos = numSlides-1;
        let nextPos = parseInt(pos); //In case of overflow, we stayed in the same position. Force working with integers
        switch (eventKeyCode) {
            case KEY_CODE.RIGHT:
                if(pos !== lastPos) //No overflow by right direction
                    nextPos ++;
                break;
            case KEY_CODE.LEFT:
                if(pos !== 0)
                    nextPos --;
                break;
            case KEY_CODE.UP:
                if(currentRow !== 0)
                    nextPos = nextPos - parseInt(numColumns);
                break;
            case KEY_CODE.DOWN:
                if(currentRow !== (numRows-1))
                    nextPos = nextPos + parseInt(numColumns);
                break;

        }
        return nextPos;

    }
    handleKeyDown(pos,event){

        if(event.keyCode === KEY_CODE.RIGHT ||
           event.keyCode === KEY_CODE.LEFT ||
           event.keyCode === KEY_CODE.UP ||
           event.keyCode === KEY_CODE.DOWN ){
           //the user wants to navigate through the grid
            let numRows = Math.ceil(this.state.deckSlides.length/this.numColumns);
            let nextPos = this.getNextPos(pos,this.state.deckSlides.length,this.numColumns,numRows,event.keyCode);  //get next cell
             //get the id of the cell
            $('#slide'+nextPos).focus(); //move to the cell
        }

    }
    loadSlidesGrid(){
        let activeItemStyle = {
            backgroundColor:'#f8ffff',
            border:'2px solid #2185d0'

        };

        let numRows = Math.ceil(this.state.deckSlides.length/this.numColumns);
        let rowCount=0;
        let columnCount=0;
        let slidesShowed=0;
        let columnsContent=[];
        let singleColumn;
        let singleRow;
        let rowsContent=[];
        let slideId;

        for(let i=0;i<numRows;i++){
            while((columnCount<this.numColumns) && (slidesShowed < this.state.deckSlides.length)){
                slideId =this.state.deckSlides[slidesShowed];
                singleColumn =  <Grid.Column key={slidesShowed}
                                    id={'slide'+slidesShowed}
                                    onClick={this.handleOnclick.bind(this,slideId)}
                                    onKeyPress={this.handleKeyPress.bind(this,slideId)}
                                    onKeyDown={this.handleKeyDown.bind(this,slidesShowed)}
                                    style={this.state.selectedSlides.includes(slideId)?activeItemStyle:{}}
                                    role="gridcell"
                                    aria-selected ={this.state.selectedSlides.includes(slideId)}
                                    tabIndex="0">
                                    <Image src={Microservices.file.uri + '/slideThumbnail/' +slideId+'.jpeg'}
                                        alt={'Slide '+ (slidesShowed+1)+'. '+this.state.deckSlidesTitles[slidesShowed]} bordered size='medium' />
                                  </Grid.Column>;
                columnsContent[columnCount] = singleColumn;
                columnCount ++;
                slidesShowed ++;
            }

            singleRow = <Grid.Row  key={i.toString()}>
                          {columnsContent}
                        </Grid.Row>;
            rowsContent[i]=singleRow;
            columnCount = 0;
            columnsContent=[];
        }

        return rowsContent;

    }

    render(){
        let userInfo = {
            userId: this.props.UserProfileStore.userid,
            username: this.props.UserProfileStore.username

        };

        let slidesContent;

        if(this.state.deckSlides.length === 0){ //No slides loaded
            slidesContent = <Segment id="panelSlidesContent">
                                <Dimmer active inverted>
                                  <Loader inverted>Loading</Loader>
                                </Dimmer>
                                <Image src="http://semantic-ui.com/images/wireframe/paragraph.png" />
                              </Segment>;
        }else{

            let headerContent =  <Grid  aria-describedby="attachSlidesDescription2">
                                    <Grid.Row columns={1}>
                                      <Grid.Column>
                                        <Label htmlFor="selectedDeckTitleId" as="label"  color="blue" pointing="right" content='Selected Deck'/>
                                        <Label  id="selectedDeckTitleId" content={this.state.selectedDeckTitle} basic color="blue" tabIndex="0"/>
                                        <TextArea className="sr-only" id="attachSlidesDescription2" value="Select slides to attach" tabIndex ='-1'/>
                                      </Grid.Column>
                                    </Grid.Row>
                                    <Grid.Row columns={2}>
                                      <Grid.Column textAlign="left">
                                      <Label htmlFor="slidesContentId"  color='blue'  pointing="right"  content="Selected slides:"/>
                                      <Label  id="slidesContentId" content={this.state.selectedSlidesLabel} basic color="blue"/>

                                      </Grid.Column>
                                      <Grid.Column textAlign="right" >
                                        <Button type="button"
                                           id="attachAllSlidesButtonId"
                                           color="blue"
                                           aria-label="Select All"
                                           data-tooltip="Select All"
                                           onClick={this.handleAllSlides} >Select All</Button>
                                        <Button type="button"
                                            color="blue"
                                            aria-label="Clear Selection"
                                            data-tooltip="Clear Selection"
                                            onClick={this.handleNone}>Clear Selection</Button>

                                      </Grid.Column>
                                    </Grid.Row>
                                  </Grid>;
            let rowsContent=this.loadSlidesGrid();
            slidesContent = <Segment id="panelSlidesContent">
                               {headerContent}
                               <Grid columns={this.numColumns}
                                 style={{maxHeight:'400px',minHeight:'320px',overflowY:'auto'}}
                                 role="grid"
                                 aria-expanded="true"
                                  aria-describedby="gridInstructions">
                                 <TextArea className="sr-only" id="gridInstructions" value="Use arrow keys to navigate through the grid and then enter to select a slide. You can select more than one slide." tabIndex ='-1'/>
                                {rowsContent}
                               </Grid>
                            </Segment>;
        }

        return slidesContent;

    }

}

AttachSlides.contextTypes = {
    executeAction: React.PropTypes.func.isRequired
};
AttachSlides = connectToStores(AttachSlides,[UserProfileStore,AttachSubdeckModalStore],(context,props) => {
    return {
        UserProfileStore: context.getStore(UserProfileStore).getState(),
        AttachSubdeckModalStore: context.getStore(AttachSubdeckModalStore).getState()
    };
});
export default AttachSlides;
<commit_msg>SWIK-1447-MoveAcrossGrid: Slides grid page 	- focus goes to the title of the selected deck only when it is open         - if user selects "all slides", focus goesd to the attach modal         - selectedSlides: each selected slide is ifentified by slideId-revision-order         - when users select one slide, the focus remains in it<commit_after>import React from 'react';
import {connectToStores} from 'fluxible-addons-react';
import UserProfileStore from '../../../../stores/UserProfileStore';
import AttachSubdeckModalStore from '../../../../stores/AttachSubdeckModalStore';
import AttachDeckList from './AttachDeckList';
import {  Segment, Loader,Label, Image,Dimmer,Grid, Button,TextArea} from 'semantic-ui-react';
import {Microservices} from '../../../../configs/microservices';
import updateSelectedSlides  from '../../../../actions/attachSubdeck/updateSelectedSlides';

const KEY_CODE = {
    LEFT:   37,
    UP:     38,
    RIGHT:  39,
    DOWN:   40
};

class AttachSlides extends React.Component{

    constructor(props){
        super(props);
        this.numColumns = props.numColumns?props.numColumns:4; //if numColumns is not provided, 4 is used as default
        this.state = {

            userDecks:this.props.AttachSubdeckModalStore.userDecks,
            selectedDeckTitle:  this.props.AttachSubdeckModalStore.selectedDeckTitle,
            selectedDeckId: this.props.AttachSubdeckModalStore.selectedDeckId,
            deckSlides: this.props.AttachSubdeckModalStore.deckSlides,
            selectedSlides:this.props.AttachSubdeckModalStore.selectedSlides,
            deckSlidesTitles:this.props.AttachSubdeckModalStore.deckSlidesTitles,
            selectedSlidesLabel: this.props.AttachSubdeckModalStore.selectedSlides.length +' of ' + this.props.AttachSubdeckModalStore.deckSlides.length,
            firstTime:true

        };

        this.handleAllSlides = this.handleAllSlides.bind(this);
        this.handleNone = this.handleNone.bind(this);

    }
    componentWillReceiveProps(nextProps){

        this.setState({
            userDecks: nextProps.AttachSubdeckModalStore.userDecks,
            selectedDeckId: nextProps.AttachSubdeckModalStore.selectedDeckId,
            deckSlides: nextProps.AttachSubdeckModalStore.deckSlides,
            selectedSlides:nextProps.AttachSubdeckModalStore.selectedSlides,
            selectedDeckTitle:nextProps.AttachSubdeckModalStore.selectedDeckTitle,
            deckSlidesTitles:nextProps.AttachSubdeckModalStore.deckSlidesTitles,
            selectedSlidesLabel: nextProps.AttachSubdeckModalStore.selectedSlides.length +' of ' + this.props.AttachSubdeckModalStore.deckSlides.length

        });

    }
    componentDidUpdate(){
        if((this.state.deckSlides.length !== 0) && this.state.firstTime){ //We have the slides rendered
            $('#selectedDeckTitleId').focus();
            this.setState({
                firstTime:false
            });
        } else if (this.state.deckSlides.length === this.state.selectedSlides.length) {
            //all slides selected...move to the attachButton
            $('#attachAttachModal').focus();
        }

            //$('#attachAllSlidesButtonId').focus();
    }

    checkNoEmpty(element){
        return (element.toString().length>0);
    }

    handleOnclick(selectedSlide){
      /*This method:
       - adds the selectedSlide into the selectedSlides list if it was not selectedSlide
       - removes the selectedSlide from the selectedSlides list if it was
      */
        let slides = this.state.selectedSlides;
        let index = slides.indexOf(selectedSlide);
        if(index === -1){//It was not selected
            slides.push(selectedSlide);
        } else { //It was selected...remove from it
            slides[index]='';
            slides = slides.filter(this.checkNoEmpty);
        };

        this.setState({
            selectedSlides: slides
        });

        this.context.executeAction(updateSelectedSlides,{selectedSlides:slides},null);

    }
    handleAllSlides(){

        let selectedIds = this.state.deckSlides.map((slideId,index) => {
            return slideId+'-'+index;

        });
        this.setState({
            selectedSlides:selectedIds,
        });
        this.context.executeAction(updateSelectedSlides,{selectedSlides:selectedIds},null);

    }

    handleNone(){
        this.setState({
            selectedSlides: [],
        });

        this.context.executeAction(updateSelectedSlides,{selectedSlides:[]},null);

    }

    handleKeyPress(selectedSlide,event){
        if(event.key === 'Enter'){
            event.preventDefault();
            this.handleOnclick(selectedSlide);
        }
    }

    getNextPos(pos,numSlides,numColumns,numRows,eventKeyCode){
        /*******************************************
            Returns next position in a grid, taking into account
            the arrow key pressed and the number of rows and columns
            of the grid
        ********************************************/
        let currentRow = Math.floor(pos/numColumns);

        let lastPos = numSlides-1;
        let nextPos = parseInt(pos); //In case of overflow, we stayed in the same position. Force working with integers
        switch (eventKeyCode) {
            case KEY_CODE.RIGHT:
                if(pos !== lastPos) //No overflow by right direction
                    nextPos ++;
                break;
            case KEY_CODE.LEFT:
                if(pos !== 0)
                    nextPos --;
                break;
            case KEY_CODE.UP:
                if(currentRow !== 0)
                    nextPos = nextPos - parseInt(numColumns);
                break;
            case KEY_CODE.DOWN:
                if(currentRow !== (numRows-1))
                    nextPos = nextPos + parseInt(numColumns);
                break;

        }
        return nextPos;

    }
    handleKeyDown(pos,event){

        if(event.keyCode === KEY_CODE.RIGHT ||
           event.keyCode === KEY_CODE.LEFT ||
           event.keyCode === KEY_CODE.UP ||
           event.keyCode === KEY_CODE.DOWN ){
           //the user wants to navigate through the grid
            let numRows = Math.ceil(this.state.deckSlides.length/this.numColumns);
            let nextPos = this.getNextPos(pos,this.state.deckSlides.length,this.numColumns,numRows,event.keyCode);  //get next cell
             //get the id of the cell
            $('#slide'+nextPos).focus(); //move to the cell
        }

    }
    loadSlidesGrid(){
        let activeItemStyle = {
            backgroundColor:'#f8ffff',
            border:'2px solid #2185d0'

        };

        let numRows = Math.ceil(this.state.deckSlides.length/this.numColumns);
        let rowCount=0;
        let columnCount=0;
        let slidesShowed=0;
        let columnsContent=[];
        let singleColumn;
        let singleRow;
        let rowsContent=[];
        let slideId;

        for(let i=0;i<numRows;i++){
            while((columnCount<this.numColumns) && (slidesShowed < this.state.deckSlides.length)){
                slideId =this.state.deckSlides[slidesShowed]+'-'+slidesShowed; //we include the position. the same slideid can be more than one time
                singleColumn =  <Grid.Column key={slidesShowed}
                                    id={'slide'+slidesShowed}
                                    onClick={this.handleOnclick.bind(this,slideId)}
                                    onKeyPress={this.handleKeyPress.bind(this,slideId)}
                                    onKeyDown={this.handleKeyDown.bind(this,slidesShowed)}
                                    style={this.state.selectedSlides.includes(slideId)?activeItemStyle:{}}
                                    role="gridcell"
                                    aria-selected ={this.state.selectedSlides.includes(slideId)}
                                    tabIndex="0">
                                    <Image src={Microservices.file.uri + '/slideThumbnail/' +this.state.deckSlides[slidesShowed]+'.jpeg'}
                                        alt={'Slide '+ (slidesShowed+1)+'. '+this.state.deckSlidesTitles[slidesShowed]} bordered size='medium' />
                                  </Grid.Column>;
                columnsContent[columnCount] = singleColumn;
                columnCount ++;
                slidesShowed ++;
            }

            singleRow = <Grid.Row  key={i.toString()}>
                          {columnsContent}
                        </Grid.Row>;
            rowsContent[i]=singleRow;
            columnCount = 0;
            columnsContent=[];
        }
        rowCount=0;
        columnCount=0;
        slidesShowed=0;
        return rowsContent;

    }

    render(){
        let userInfo = {
            userId: this.props.UserProfileStore.userid,
            username: this.props.UserProfileStore.username

        };

        let slidesContent;

        if(this.state.deckSlides.length === 0){ //No slides loaded
            slidesContent = <Segment id="panelSlidesContent">
                                <Dimmer active inverted>
                                  <Loader inverted>Loading</Loader>
                                </Dimmer>
                                <Image src="http://semantic-ui.com/images/wireframe/paragraph.png" />
                              </Segment>;
        }else{

            let headerContent =  <Grid  aria-describedby="attachSlidesDescription2">
                                    <Grid.Row columns={1}>
                                      <Grid.Column>
                                        <Label htmlFor="selectedDeckTitleId" as="label"  color="blue" pointing="right" content='Selected Deck'/>
                                        <Label  id="selectedDeckTitleId" content={this.state.selectedDeckTitle} basic color="blue" tabIndex="0"/>
                                        <TextArea className="sr-only" id="attachSlidesDescription2" value="Select slides to attach" tabIndex ='-1'/>
                                      </Grid.Column>
                                    </Grid.Row>
                                    <Grid.Row columns={2}>
                                      <Grid.Column textAlign="left">
                                      <Label htmlFor="slidesContentId"  color='blue'  pointing="right"  content="Selected slides:"/>
                                      <Label  id="slidesContentId" content={this.state.selectedSlidesLabel} basic color="blue"/>

                                      </Grid.Column>
                                      <Grid.Column textAlign="right" >
                                        <Button type="button"
                                           id="attachAllSlidesButtonId"
                                           color="blue"
                                           aria-label="Select All"
                                           data-tooltip="Select All"
                                           onClick={this.handleAllSlides} >Select All</Button>
                                        <Button type="button"
                                            color="blue"
                                            aria-label="Clear Selection"
                                            data-tooltip="Clear Selection"
                                            onClick={this.handleNone}>Clear Selection</Button>

                                      </Grid.Column>
                                    </Grid.Row>
                                  </Grid>;
            let rowsContent=this.loadSlidesGrid();
            slidesContent = <Segment id="panelSlidesContent">
                               {headerContent}
                               <Grid columns={this.numColumns}
                                 style={{maxHeight:'400px',minHeight:'320px',overflowY:'auto'}}
                                 role="grid"
                                 aria-expanded="true"
                                  aria-describedby="gridInstructions">
                                 <TextArea className="sr-only" id="gridInstructions" value="Use arrow keys to navigate through the grid and then enter to select a slide. You can select more than one slide." tabIndex ='-1'/>
                                {rowsContent}
                               </Grid>
                            </Segment>;
        }

        return slidesContent;

    }

}

AttachSlides.contextTypes = {
    executeAction: React.PropTypes.func.isRequired
};
AttachSlides = connectToStores(AttachSlides,[UserProfileStore,AttachSubdeckModalStore],(context,props) => {
    return {
        UserProfileStore: context.getStore(UserProfileStore).getState(),
        AttachSubdeckModalStore: context.getStore(AttachSubdeckModalStore).getState()
    };
});
export default AttachSlides;
<|endoftext|>


<commit_before>'use strict'

import {Component}  from 'react'
import {Link} from 'react-router'

class Header extends Component {
  static propTypes: {
    transparent: PropTypes.bool
  }

  constructor(props) {
    super(props)

    this.state = {
      navItems: [
        { to: '/install', label: 'Install' },
        { to: '/tutorials', label: 'Tutorials' },
        { to: '/blog', label: 'Blog' },
        { to: '/papers', label: 'Papers', mobile: false },
        { to: '/videos', label: 'Videos' },
        { to: '/careers', label: 'Careers' },
        { to: 'https://github.com/blockstack', label: 'Code', mobile: false },
      ]
    }
  }

  render() {
    let mobileNavItems = []

    this.state.navItems.map((navItem) => {
      if (navItem.mobile !== false) {
        mobileNavItems.push(navItem)
      }
    })

    return (

            <Link className="navbar-brand brand-bug" to="/">

            </Link>
            <Link className="navbar-brand brand-logo" to="/">

            </Link>
            <button type="button" className="navbar-toggler collapsed hidden-md-up"
                    data-toggle="collapse" data-target="#mobile-nav"
                    aria-controls="dropdown" aria-expanded="false">

            </button>

              {this.state.navItems.map((navItem, index) => {
                return (

                    <Link to={navItem.to} className="nav-link"
                          target={navItem.to.startsWith('http') ? '_blank' : '_self'}>
                      {navItem.label}
                    </Link>

                )
              })}

              {mobileNavItems.map((navItem, index) => {
                return (

                    <Link to={navItem.to} className="nav-link"
                          target={navItem.to.startsWith('http') ? '_blank' : '_self'}>
                      {navItem.label}
                    </Link>

                )
              })}

    )
  }

}

export default Header
<commit_msg>update nav header<commit_after>'use strict'

import {Component}  from 'react'
import {Link} from 'react-router'

class Header extends Component {
  static propTypes: {
    transparent: PropTypes.bool
  }

  constructor(props) {
    super(props)

    this.state = {
      navItems: [
        { to: '/install', label: 'Install' },
        { to: '/tutorials', label: 'Tutorials' },
        { to: '/blog', label: 'Blog' },
        { to: '/papers', label: 'Papers' },
        { to: '/videos', label: 'Videos' },
        { to: '/careers', label: 'Careers' },
        { to: 'https://github.com/blockstack', label: 'Code', mobile: false },
      ]
    }
  }

  render() {
    let mobileNavItems = []

    this.state.navItems.map((navItem) => {
      if (navItem.mobile !== false) {
        mobileNavItems.push(navItem)
      }
    })

    return (

            <Link className="navbar-brand brand-bug" to="/">

            </Link>
            <Link className="navbar-brand brand-logo" to="/">

            </Link>
            <button type="button" className="navbar-toggler collapsed hidden-md-up"
                    data-toggle="collapse" data-target="#mobile-nav"
                    aria-controls="dropdown" aria-expanded="false">

            </button>

              {this.state.navItems.map((navItem, index) => {
                return (

                    { !navItem.to.startsWith('http') ? (
                      <Link to={navItem.to} className="nav-link">
                        {navItem.label}
                      </Link>
                    ) : (
                      <Link to={navItem.to} className="nav-link" target="_blank">
                        {navItem.label}
                      </Link>
                    )}

                )
              })}

              {mobileNavItems.map((navItem, index) => {
                return (

                    { !navItem.to.startsWith('http') ? (
                      <Link to={navItem.to} className="nav-link">
                        {navItem.label}
                      </Link>
                    ) : (
                      <Link to={navItem.to} className="nav-link" target="_blank">
                        {navItem.label}
                      </Link>
                    )}

                )
              })}

    )
  }

}

export default Header
<|endoftext|>


<commit_before>var isNumeric = require('fast-isnumeric');
var mouseEvent = require('./mouse_event');
var getNodeCoords = require('./get_node_coords');
var delay = require('./delay');

function makeFns(node, dx, dy, opts) {
    opts = opts || {};

    var nsteps = opts.nsteps || 1;
    var edge = opts.edge || '';
    var noCover = Boolean(opts.noCover);

    var coords = getNodeCoords(node, edge);
    var fromX = isNumeric(opts.x0) ? opts.x0 : coords.x;
    var fromY = isNumeric(opts.y0) ? opts.y0 : coords.y;

    var dragCoverNode;
    var toX;
    var toY;

    function start() {
        mouseEvent('mousemove', fromX, fromY, {element: node});
        mouseEvent('mousedown', fromX, fromY, {element: node});

        return (noCover ? Promise.resolve(node) : waitForDragCover())
        .then(function(_dragCoverNode) {
            dragCoverNode = _dragCoverNode;

            for(var i = 1; i <= nsteps; i++) {
                toX = fromX + i * dx / nsteps;
                toY = fromY + i * dy / nsteps;
                mouseEvent('mousemove', toX, toY, {element: dragCoverNode});
            }
        });
    }

    function end() {
        mouseEvent('mouseup', toX, toY, {element: dragCoverNode});
        return noCover || waitForDragCoverRemoval();
    }

    return {
        start: start,
        end: end
    };
}

/*
 * drag: grab a node and drag it (dx, dy) pixels
 * optionally specify an edge ('n', 'se', 'w' etc)
 * to grab it by an edge or corner (otherwise the middle is used)
 */
function drag(node, dx, dy, edge, x0, y0, nsteps, noCover, timeDelay) {
    if(!timeDelay) timeDelay = 0;
    var fns = makeFns(node, dx, dy, {
        edge: edge,
        x0: x0,
        y0: y0,
        nsteps: nsteps,
        noCover: noCover
    });

    return fns.start().then(delay(timeDelay)).then(fns.end);
}

function waitForDragCover() {
    return new Promise(function(resolve) {
        var interval = 5;
        var timeout = 5000;

        var id = setInterval(function() {
            var dragCoverNode = document.querySelector('.dragcover');
            if(dragCoverNode) {
                clearInterval(id);
                resolve(dragCoverNode);
            }

            timeout -= interval;
            if(timeout < 0) {
                clearInterval(id);
                throw new Error('waitForDragCover: timeout');
            }
        }, interval);
    });
}

function waitForDragCoverRemoval() {
    return new Promise(function(resolve) {
        var interval = 5;
        var timeout = 5000;

        var id = setInterval(function() {
            var dragCoverNode = document.querySelector('.dragcover');
            if(!dragCoverNode) {
                clearInterval(id);
                resolve(dragCoverNode);
            }

            timeout -= interval;
            if(timeout < 0) {
                clearInterval(id);
                throw new Error('waitForDragCoverRemoval: timeout');
            }
        }, interval);
    });
}

module.exports = drag;
drag.makeFns = makeFns;
drag.waitForDragCover = waitForDragCover;
drag.waitForDragCoverRemoval = waitForDragCoverRemoval;
<commit_msg>generalize assets/drag.js<commit_after>var isNumeric = require('fast-isnumeric');

var mouseEvent = require('./mouse_event');
var touchEvent = require('./touch_event');
var getNodeCoords = require('./get_node_coords');
var delay = require('./delay');

/**
 * Inside `opts`:
 *
 * @param {array of 2 numbers} pos0 :
 *  px position of start of drag motion, default computed using `node` and `edge`
 * @param {DOM element} node :
 *  element to drag on, default found using `pos0`
 * @param {string} edge :
 *  combinations of 'n', 's', 'e', 'w' used to find `pos0` of `node`
 *
 * Set one of:
 * @param {array of arrays of numbers} path :
 *  px position drag path
 * @param {array of 2 numbers} dpos :
 *  px position delta
 * @param {array of 2 numbers} posN :
 *  px position of end of drag motion
 *
 * If using `dpos` or `posN`
 * @param {number} nsteps :
 *  set number of steps to take between `pos0` and `pos0` + `dpos` or `posN`, default is 1
 *
 * @param {boolean} touch :
 *  pass `true` to simulate touch events
 * @param {boolean} shiftKey, altKey, ctrlKey ....
 * pass `true to simulate <shift>, alt, ctrl drag (see ./mouse_event.js for more info)
 *
 * @param {function} clearThrottle :
 *  pass Lib.clearThrottle to clear throttle for all mouse/touch event
 * @param {boolean} noCover :
 *  do not wait for "drag cover" element to start "move" events
 *
 * @return {object}
 *  - {function} start
 *  - {function} end
 */
function makeFns(opts) {
    opts = opts || {};

    var path;

    if(Array.isArray(opts.path) && opts.path.length >= 2 &&
        Array.isArray(opts.path[0]) && Array.isArray(opts.path[1])) {
        path = opts.path;
    } else {
        var nsteps = opts.nsteps || 1;
        var p0, dp;
        var msg = [];

        if(opts.pos0 && isNumeric(opts.pos0[0]) && isNumeric(opts.pos0[1])) {
            p0 = opts.pos0;
        } else if(opts.node) {
            var coords = getNodeCoords(opts.node, opts.edge || '');
            p0 = [coords.x, coords.y];
        } else {
            msg.push('Cannot determine drag path start position from the given options');
        }

        if(opts.dpos && isNumeric(opts.dpos[0]) && isNumeric(opts.dpos[1])) {
            dp = opts.dpos;
        } else if(opts.posN && isNumeric(opts.posN[0]) && isNumeric(opts.posN[1])) {
            dp = [opts.posN[0] - opts.pos0[0], opts.posN[1] - opts.pos0[1]];
        } else {
            msg.push('Cannot determine drag path step from the given options');
        }

        if(msg.length) {
            throw new Error(msg.join('\n'));
        }

        path = [p0];

        for(var i = 1; i <= nsteps; i++) {
            path[i] = [
                p0[0] + i * dp[0] / nsteps,
                p0[1] + i * dp[1] / nsteps
            ];
        }
    }

    function extendOpts(patch) {
        var out = {};
        var k;
        for(k in opts) out[k] = opts[k];
        for(k in patch) out[k] = patch[k];
        return out;
    }

    var dragCoverNode;

    function start() {
        if(opts.clearThrottle) opts.clearThrottle();

        var x0 = path[0][0];
        var y0 = path[0][1];

        var _opts = extendOpts({element: opts.node});

        if(opts.touch) {
            touchEvent('touchstart', x0, y0, _opts);
        } else {
            mouseEvent('mousemove', x0, y0, _opts);
            mouseEvent('mousedown', x0, y0, _opts);
        }

        return (opts.noCover ? Promise.resolve(opts.node) : waitForDragCover())
        .then(function(_dragCoverNode) {
            dragCoverNode = _dragCoverNode;

            var _opts = extendOpts({element: dragCoverNode});

            path.slice(1).forEach(function(p) {
                if(opts.clearThrottle) opts.clearThrottle();
                if(opts.touch) {
                    touchEvent('touchmove', p[0], p[1], _opts);
                } else {
                    mouseEvent('mousemove', p[0], p[1], _opts);
                }
            });
        });
    }

    function end() {
        var iN = path.length - 1;
        var xN = path[iN][0];
        var yN = path[iN][1];

        var _opts = extendOpts({element: dragCoverNode});

        if(opts.touch) {
            touchEvent('touchend', xN, yN, _opts);
        } else {
            mouseEvent('mouseup', xN, yN, _opts);
        }

        return opts.noCover || waitForDragCoverRemoval();
    }

    return {
        start: start,
        end: end
    };
}

/**
 * Inside `opts`:
 *
 * Same as in makeDragFns plus:
 *
 * @param {number} timeDelay :
 *  time delay between drag start promise resolve and drag end call
 */
function drag(opts) {
    var fns = makeFns(opts);
    var timeDelay = opts.timeDelay || 0;
    return fns.start().then(delay(timeDelay)).then(fns.end);
}

function waitForDragCover() {
    return new Promise(function(resolve) {
        var interval = 5;
        var timeout = 5000;

        var id = setInterval(function() {
            var dragCoverNode = document.querySelector('.dragcover');
            if(dragCoverNode) {
                clearInterval(id);
                resolve(dragCoverNode);
            }

            timeout -= interval;
            if(timeout < 0) {
                clearInterval(id);
                throw new Error('waitForDragCover: timeout');
            }
        }, interval);
    });
}

function waitForDragCoverRemoval() {
    return new Promise(function(resolve) {
        var interval = 5;
        var timeout = 5000;

        var id = setInterval(function() {
            var dragCoverNode = document.querySelector('.dragcover');
            if(!dragCoverNode) {
                clearInterval(id);
                resolve(dragCoverNode);
            }

            timeout -= interval;
            if(timeout < 0) {
                clearInterval(id);
                throw new Error('waitForDragCoverRemoval: timeout');
            }
        }, interval);
    });
}

module.exports = drag;
drag.makeFns = makeFns;
drag.waitForDragCover = waitForDragCover;
drag.waitForDragCoverRemoval = waitForDragCoverRemoval;
<|endoftext|>


<commit_before>(function(Factlink) {
    // Timeout will hold the timeout id of the current timeout
    var timeout = null;

    // Function which will return the Selection object
    //@TODO: Add rangy support for IE

    function getTextRange() {
        var d = '';
        if (window.getSelection) {
            d = window.getSelection();
        } else {
            if (document.getSelection) {
                d = document.getSelection();
            } else if (document.selection) {
                d = document.selection.createRange().text;
            }
        }
        return d;
    }

    // We make this a global function so it can be used for direct adding of facts
    // (Right click with chrome-extension)
    Factlink.getSelectionInfo = function() {
        // Get the selection object
        var selection = getTextRange();

        //TODO: Add passage detection here
        return {
            selection: selection,
            passage: ""
        };
    };

    // Prepare a new Factlink
    function prepare(rng, passage, top, left) {
        // Position the frame
        Factlink.remote.position(top, left);
        Factlink.modal.show.method();
    }

    // Bind the actual selecting
    $('body').bind('mouseup', function(e) {
        // If there is an active timeout
        if (timeout) {
            // Clear it!
            window.clearTimeout(timeout);
        }

        // Retrieve all needed info of current selection
        var selectionInfo = Factlink.getSelectionInfo();
        // Get the selection object
        var selection     = selectionInfo.selection;
        // Retrieve the text from the selection
        var text          = selection.getRangeAt(0).toString();

        // Check if the selected text is long enough to be added
        if (text !== undefined && text.length > 1) {
            // We can already start loading the prepare page in the background, this
            // way the script looks smoother
            // TODO: This whole script is due for some big refactors, I don't like the way things are done right now, it doesn't seem consistent and I think it's to hard to understand for other people then the one who wrote it.
            if (FactlinkConfig.modus === "default") {
                Factlink.remote.prepareNewFactlink(text, selectionInfo.passage, window.location.href);
            } else if (FactlinkConfig.modus === "addToFact") {
                Factlink.remote.prepareNewEvidence(text, selectionInfo.passage, window.location.href);
            }

            // Store the time out
            timeout = setTimeout(function() {
                // Retrieve all needed info of current selection
                var selectionInfo = Factlink.getSelectionInfo();
                // Make sure the text is still selected
                var selection     = selectionInfo.selection;

                if (selection.rangeCount > 0) {
                    prepare(selection.getRangeAt(0), selectionInfo.passage, e.clientY, e.clientX);
                }
            },
            500);
        }
    });
})(window.Factlink);
<commit_msg>proxyFrame support fixed<commit_after>(function(Factlink) {
    // Timeout will hold the timeout id of the current timeout
    var timeout = null;

    // Function which will return the Selection object
    //@TODO: Add rangy support for IE

    function getTextRange() {
        var d = '';
        if (window.getSelection) {
            d = window.getSelection();
        } else {
            if (document.getSelection) {
                d = document.getSelection();
            } else if (document.selection) {
                d = document.selection.createRange().text;
            }
        }
        return d;
    }

    // We make this a global function so it can be used for direct adding of facts
    // (Right click with chrome-extension)
    Factlink.getSelectionInfo = function() {
        // Get the selection object
        var selection = getTextRange();

        //TODO: Add passage detection here
        return {
            selection: selection,
            passage: ""
        };
    };

    // Prepare a new Factlink
    function prepare(rng, passage, top, left) {
        // Position the frame
        Factlink.remote.position(top, left);
        Factlink.modal.show.method();
    }

    // Bind the actual selecting
    $('body').bind('mouseup', function(e) {
        // If there is an active timeout
        if (timeout) {
            // Clear it!
            window.clearTimeout(timeout);
        }

        // Retrieve all needed info of current selection
        var selectionInfo = Factlink.getSelectionInfo();
        // Get the selection object
        var selection     = selectionInfo.selection;
        // Retrieve the text from the selection
        var text          = selection.getRangeAt(0).toString();

        // Check if the selected text is long enough to be added
        if (text !== undefined && text.length > 1) {
            // We can already start loading the prepare page in the background, this
            // way the script looks smoother
            // TODO: This whole script is due for some big refactors, I don't like the way things are done right now, it doesn't seem consistent and I think it's to hard to understand for other people then the one who wrote it.
            if (FactlinkConfig.modus === "default") {
                Factlink.remote.prepareNewFactlink(text, selectionInfo.passage, window.location.href);
            } else if (FactlinkConfig.modus === "addToFact") {
                Factlink.remote.prepareNewEvidence(text, selectionInfo.passage, FactlinkConfig.url || window.location.href);
            }

            // Store the time out
            timeout = setTimeout(function() {
                // Retrieve all needed info of current selection
                var selectionInfo = Factlink.getSelectionInfo();
                // Make sure the text is still selected
                var selection     = selectionInfo.selection;

                if (selection.rangeCount > 0) {
                    prepare(selection.getRangeAt(0), selectionInfo.passage, e.clientY, e.clientX);
                }
            },
            500);
        }
    });
})(window.Factlink);
<|endoftext|>


<commit_before>(function ($) {
    $.fn.dragMe = function (options) {
        if (typeof options === 'undefined') {
            options = {};
        }
        var _div = this;
        var _container = $(options.container || 'body');
        _div.off('mousedown.dragMe').on('mousedown.dragMe', function (e) {
            var offset = $(this).offset();
            var x = e.pageX - offset.left;
            var y = e.pageY - offset.top;
            $(window).on('mousemove.dragMe', function (event) {
                var containerOffset = _container.offset();
                var maxBottom = containerOffset.top + _container.outerHeight() - _div.outerHeight();
                var maxRight = containerOffset.left + _container.outerWidth() - _div.outerWidth();
                var top = Math.min(Math.max(containerOffset.top, event.clientY - y), maxBottom);
                var left = Math.min(Math.max(containerOffset.left, event.clientX - x), maxRight);
                _div.css({
                    position: 'absolute',
                    margin: 0,
                    top: top + 'px',
                    left: left + 'px'
                });
            });
        });
        $(window).on('mouseup.dragMe', function (e) {
            $(window).off('mousemove.dragMe');
        });
    }
}(jQuery));<commit_msg>removes margin instead of setting it to 0<commit_after>(function ($) {
    $.fn.dragMe = function (options) {
        if (typeof options === 'undefined') {
            options = {};
        }
        var _div = this;
        var _container = $(options.container || 'body');
        _div.off('mousedown.dragMe').on('mousedown.dragMe', function (e) {
            var offset = $(this).offset();
            var x = e.pageX - offset.left;
            var y = e.pageY - offset.top;
            $(window).on('mousemove.dragMe', function (event) {
                var containerOffset = _container.offset();
                var maxBottom = containerOffset.top + _container.outerHeight() - _div.outerHeight();
                var maxRight = containerOffset.left + _container.outerWidth() - _div.outerWidth();
                var top = Math.min(Math.max(containerOffset.top, event.clientY - y), maxBottom);
                var left = Math.min(Math.max(containerOffset.left, event.clientX - x), maxRight);
                _div.css({
                    position: 'absolute',
                    margin: '',
                    top: top + 'px',
                    left: left + 'px'
                });
            });
        });
        $(window).on('mouseup.dragMe', function (e) {
            $(window).off('mousemove.dragMe');
        });
    }
}(jQuery));<|endoftext|>