<commit_before>import Compose from '~/components/Compose'
import { mount, createStore } from 'helpers/client'

import sinon from 'sinon'
import noSsr from 'nuxt/lib/app/components/no-ssr'

describe('Compose component', () => {
  let store, vm, wrapper
  beforeEach(() => {
    store = createStore()
    store.commit('SET_USER', {
      username: 'foo',
      id: 1,
      storage: {
        available: 0
      }
    })
    wrapper = mount(Compose, {
      mocks: {
        $store: store
      },
      stubs: {
        noSsr,
        picker: true
      }
    })
    vm = wrapper.vm
  })
  describe('postCounter', () => {
    it('default postCounter is 256', () => {
      const remaining = +wrapper.find('[data-test-id="post-counter"]').text()
      expect(remaining).is.equal(256)
    })
    context('when text length is 128', () => {
      it('postCounter equals 128', async () => {
        vm.text = 'a'.repeat(128)
        await new Promise(resolve => setTimeout(resolve, 500))
        const remaining = +wrapper.find('[data-test-id="post-counter"]').text()
        expect(remaining).is.equal(128)
      })
    })
    context('when text length is 256', () => {
      it('postCounter equals 0 ', async () => {
        vm.text = 'a'.repeat(256)
        await new Promise(resolve => setTimeout(resolve, 500))
        const remaining = +wrapper.find('[data-test-id="post-counter"]').text()
        expect(remaining).is.equal(0)
      })
    })
    context('when text length is 266', () => {
      it('postCounter equals -10', async () => {
        vm.text = 'a'.repeat(266)
        await new Promise(resolve => setTimeout(resolve, 500))
        const remaining = +wrapper.find('[data-test-id="post-counter"]').text()
        expect(remaining).is.equal(-10)
      })
    })
  })
  describe('disabled', () => {
    // FIXME
    // disabled textarea and submit button when posted
  })
  // FIXME
  describe('methods', () => {
    describe('setFocus', () => {
      context('when focus prop is object', () => {
        it('can pass object', () => {
          wrapper.setProps({
            focus: [0, 10]
          })
          expect(vm.$props.focus).to.be.deep.equal([0, 10])
        })
        it('can call setCaret', () => {
          wrapper.setProps({
            focus: [0, 10]
          })
          vm.$refs.textarea.setCaret = sinon.stub()
          vm.setFocus()
          expect(vm.$refs.textarea.setCaret.called).to.be.true
        })
      })
      context('when focus prop is string or number', () => {
        it('can pass string or number', () => {
          wrapper.setProps({
            focus: 1
          })
          expect(vm.$props.focus).to.be.equal(1)
          wrapper.setProps({
            focus: '1'
          })
          expect(vm.$props.focus).to.be.equal('1')
        })
        it('can call setCaret', () => {
          wrapper.setProps({
            focus: 1
          })
          vm.$refs.textarea.setCaret = sinon.stub()
          vm.setFocus()
          expect(vm.$refs.textarea.setCaret.called).to.be.true
          wrapper.setProps({
            focus: '1'
          })
          vm.$refs.textarea.setCaret = sinon.stub()
          vm.setFocus()
          expect(vm.$refs.textarea.setCaret.called).to.be.true
        })
      })
      context('when focus prop is boolean', () => {
        it('can pass boolean', () => {
          wrapper.setProps({
            focus: true
          })
          expect(vm.$props.focus).to.be.true
          wrapper.setProps({
            focus: false
          })
          expect(vm.$props.focus).to.be.false
        })
        context('when boolean is true', () => {
          it('can call setCaret', () => {
            wrapper.setProps({
              focus: true
            })
            vm.$refs.textarea.setCaret = sinon.stub()
            vm.setFocus()
            expect(vm.$refs.textarea.setCaret.called).to.be.true
          })
        })
        context('when boolean is false', () => {
          it('cannot call setCaret', () => {
            wrapper.setProps({
              focus: false
            })
            vm.$refs.textarea.setCaret = sinon.stub()
            vm.setFocus()
            expect(vm.$refs.textarea.setCaret.notCalled).to.be.true
          })
        })
      })
    })
    describe('submit button', () => {
      let $submitButton
      beforeEach(() => {
        $submitButton = wrapper.find('button[type="submit"]')
      })
      context('has not text', () => {
        it('disabled ', () => {
          expect($submitButton.attributes().disabled).to.equal('disabled')
        })
      })
      context('has text over 256 characters', () => {
        it('disabled ', () => {
          vm.compiledTextLength = 260
          expect($submitButton.attributes().disabled).to.equal('disabled')
        })
      })
      context('has some photos but has not text', () => {
        it('disabled', () => {
          expect($submitButton.attributes().disabled).to.equal('disabled')
        })
      })
      // TODO: write more tests
      // context('with some text', () => {
      //   it('return promise')
      // })
    })
  })
  describe('picker button', () => {
    it('is visible', () => {
      expect(wrapper.find('.open-emoji-picker').isVisible()).to.be.true
    })
    context('clicked picker button', () => {
      let emojiPicker
      beforeEach(async () => {
        wrapper.find('.open-emoji-picker').trigger('click')
        emojiPicker = wrapper.find({
          ref: 'picker'
        })
      })
      it('emoji palette is visible', () => {
        expect(emojiPicker.isVisible()).is.true
      })
    })
    context('selected any emoji', () => {
      beforeEach(() => {
        wrapper.vm.addEmoji = sinon.stub()
        wrapper.vm.closeEmojiPalette = sinon.stub()
        wrapper.vm.insertText = sinon.stub()
        wrapper.find('.open-emoji-picker').trigger('click')
        wrapper
          .find({
            ref: 'picker'
          })
          .vm.$emit('select')
      })
      it('called addEmoji', () => {
        expect(wrapper.vm.addEmoji.called).to.be.true
      })
    })
    context('addEmoji', () => {
      it('called insertText', async () => {
        wrapper.vm.insertText = sinon.stub()
        wrapper.vm.addEmoji({
          native: '🤔'
        })
        await wrapper.vm.$nextTick()
        expect(wrapper.vm.insertText.called).to.be.true
      })
    })
  })
  describe('add photo button', () => {
    context('when unpaid a user', () => {
      it('is hidden', () => {
        expect(wrapper.find('.add-photo').exists()).is.false
      })
    })
    context('when paid a user', () => {
      it('is shown', () => {
        store.commit('SET_USER', {
          username: 'foo',
          id: 1,
          storage: {
            available: 1
          }
        })
        expect(wrapper.find('.add-photo').exists()).is.true
      })
    })
  })
})
<commit_msg>remove setCaret tests<commit_after>import Compose from '~/components/Compose'
import { mount, createStore } from 'helpers/client'

import sinon from 'sinon'
import noSsr from 'nuxt/lib/app/components/no-ssr'

describe('Compose component', () => {
  let store, vm, wrapper
  beforeEach(() => {
    store = createStore()
    store.commit('SET_USER', {
      username: 'foo',
      id: 1,
      storage: {
        available: 0
      }
    })
    wrapper = mount(Compose, {
      mocks: {
        $store: store
      },
      stubs: {
        noSsr,
        picker: true
      }
    })
    vm = wrapper.vm
  })
  describe('postCounter', () => {
    it('default postCounter is 256', () => {
      const remaining = +wrapper.find('[data-test-id="post-counter"]').text()
      expect(remaining).is.equal(256)
    })
    context('when text length is 128', () => {
      it('postCounter equals 128', async () => {
        vm.text = 'a'.repeat(128)
        await new Promise(resolve => setTimeout(resolve, 500))
        const remaining = +wrapper.find('[data-test-id="post-counter"]').text()
        expect(remaining).is.equal(128)
      })
    })
    context('when text length is 256', () => {
      it('postCounter equals 0 ', async () => {
        vm.text = 'a'.repeat(256)
        await new Promise(resolve => setTimeout(resolve, 500))
        const remaining = +wrapper.find('[data-test-id="post-counter"]').text()
        expect(remaining).is.equal(0)
      })
    })
    context('when text length is 266', () => {
      it('postCounter equals -10', async () => {
        vm.text = 'a'.repeat(266)
        await new Promise(resolve => setTimeout(resolve, 500))
        const remaining = +wrapper.find('[data-test-id="post-counter"]').text()
        expect(remaining).is.equal(-10)
      })
    })
  })
  describe('disabled', () => {
    // FIXME
    // disabled textarea and submit button when posted
  })
  // FIXME
  describe('methods', () => {
    describe('setFocus', () => {
      context('when focus prop is object', () => {
        it('can pass object', () => {
          wrapper.setProps({
            focus: [0, 10]
          })
          expect(vm.$props.focus).to.be.deep.equal([0, 10])
        })
      })
      context('when focus prop is string or number', () => {
        it('can pass string or number', () => {
          wrapper.setProps({
            focus: 1
          })
          expect(vm.$props.focus).to.be.equal(1)
          wrapper.setProps({
            focus: '1'
          })
          expect(vm.$props.focus).to.be.equal('1')
        })
      })
      context('when focus prop is boolean', () => {
        it('can pass boolean', () => {
          wrapper.setProps({
            focus: true
          })
          expect(vm.$props.focus).to.be.true
          wrapper.setProps({
            focus: false
          })
          expect(vm.$props.focus).to.be.false
        })
      })
    })
    describe('submit button', () => {
      let $submitButton
      beforeEach(() => {
        $submitButton = wrapper.find('button[type="submit"]')
      })
      context('has not text', () => {
        it('disabled ', () => {
          expect($submitButton.attributes().disabled).to.equal('disabled')
        })
      })
      context('has text over 256 characters', () => {
        it('disabled ', () => {
          vm.compiledTextLength = 260
          expect($submitButton.attributes().disabled).to.equal('disabled')
        })
      })
      context('has some photos but has not text', () => {
        it('disabled', () => {
          expect($submitButton.attributes().disabled).to.equal('disabled')
        })
      })
      // TODO: write more tests
      // context('with some text', () => {
      //   it('return promise')
      // })
    })
  })
  describe('picker button', () => {
    it('is visible', () => {
      expect(wrapper.find('.open-emoji-picker').isVisible()).to.be.true
    })
    context('clicked picker button', () => {
      let emojiPicker
      beforeEach(async () => {
        wrapper.find('.open-emoji-picker').trigger('click')
        emojiPicker = wrapper.find({
          ref: 'picker'
        })
      })
      it('emoji palette is visible', () => {
        expect(emojiPicker.isVisible()).is.true
      })
    })
    context('selected any emoji', () => {
      beforeEach(() => {
        wrapper.vm.addEmoji = sinon.stub()
        wrapper.vm.closeEmojiPalette = sinon.stub()
        wrapper.vm.insertText = sinon.stub()
        wrapper.find('.open-emoji-picker').trigger('click')
        wrapper
          .find({
            ref: 'picker'
          })
          .vm.$emit('select')
      })
      it('called addEmoji', () => {
        expect(wrapper.vm.addEmoji.called).to.be.true
      })
    })
    context('addEmoji', () => {
      it('called insertText', async () => {
        wrapper.vm.insertText = sinon.stub()
        wrapper.vm.addEmoji({
          native: '🤔'
        })
        await wrapper.vm.$nextTick()
        expect(wrapper.vm.insertText.called).to.be.true
      })
    })
  })
  describe('add photo button', () => {
    context('when unpaid a user', () => {
      it('is hidden', () => {
        expect(wrapper.find('.add-photo').exists()).is.false
      })
    })
    context('when paid a user', () => {
      it('is shown', () => {
        store.commit('SET_USER', {
          username: 'foo',
          id: 1,
          storage: {
            available: 1
          }
        })
        expect(wrapper.find('.add-photo').exists()).is.true
      })
    })
  })
})
<|endoftext|>


<commit_before>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 *
*/

var Promise = require('./Promise'),
    ByteCodes = require('./ByteCodes'),
    Command = require('./Command');

var outputBitfields = [0x01, 0x02, 0x04, 0x08];

var Motor = function(port, transport) {
    if (typeof port == 'undefined') {
        throw new Error('motorIdx is not defined. Use MotorEnum.MOTOR_X ...');
    }

    if (typeof transport == 'undefined') {
        throw new Error('transport is not defined.');
    }

    this.port = port;
    this.transport = transport;
}

Motor.prototype.setSpeed = function(value) {
    var command = new Command(),
        portBitField = outputBitfields[this.port];

    command.writeUint8(ByteCodes.OutputSpeed);
    command.writeUint8(0);
    command.writeUint8(portBitField);
    command.writeSByteLong(value);
    command.writeUint8(ByteCodes.OutputStart);
    command.writeUint8(0);
    command.writeUint8(portBitField);

    return this.transport.sendAsync(command);
}

Motor.prototype.setPower = function(value) {
    var command = new Command(),
        portBitField = outputBitfields[this.port];

    command.writeUint8(ByteCodes.OutputPower);
    command.writeUint8(0);
    command.writeUint8(portBitField);
    command.writeSByteLong(value);

    command.writeUint8(ByteCodes.OutputStart);
    command.writeUint8(0);
    command.writeUint8(portBitField);

    return this.transport.sendAsync(command);
}

Motor.prototype.stop = function (breakAfterStop) {
    if (typeof breakAfterStop == 'undefined') {
        breakAfterStop = 0;
    }

    var command = new Command(),
        portBitField = outputBitfields[this.port];

    command.writeUint8(ByteCodes.OutputStop);
    command.writeUint8(0);
    command.writeUint8(portBitField);
    command.writeUint8(breakAfterStop);

    return this.transport.sendAsync(command);
}

module.exports = Motor;<commit_msg>reverse support for motor<commit_after>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 *
*/

var Promise = require('./Promise'),
    ByteCodes = require('./ByteCodes'),
    Command = require('./Command');

var outputBitfields = [0x01, 0x02, 0x04, 0x08];

var Motor = function(port, transport) {
    if (typeof port == 'undefined') {
        throw new Error('motorIdx is not defined. Use MotorEnum.MOTOR_X ...');
    }

    if (typeof transport == 'undefined') {
        throw new Error('transport is not defined.');
    }

    this.port = port;
    this.transport = transport;
    this.isReversed = false;
}

Motor.prototype.setSpeed = function(value) {
    var command = new Command(),
        portBitField = outputBitfields[this.port];

    if (this.isReversed) {
        value = -value;
    }

    command.writeUint8(ByteCodes.OutputSpeed);
    command.writeUint8(0);
    command.writeUint8(portBitField);
    command.writeSByteLong(value);
    command.writeUint8(ByteCodes.OutputStart);
    command.writeUint8(0);
    command.writeUint8(portBitField);

    return this.transport.sendAsync(command);
}

// Motor.prototype.setPower = function(value) {
//     var command = new Command(),
//         portBitField = outputBitfields[this.port];

//     command.writeUint8(ByteCodes.OutputPower);
//     command.writeUint8(0);
//     command.writeUint8(portBitField);
//     command.writeSByteLong(value);

//     command.writeUint8(ByteCodes.OutputStart);
//     command.writeUint8(0);
//     command.writeUint8(portBitField);

//     return this.transport.sendAsync(command);
// }

Motor.prototype.stop = function (breakAfterStop) {
    if (typeof breakAfterStop == 'undefined') {
        breakAfterStop = 0;
    }

    var command = new Command(),
        portBitField = outputBitfields[this.port];

    command.writeUint8(ByteCodes.OutputStop);
    command.writeUint8(0);
    command.writeUint8(portBitField);
    command.writeUint8(breakAfterStop);

    return this.transport.sendAsync(command);
}

Motor.prototype.reverse = function (isReversed) {
    if (typeof isReversed == 'undefined') {
        this.isReversed = !this.isReversed;
    } else {
        this.isReversed = isReversed;
    }
}

module.exports = Motor;<|endoftext|>


<commit_before>define('lyria/achievement/manager', ['jquery', 'lyria/achievement', 'lyria/template/engine', 'lyria/template/list', 'lyria/localization'], function($, Achievement, TemplateEngine, templateList, Localization) {

  var achievementStore = {};

  var AchievementManager = {
    localization: new Localization(),
    add: function(achievement) {
      if ( achievement instanceof Achievement) {
        if (!Object.hasOwnProperty.call(achievementStore, achievement.name)) {
          achievementStore[achievement.name] = achievement;
        }
      }
    },
    progressSeparator: '/',
    viewport: null,
    remove: function(achName) {
      if (Object.hasOwnProperty.call(achievementStore, achName)) {
        delete achievementStore[achName];
      }
    },
    list: function() {
      //TemplateEngine.compile()
    },
    show: function(achName) {
      var currentAchievement = achievementStore[achName];

      if (currentAchievement == null) {
        throw new Error('Achievement ' + achName + ' not found.');
      }

      var title = (AchievementManager.localization.exists(currentAchievement.name)) ? AchievementManager.localization.t(currentAchievement.name) : currentAchievement.name;
      var description = (AchievementManager.localization.exists(currentAchievement.name + '-description')) ? AchievementManager.localization.t(currentAchievement.name + '-description') : currentAchievement.description;

      var achTemplate = TemplateEngine.compile(templateList['achievement'])({
        id: currentAchievement.id,
        className: currentAchievement.name,
        title: title,
        description: description,
        offscreen: true,
        progressable: (currentAchievement.progress.max > 0 && currentAchievement.progress.max !== 1),
        max: currentAchievement.progress.max,
        current: currentAchievement.progress.current,
        separator: AchievementManager.progressSeparator
      });

      var $currentAchievement = $('#' + currentAchievement.id);

      if (AchievementManager.viewport == null) {
        $('body').append(achTemplate);
      } else {
        AchievementManager.viewport.$element.append(achTemplate);
      }

      // Refactor this if lyria/tween is available
      $currentAchievement.animate({
        opacity: 1.0
      }, 600).delay(2000).animate({
        opacity: 0.0
      }, 600, function() {
        $currentAchievement.remove();
      });
    },
    toJSON: function() {
      var key, value;
      var result = {};

      for (key in achievementStore) {
        value = achievementStore[key];
        result[key] = value.toJSON();
      }

      return result;
    },
    toString: function() {
      var result = '';

      try {
        result = JSON.stringify(AchievementManager.toJSON());
      } catch (e) {
        throw new Error('Error while serializing achievements in AchievementManager: ' + e);
      }
      return result;
    },
    fromJSON: function(achievements) {
      var key, value;

      for (key in achievements) {
        value = achievements[key];
        AchievementManager.add(value);
      }
    },
    fromString: function(achievements) {
      var deserializedValue = {};

      try {
        deserializedValue = JSON.parse(achievements);
      } catch (e) {
        throw new Error('Error while deserializing achivements in AchievementManager: ' + e);
      }

      return AchievementManager.fromJSON(deserializedValue);
    }
  };

  Object.defineProperty(AchievementManager, 'length', {
    get: function() {
      return Object.keys(achievementStore).length;
    }
  });

  Object.defineProperty(AchievementManager, 'unlockedCount', {
    get: function() {
      var counter = 0;

      $.each(achievementStore, function(key, value) {
        if (value.unlocked) {
          counter++;
        }
      });

      return counter;
    }
  });

  return AchievementManager;

});
<commit_msg>Fixes #30, fixes #19, fixes #11<commit_after>define('lyria/achievement/manager', ['jquery', 'lyria/achievement', 'lyria/template/engine', 'lyria/template/list', 'lyria/localization'], function($, Achievement, TemplateEngine, templateList, Localization) {

  var achievementStore = {};

  var AchievementManager = {
    localization: new Localization(),
    add: function(achievement) {
      if ( achievement instanceof Achievement) {
        if (!Object.hasOwnProperty.call(achievementStore, achievement.name)) {
          achievementStore[achievement.name] = achievement;
        }
      }
    },
    progressSeparator: '/',
    viewport: null,
    remove: function(achName) {
      if (Object.hasOwnProperty.call(achievementStore, achName)) {
        delete achievementStore[achName];
      }
    },
    list: function() {
      //TemplateEngine.compile()
    },
    show: function(achName) {
      var currentAchievement = achievementStore[achName];

      if (currentAchievement == null) {
        throw new Error('Achievement ' + achName + ' not found.');
      }

      var title = (AchievementManager.localization.exists(currentAchievement.name)) ? AchievementManager.localization.t(currentAchievement.name) : currentAchievement.name;
      var description = (AchievementManager.localization.exists(currentAchievement.name + '-description')) ? AchievementManager.localization.t(currentAchievement.name + '-description') : currentAchievement.description;

      var achTemplate = TemplateEngine.compile(templateList['achievement'])({
        id: currentAchievement.id,
        className: currentAchievement.name,
        title: title,
        description: description,
        offscreen: true,
        progressable: (currentAchievement.progress.max > 0 && currentAchievement.progress.max !== 1),
        max: currentAchievement.progress.max,
        current: currentAchievement.progress.current,
        separator: AchievementManager.progressSeparator
      });

      var $currentAchievement = $('#' + currentAchievement.id);

      if (AchievementManager.viewport == null) {
        $('body').append(achTemplate);
      } else {
        AchievementManager.viewport.$element.append(achTemplate);
      }

      // Refactor this if lyria/tween is available
      $currentAchievement.animate({
        opacity: 1.0
      }, 600).delay(2000).animate({
        opacity: 0.0
      }, 600, function() {
        $currentAchievement.remove();
      });
    },
    toJSON: function() {
      var key, value;
      var result = {};

      for (key in achievementStore) {
        value = achievementStore[key];
        result[key] = value.toJSON();
      }

      return result;
    },
    toString: function() {
      var result = '';

      try {
        result = JSON.stringify(AchievementManager.toJSON());
      } catch (e) {
        throw new Error('Error while serializing achievements in AchievementManager: ' + e);
      }
      return result;
    },
    fromJSON: function(achievements) {
      var key, value;

      for (key in achievements) {
        value = achievements[key];
        AchievementManager.add(value);
      }
    },
    fromString: function(achievements) {
      var deserializedValue = {};

      try {
        deserializedValue = JSON.parse(achievements);
      } catch (e) {
        throw new Error('Error while deserializing achivements in AchievementManager: ' + e);
      }

      return AchievementManager.fromJSON(deserializedValue);
    }
  };

  Object.defineProperty(AchievementManager, 'length', {
    get: function() {
      return Object.keys(achievementStore).length;
    }
  });

  Object.defineProperty(AchievementManager, 'unlockedCount', {
    get: function() {
      var counter = 0;

      $.each(achievementStore, function(key, value) {
        if (value.unlocked) {
          counter++;
        }
      });

      return counter;
    }
  });

  Object.defineProperty(AchievementManager, 'store', {
    get: function() {
      return achievementStore;
    }
  });

  return AchievementManager;

});
<|endoftext|>


<commit_before>define('lyria/achievement/manager', ['root', 'jquery', 'lyria/achievement'], function(root, $, Achievement) {

  var achievementStore = {};

  var AchievementManager = {
    add: function(achievement) {
      if (achievement instanceof Achievement) {
        achievementStore[achievement.name] = achievement;
      }
    },
    remove: function(achName) {
      if (Object.hasOwnProperty.call(achievementStore, achName)) {
        delete achievementStore[achName];
      }
    },
    list: function() {

    },
    show: function(achName) {

    }
  };

  return AchievementManager;

});
<commit_msg>Removed unnecessary dependency to root in lyria/achievement/manager<commit_after>define('lyria/achievement/manager', ['jquery', 'lyria/achievement', 'lyria/template/engine'], function($, Achievement, TemplateEngine) {

  var achievementStore = {};

  var AchievementManager = {
    add: function(achievement) {
      if (achievement instanceof Achievement) {
        achievementStore[achievement.name] = achievement;
      }
    },
    remove: function(achName) {
      if (Object.hasOwnProperty.call(achievementStore, achName)) {
        delete achievementStore[achName];
      }
    },
    list: function() {
      //TemplateEngine.compile()
    },
    show: function(achName) {
      //TemplateEngine.compile();
    },
    toJSON: function() {
      //return achievementStore;
    },
    toString: function() {
      return JSON.stringify(AchievementManager.toJSON());
    },
    templates: {
      achievement: '',
      list: ''
    }
  };

  return AchievementManager;

});
<|endoftext|>


<commit_before>import axios from "axios";
import {history} from './../history';

export const setUser = (user)=>{
    return (dispatch)=>{
        dispatch({type:'SET_USER', payload:user});
        history.push('/about');
    }
}

export const getUsers = ()=>{
    return {
       type: 'GET_POST',
       payload:axios.get("https://jsonplaceholder.typicode.com/posts")
    }
    // return (dispatch)=>{
    //     axios.get("https://jsonplaceholder.typicode.com/posts")
    //         .then((data)=>{
    //             dispatch({
    //                 type:'GET_POSTS_DATA',
    //                 payload:data
    //             });
    //         })
    // }
}
export const setCurrentName = (name)=> {
    return {
        type:'SET_CURRENT_NAME',
        payload:name
    };
}

export const setCurrentAge = (age)=>{
    return {
        type:'SET_CURRENT_AGE',
        payload:age
    };
}<commit_msg>remove space<commit_after>import axios from "axios";
import {history} from './../history';
export const setUser = (user)=>{
    return (dispatch)=>{
        dispatch({type:'SET_USER', payload:user});
        history.push('/about');
    }
}

export const getUsers = ()=>{
    return {
       type: 'GET_POST',
       payload:axios.get("https://jsonplaceholder.typicode.com/posts")
    }
    // return (dispatch)=>{
    //     axios.get("https://jsonplaceholder.typicode.com/posts")
    //         .then((data)=>{
    //             dispatch({
    //                 type:'GET_POSTS_DATA',
    //                 payload:data
    //             });
    //         })
    // }
}
export const setCurrentName = (name)=> {
    return {
        type:'SET_CURRENT_NAME',
        payload:name
    };
}

export const setCurrentAge = (age)=>{
    return {
        type:'SET_CURRENT_AGE',
        payload:age
    };
}<|endoftext|>


<commit_before>/**
 * indexed db adapter
 * ===
 * - originally authored by Vivian Li
 *
 */

Lawnchair.adapter('indexed-db', (function(){

  function fail(e, i) { console.error('error in indexed-db adapter!', e, i); }

  var STORE_NAME = 'lawnchair';

  // update the STORE_VERSION when the schema used by this adapter changes
  // (for example, if you change the STORE_NAME above)
  var STORE_VERSION = 2;

  var getIDB = function() {
    return window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.oIndexedDB || window.msIndexedDB;
  };
  var getIDBTransaction = function() {
      return window.IDBTransaction || window.webkitIDBTransaction ||
          window.mozIDBTransaction || window.oIDBTransaction ||
          window.msIDBTransaction;
  };
  var getIDBKeyRange = function() {
      return window.IDBKeyRange || window.webkitIDBKeyRange ||
          window.mozIDBKeyRange || window.oIDBKeyRange ||
          window.msIDBKeyRange;
  };

  // see https://groups.google.com/a/chromium.org/forum/?fromgroups#!topic/chromium-html5/OhsoAQLj7kc
  var READ_WRITE = (getIDBTransaction() &&
                    'READ_WRITE' in getIDBTransaction()) ?
    getIDBTransaction().READ_WRITE : 'readwrite';

  return {

    valid: function() { return !!getIDB(); },

    init:function(options, callback) {
        this.idb = getIDB();
        this.waiting = [];
        var request = this.idb.open(this.name, STORE_VERSION);
        var self = this;
        var cb = self.fn(self.name, callback);
        var win = function() {
            // manually clean up event handlers on request; this helps on chrome
            request.onupgradeneeded = request.onsuccess = request.error = null;
            return cb.call(self, self);
        };

        var upgrade = function(from, to) {
            // don't try to migrate dbs, just recreate
            try {
                self.db.deleteObjectStore('teststore'); // old adapter
            } catch (e1) { /* ignore */ }
            try {
                self.db.deleteObjectStore(STORE_NAME);
            } catch (e2) { /* ignore */ }

            // ok, create object store.
            self.store = self.db.createObjectStore(STORE_NAME,
                                                   { autoIncrement: true} );
            for (var i = 0; i < self.waiting.length; i++) {
                self.waiting[i].call(self);
            }
            self.waiting = [];
            win();
        };
        request.onupgradeneeded = function(event) {
            upgrade(event.oldVersion, event.newVersion);
        };
        request.onsuccess = function(event) {
           self.db = request.result;

            if(self.db.version != (''+STORE_VERSION)) {
              // DEPRECATED API: modern implementations will fire the
              // upgradeneeded event instead.
              var oldVersion = self.db.version;
              var setVrequest = self.db.setVersion(''+STORE_VERSION);
              // onsuccess is the only place we can create Object Stores
              setVrequest.onsuccess = function(e) {
                  setVrequest.onsuccess = setVrequest.onerror = null;
                  upgrade(oldVersion, STORE_VERSION);
              };
              setVrequest.onerror = function(e) {
                  setVrequest.onsuccess = setVrequest.onerror = null;
                  console.log("Failed to create objectstore " + e);
                  fail(e);
              }
            } else {
                self.store = {};
                for (var i = 0; i < self.waiting.length; i++) {
                      self.waiting[i].call(self);
                }
                self.waiting = [];
                win();
            }
        }
        request.onerror = fail;
    },

    save:function(obj, callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.save(obj, callback);
            });
            return;
         }

         var self = this, request;
         var win  = function (e) {
             // manually clean up event handlers; helps free memory on chrome.
             request.onsuccess = request.onerror = null;
             if (callback) { obj.key = e.target.result; self.lambda(callback).call(self, obj) }
         };

         var trans = this.db.transaction(STORE_NAME, READ_WRITE);
         var store = trans.objectStore(STORE_NAME);
         request = obj.key ? store.put(obj, obj.key) : store.put(obj);

         request.onsuccess = win;
         request.onerror = fail;

         return this;
    },

    // FIXME this should be a batch insert / just getting the test to pass...
    batch: function (objs, cb) {

        var results = []
        ,   done = false
        ,   self = this

        var updateProgress = function(obj) {
            results.push(obj)
            done = results.length === objs.length
        }

        var checkProgress = setInterval(function() {
            if (done) {
                if (cb) self.lambda(cb).call(self, results)
                clearInterval(checkProgress)
            }
        }, 200)

        for (var i = 0, l = objs.length; i < l; i++)
            this.save(objs[i], updateProgress)

        return this
    },

    get:function(key, callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.get(key, callback);
            });
            return;
        }

        var self = this;
        var win  = function (e) { if (callback) { self.lambda(callback).call(self, e.target.result) }};

        if (!this.isArray(key)){
            var req = this.db.transaction(STORE_NAME).objectStore(STORE_NAME).get(key);

            req.onsuccess = function(event) {
                req.onsuccess = req.onerror = null;
                win(event);
            };
            req.onerror = function(event) {
                console.log("Failed to find " + key);
                req.onsuccess = req.onerror = null;
                fail(event);
            };

        // FIXME: again the setInterval solution to async callbacks..
        } else {

            // note: these are hosted.
            var results = []
            ,   done = false
            ,   keys = key

            var updateProgress = function(obj) {
                results.push(obj)
                done = results.length === keys.length
                if (done) {
                    self.lambda(callback).call(self, results);
                }
            }

            for (var i = 0, l = keys.length; i < l; i++)
                this.get(keys[i], updateProgress)

        }

        return this;
    },

    exists:function(key, callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.exists(key, callback);
            });
            return;
        }

        var self = this;

        var req = this.db.transaction(STORE_NAME).objectStore(STORE_NAME).openCursor(getIDBKeyRange().only(key));

        req.onsuccess = function(event) {
            req.onsuccess = req.onerror = null;
            // exists iff req.result is not null
            // XXX but firefox returns undefined instead, sigh XXX
            var undef;
            self.lambda(callback).call(self, event.target.result !== null &&
                                             event.target.result !== undef);
        };
        req.onerror = function(event) {
            req.onsuccess = req.onerror = null;
            console.log("Failed to test for " + key);
            fail(event);
        };

        return this;
    },

    all:function(callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.all(callback);
            });
            return;
        }
        var cb = this.fn(this.name, callback) || undefined;
        var self = this;
        var objectStore = this.db.transaction(STORE_NAME).objectStore(STORE_NAME);
        var toReturn = [];
        objectStore.openCursor().onsuccess = function(event) {
          var cursor = event.target.result;
          if (cursor) {
               toReturn.push(cursor.value);
               cursor['continue']();
          }
          else {
              if (cb) cb.call(self, toReturn);
          }
        };
        return this;
    },

    remove:function(keyOrObj, callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.remove(keyOrObj, callback);
            });
            return;
        }
        if (typeof keyOrObj == "object") {
            keyOrObj = keyOrObj.key;
        }
        var self = this, request;
        var win  = function () {
            request.onsuccess = request.onerror = null;
            if (callback) self.lambda(callback).call(self)
        };

        request = this.db.transaction(STORE_NAME, READ_WRITE).objectStore(STORE_NAME)['delete'](keyOrObj);
        request.onsuccess = win;
        request.onerror = fail;
        return this;
    },

    nuke:function(callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.nuke(callback);
            });
            return;
        }

        var self = this
        ,   win  = callback ? function() { self.lambda(callback).call(self) } : function(){};

        try {
            this.db
                .transaction(STORE_NAME, READ_WRITE)
                .objectStore(STORE_NAME).clear().onsuccess = win;

        } catch(e) {
            fail();
        }
        return this;
    }

  };

})());
<commit_msg>IndexedDB: Update 'versionchange' to match latest spec (Chrome 21, Firefox).<commit_after>/**
 * indexed db adapter
 * ===
 * - originally authored by Vivian Li
 *
 */

Lawnchair.adapter('indexed-db', (function(){

  function fail(e, i) { console.error('error in indexed-db adapter!', e, i); }

  var STORE_NAME = 'lawnchair';

  // update the STORE_VERSION when the schema used by this adapter changes
  // (for example, if you change the STORE_NAME above)
  var STORE_VERSION = 2;

  var getIDB = function() {
    return window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.oIndexedDB || window.msIndexedDB;
  };
  var getIDBTransaction = function() {
      return window.IDBTransaction || window.webkitIDBTransaction ||
          window.mozIDBTransaction || window.oIDBTransaction ||
          window.msIDBTransaction;
  };
  var getIDBKeyRange = function() {
      return window.IDBKeyRange || window.webkitIDBKeyRange ||
          window.mozIDBKeyRange || window.oIDBKeyRange ||
          window.msIDBKeyRange;
  };

  // see https://groups.google.com/a/chromium.org/forum/?fromgroups#!topic/chromium-html5/OhsoAQLj7kc
  var READ_WRITE = (getIDBTransaction() &&
                    'READ_WRITE' in getIDBTransaction()) ?
    getIDBTransaction().READ_WRITE : 'readwrite';

  return {

    valid: function() { return !!getIDB(); },

    init:function(options, callback) {
        this.idb = getIDB();
        this.waiting = [];
        var request = this.idb.open(this.name, STORE_VERSION);
        var self = this;
        var cb = self.fn(self.name, callback);
        var win = function() {
            // manually clean up event handlers on request; this helps on chrome
            request.onupgradeneeded = request.onsuccess = request.error = null;
            return cb.call(self, self);
        };

        var upgrade = function(from, to) {
            // don't try to migrate dbs, just recreate
            try {
                self.db.deleteObjectStore('teststore'); // old adapter
            } catch (e1) { /* ignore */ }
            try {
                self.db.deleteObjectStore(STORE_NAME);
            } catch (e2) { /* ignore */ }

            // ok, create object store.
            self.db.createObjectStore(STORE_NAME, { autoIncrement: true});
            self.store = true;
        };
        request.onupgradeneeded = function(event) {
            self.db = request.result;
            self.transaction = request.transaction;
            upgrade(event.oldVersion, event.newVersion);
            // will end up in onsuccess callback
        };
        request.onsuccess = function(event) {
           self.db = request.result;

            if(self.db.version != (''+STORE_VERSION)) {
              // DEPRECATED API: modern implementations will fire the
              // upgradeneeded event instead.
              var oldVersion = self.db.version;
              var setVrequest = self.db.setVersion(''+STORE_VERSION);
              // onsuccess is the only place we can create Object Stores
              setVrequest.onsuccess = function(event) {
                  var transaction = setVrequest.result;
                  setVrequest.onsuccess = setVrequest.onerror = null;
                  // can't upgrade w/o versionchange transaction.
                  upgrade(oldVersion, STORE_VERSION);
                  transaction.oncomplete = function() {
                      for (var i = 0; i < self.waiting.length; i++) {
                          self.waiting[i].call(self);
                      }
                      self.waiting = [];
                      win();
                  };
              };
              setVrequest.onerror = function(e) {
                  setVrequest.onsuccess = setVrequest.onerror = null;
                  console.log("Failed to create objectstore " + e);
                  fail(e);
              };
            } else {
                self.store = true;
                for (var i = 0; i < self.waiting.length; i++) {
                      self.waiting[i].call(self);
                }
                self.waiting = [];
                win();
            }
        }
        request.onerror = fail;
    },

    save:function(obj, callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.save(obj, callback);
            });
            return;
         }

         var self = this, request;
         var win  = function (e) {
             // manually clean up event handlers; helps free memory on chrome.
             request.onsuccess = request.onerror = null;
             if (callback) { obj.key = e.target.result; self.lambda(callback).call(self, obj) }
         };

         var trans = this.db.transaction(STORE_NAME, READ_WRITE);
         var store = trans.objectStore(STORE_NAME);
         request = obj.key ? store.put(obj, obj.key) : store.put(obj);

         request.onsuccess = win;
         request.onerror = fail;

         return this;
    },

    // FIXME this should be a batch insert / just getting the test to pass...
    batch: function (objs, cb) {

        var results = []
        ,   done = false
        ,   self = this

        var updateProgress = function(obj) {
            results.push(obj)
            done = results.length === objs.length
        }

        var checkProgress = setInterval(function() {
            if (done) {
                if (cb) self.lambda(cb).call(self, results)
                clearInterval(checkProgress)
            }
        }, 200)

        for (var i = 0, l = objs.length; i < l; i++)
            this.save(objs[i], updateProgress)

        return this
    },

    get:function(key, callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.get(key, callback);
            });
            return;
        }

        var self = this;
        var win  = function (e) { if (callback) { self.lambda(callback).call(self, e.target.result) }};

        if (!this.isArray(key)){
            var req = this.db.transaction(STORE_NAME).objectStore(STORE_NAME).get(key);

            req.onsuccess = function(event) {
                req.onsuccess = req.onerror = null;
                win(event);
            };
            req.onerror = function(event) {
                console.log("Failed to find " + key);
                req.onsuccess = req.onerror = null;
                fail(event);
            };

        // FIXME: again the setInterval solution to async callbacks..
        } else {

            // note: these are hosted.
            var results = []
            ,   done = false
            ,   keys = key

            var updateProgress = function(obj) {
                results.push(obj)
                done = results.length === keys.length
                if (done) {
                    self.lambda(callback).call(self, results);
                }
            }

            for (var i = 0, l = keys.length; i < l; i++)
                this.get(keys[i], updateProgress)

        }

        return this;
    },

    exists:function(key, callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.exists(key, callback);
            });
            return;
        }

        var self = this;

        var req = this.db.transaction(STORE_NAME).objectStore(STORE_NAME).openCursor(getIDBKeyRange().only(key));

        req.onsuccess = function(event) {
            req.onsuccess = req.onerror = null;
            // exists iff req.result is not null
            // XXX but firefox returns undefined instead, sigh XXX
            var undef;
            self.lambda(callback).call(self, event.target.result !== null &&
                                             event.target.result !== undef);
        };
        req.onerror = function(event) {
            req.onsuccess = req.onerror = null;
            console.log("Failed to test for " + key);
            fail(event);
        };

        return this;
    },

    all:function(callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.all(callback);
            });
            return;
        }
        var cb = this.fn(this.name, callback) || undefined;
        var self = this;
        var objectStore = this.db.transaction(STORE_NAME).objectStore(STORE_NAME);
        var toReturn = [];
        objectStore.openCursor().onsuccess = function(event) {
          var cursor = event.target.result;
          if (cursor) {
               toReturn.push(cursor.value);
               cursor['continue']();
          }
          else {
              if (cb) cb.call(self, toReturn);
          }
        };
        return this;
    },

    remove:function(keyOrObj, callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.remove(keyOrObj, callback);
            });
            return;
        }
        if (typeof keyOrObj == "object") {
            keyOrObj = keyOrObj.key;
        }
        var self = this, request;
        var win  = function () {
            request.onsuccess = request.onerror = null;
            if (callback) self.lambda(callback).call(self)
        };

        request = this.db.transaction(STORE_NAME, READ_WRITE).objectStore(STORE_NAME)['delete'](keyOrObj);
        request.onsuccess = win;
        request.onerror = fail;
        return this;
    },

    nuke:function(callback) {
        if(!this.store) {
            this.waiting.push(function() {
                this.nuke(callback);
            });
            return;
        }

        var self = this
        ,   win  = callback ? function() { self.lambda(callback).call(self) } : function(){};

        try {
            this.db
                .transaction(STORE_NAME, READ_WRITE)
                .objectStore(STORE_NAME).clear().onsuccess = win;

        } catch(e) {
            fail();
        }
        return this;
    }

  };

})());
<|endoftext|>


<commit_before>/*
Name: mag-komposer v0.2.1
Description: side loading props based on react-komposer (https://github.com/kadirahq/react-komposer)
Let's compose MagJS containers and feed data into components.
Author: Michael Glazer
License: MIT
Homepage: https://github.com/magnumjs/mag.js
@requires mag.js(v0.25.7) & mag addons 0.22.5
(c) 2017
Simple Example: http://embed.plnkr.co/j4HjdnopEqw0LHZI5151/
Full Example: http://embed.plnkr.co/YZq93tsHKiIUPSBFtdPI/
*/

mag.komposer = function(handlerFunc, loadingComp, errorComp) {
  return function(composed) {
    var lnode = document.createElement('div');
    lnode.appendChild(lnode.cloneNode());

    loadingComp = loadingComp || mag.create(lnode, {
      view: function(state) {
        state.div = 'Loading...';
      }
    });

    //Default error component can be over ridden:
    var enode = document.createElement('div');
    enode.appendChild(enode.cloneNode());

    errorComp = errorComp || mag.create(enode, {
      view: function(state, props) {
        state.div = {
          _text: props.message,
          _style: 'color:red',
        };
      }
    });

    var cleaners = [],
      errors = [],
      loading = [];
    var _subscribe = function(props, instanceID) {
      if (cleaners[props.key]) cleaners[props.key]();
      cleaners[props.key] = handlerFunc(props, function(key, ids, nprops) {

        loading[key] = false;
        var cp = composed().getProps(ids);

        if (nprops instanceof Error) {
          errors[key] = true;
          error = {};
          Object.getOwnPropertyNames(nprops).forEach(function(skey) {
            error[skey] = nprops[skey];
          });
          mag.merge(cp, error);

        } else {
          mag.merge(cp, nprops);
        }

        requestAnimationFrame(function() {
          if (mag.getNode(mag.getId(ids))) {
            mag.redraw(mag.getNode(mag.getId(ids)), ids);
          }
        })
      }.bind({}, props.key, instanceID));
    };

    var prevProps = [];
    var hoc = {
      controller: function(props) {
        if (!props.key) props.key = performance.now();
        loading[props.key] = true;

        this.willload = function() {
          _subscribe(arguments[1], arguments[2])
        };

        this.willgetprops = function() {
          _subscribe(arguments[3], arguments[2])
        };
        this.willupdate = function(node, newProps) {
          // prevent layout thrashing on init when no props
          //TODO: real use case?
          // if (Object.keys(newProps).length < 1) e.preventDefault();

          //don't rerun when unecessary
          if (newProps.key && JSON.stringify(prevProps[newProps.key]) === JSON.stringify(newProps)) {
            e.preventDefault();
          } else {
            prevProps[newProps.key] = mag.utils.copy(newProps);
          }
        }
      },
      view: function(state, props) {

        if (loading[props.key]) {
          state.div = loadingComp(props);
          return;
        }
        //is error?
        if (errors[props.key]) {
          state.div = errorComp(props);
          return;
        }
        //is loading?
        state.div = composed(props);
      }
    };

    // virtual node
    var node = document.createElement('div');
    node.appendChild(node.cloneNode())

    return mag.create(node, hoc);
  }
};
<commit_msg>Update mag-komposer.js<commit_after>/*
Name: mag-komposer v0.2.1
Description: side loading props based on react-komposer (https://github.com/kadirahq/react-komposer)
Let's compose MagJS containers and feed data into components.
Author: Michael Glazer
License: MIT
Homepage: https://github.com/magnumjs/mag.js
@requires mag.js(v0.25.7) & mag addons 0.22.5
(c) 2017
Simple Example: http://embed.plnkr.co/j4HjdnopEqw0LHZI5151/
Full Example: http://embed.plnkr.co/YZq93tsHKiIUPSBFtdPI/
*/

mag.komposer = function(handlerFunc, loadingComp, errorComp) {
  return function(composed) {
    var lnode = document.createElement('div');
    lnode.appendChild(lnode.cloneNode());

    loadingComp = loadingComp || mag.create(lnode, {
      view: function(state) {
        state.div = 'Loading...';
      }
    });

    //Default error component can be over ridden:
    var enode = document.createElement('div');
    enode.appendChild(enode.cloneNode());

    errorComp = errorComp || mag.create(enode, {
      view: function(state, props) {
        state.div = {
          _text: props.message,
          _style: 'color:red',
        };
      }
    });

    var cleaners = [],
      errors = [],
      loading = [];
    var _subscribe = function(props, instanceID) {
      if (cleaners[props.key]) cleaners[props.key]();
      cleaners[props.key] = handlerFunc(props, function(key, ids, nprops) {

        loading[key] = false;
        var cp = composed().getProps(ids);

        if (nprops instanceof Error) {
          errors[key] = true;
          error = {};
          Object.getOwnPropertyNames(nprops).forEach(function(skey) {
            error[skey] = nprops[skey];
          });
          mag.merge(cp, error);

        } else {
          mag.merge(cp, nprops);
        }

        requestAnimationFrame(function() {
          if (mag.getNode(mag.getId(ids))) {
            mag.redraw(mag.getNode(mag.getId(ids)), ids);
          }
        })
      }.bind({}, props.key, instanceID));
    };

    var prevProps = [];
    var hoc = {
      controller: function(props) {
        if (!props.key) props.key = performance.now();
        loading[props.key] = true;

        this.willload = function(node, cprops, instanceId) {
          _subscribe(cprops, instanceId)
        };

        this.willgetprops = function(node, cprops, instanceId, nprops) {
          _subscribe(nprops, cprops)
        };
        this.willupdate = function(node, newProps) {
          // prevent layout thrashing on init when no props
          //TODO: real use case?

          //don't rerun when unecessary
          if (newProps.key && JSON.stringify(prevProps[newProps.key]) === JSON.stringify(newProps)) {
            return false;
          } else {
            prevProps[newProps.key] = mag.copy(newProps);
          }
        }
      },
      view: function(state, props) {
        //is loading?
        if (loading[props.key]) {
          state.div = loadingComp(props);
          return;
        }
        //is error?
        if (errors[props.key]) {
          state.div = errorComp(props);
          return;
        }
        state.div = composed(props);
      }
    };

    // virtual node
    var node = document.createElement('div');
    node.appendChild(node.cloneNode())

    return mag.create(node, hoc);
  }
};
<|endoftext|>


<commit_before>/*
 * Handles requests to Windows Service.
 *
 * Copyright 2018 Raising the Floor - International
 *
 * Licensed under the New BSD license. You may not use this file except in
 * compliance with this License.
 *
 * The R&D leading to these results received funding from the
 * Department of Education - Grant H421A150005 (GPII-APCP). However,
 * these results do not necessarily represent the policy of the
 * Department of Education, and you should not assume endorsement by the
 * Federal Government.
 *
 * You may obtain a copy of the License at
 * https://github.com/GPII/universal/blob/master/LICENSE.txt
 */

"use strict";

var fluid = require("gpii-universal");

var gpii = fluid.registerNamespace("gpii");
fluid.registerNamespace("gpii.windows.service.serviceHandler");

// Handles requests to the service.
fluid.defaults("gpii.windows.service.actions", {
    gradeNames: ["fluid.component" ],
    invokers: {
        sendRequest: {
            funcName: "gpii.windows.service.sendRequest",
            // action, requestData
            args: [ "{serviceHandler}", "{arguments}.0", "{arguments}.1" ]
        },
        echo: {
            func: "{that}.sendRequest",
            args: [ "echo", "{arguments}.0" ]
        },
        execute: {
            funcName: "gpii.windows.service.actions.execute",
            // command, args, options
            args: [ "{that}", "{arguments}.0", "{arguments}.1", "{arguments}.2" ]
        }
    }
});

/**
 * Sends a request to the service.
 *
 * @param {Component} service The gpii.serviceHandler instance.
 * @param {string} action The request action.
 * @param {object} requestData Request data.
 * @return {Promise} Promise resolving with the response.
 */
gpii.windows.service.sendRequest = function (service, action, requestData) {
    fluid.log("Service: sending ", action);

    var req = {
        action: action,
        data: requestData
    };

    return service.session.sendRequest(req);
};

/**
 * Sends the "execute" action to the service.
 *
 * @param {Component} that The gpii.windows.service.actions instance.
 * @param {string} command The command to run.
 * @param {string[]} args Arguments to pass.
 * @param {Object} options The request data.
 * @param {Object} options.options The options argument for child_process.spawn.
 * @param {boolean} options.wait True to wait for the process to terminate before resolving.
 * @param {boolean} options.capture True capture output to stdout/stderr members of the response; implies wait=true.
 * @return {Promise} Resolves when the process has started, if wait=false, or when it's terminated.
 */
gpii.windows.service.actions.execute = function (that, command, args, options) {
    var request = Object.assign({
        command: command,
        args: args
    }, options);
    return that.sendRequest("execute", request);
};

<commit_msg>GPII-2338: Checked if connected to service before making a request to it.<commit_after>/*
 * Handles requests to Windows Service.
 *
 * Copyright 2018 Raising the Floor - International
 *
 * Licensed under the New BSD license. You may not use this file except in
 * compliance with this License.
 *
 * The R&D leading to these results received funding from the
 * Department of Education - Grant H421A150005 (GPII-APCP). However,
 * these results do not necessarily represent the policy of the
 * Department of Education, and you should not assume endorsement by the
 * Federal Government.
 *
 * You may obtain a copy of the License at
 * https://github.com/GPII/universal/blob/master/LICENSE.txt
 */

"use strict";

var fluid = require("gpii-universal");

var gpii = fluid.registerNamespace("gpii");
fluid.registerNamespace("gpii.windows.service.serviceHandler");

// Handles requests to the service.
fluid.defaults("gpii.windows.service.actions", {
    gradeNames: ["fluid.component" ],
    invokers: {
        sendRequest: {
            funcName: "gpii.windows.service.sendRequest",
            // action, requestData
            args: [ "{serviceHandler}", "{arguments}.0", "{arguments}.1" ]
        },
        echo: {
            func: "{that}.sendRequest",
            args: [ "echo", "{arguments}.0" ]
        },
        execute: {
            funcName: "gpii.windows.service.actions.execute",
            // command, args, options
            args: [ "{that}", "{arguments}.0", "{arguments}.1", "{arguments}.2" ]
        }
    }
});

/**
 * Sends a request to the service.
 *
 * @param {Component} service The gpii.serviceHandler instance.
 * @param {string} action The request action.
 * @param {object} requestData Request data.
 * @return {Promise} Promise resolving with the response.
 */
gpii.windows.service.sendRequest = function (service, action, requestData) {
    if (service.session) {
        fluid.log("Service: sending ", action);

        var req = {
            action: action,
            data: requestData
        };

        return service.session.sendRequest(req);
    } else {
        return fluid.promise().reject({
            isError: true,
            message: "Not attached to the Windows service."
        });
    }
};

/**
 * Sends the "execute" action to the service.
 *
 * @param {Component} that The gpii.windows.service.actions instance.
 * @param {string} command The command to run.
 * @param {string[]} args Arguments to pass.
 * @param {Object} options The request data.
 * @param {Object} options.options The options argument for child_process.spawn.
 * @param {boolean} options.wait True to wait for the process to terminate before resolving.
 * @param {boolean} options.capture True capture output to stdout/stderr members of the response; implies wait=true.
 * @return {Promise} Resolves when the process has started, if wait=false, or when it's terminated.
 */
gpii.windows.service.actions.execute = function (that, command, args, options) {
    var request = Object.assign({
        command: command,
        args: args
    }, options);
    return that.sendRequest("execute", request);
};

<|endoftext|>


<commit_before>/*
Mag.JS Redux Remote Connect reducer v0.1
http://github.com/magnumjs/mag.js
(c) Michael Glazer
License: MIT
*/

//TODO: add more action types for hookins from user
// e.g. REMOTE_LOAD_FAILED, REMOTE_SAVE_SUCCESS, REMOTE_SAVE_PENDING ..

Redux.remoteReducer = (function(window) {

  var remoteReducer = function(options) {

    var storageType = window[options.storageType || 'sessionStorage']
    var initialState = options.cache
    var expires = options.expires || 3000;
    var SOURCE_URL = options.source

    var types1 = options.types;
    var actions1 = options.actions;

    var key = options.key;
    var reducer1 = key ? options[key] : options.reducer;

    // add to types
    var types2 = {
        SAVE_STATE: 'SAVE_STATE',
        SET_STATE_REQUEST: 'SET_STATE_REQUEST',
        SET_STATE_SUCCESS: 'SET_STATE_SUCCESS'
      }
      //add to actions
    var actions2 = {
      saveState: function(lastState) {
        return {
          type: types2.SAVE_STATE,
          promise: saveData(lastState),
          lastState : lastState
        }
      },
      loadState: function(param) {
        return {
          types: [types2.SET_STATE_REQUEST, types2.SET_STATE_SUCCESS],
          promise: getData(param),
          param
        }
      }
    }

    //Privates :
    var cache;

    function setCache(data) {
      storageType.setItem(SOURCE_URL, JSON.stringify(cache))
      return cache = data;
    }

    function getCache() {
      return cache = JSON.parse(storageType.getItem(SOURCE_URL))
    }

    var cache;

    function saveData(data) {

      //Reset caches:
      setCache(data)

      return mag.request({
        url: SOURCE_URL,
        method: 'put',
        data: JSON.stringify(data),
        headers: [{
          "Content-Type": "application/json; charset=utf-8"
        }]
      })

    }

    function getData() {
      if (getCache()) {
        var def = mag.deferred();
        def.resolve(getCache())
        return def.promise
      } else {
        return mag.request({
          url: SOURCE_URL
        })
      }
    }

    var types = mag.merge(types1, types2)
    var actions = mag.merge(actions1, actions2)
      // add to existing reducer
    function reducer2(state, action) {

      switch (action.type) {
        case types2.SAVE_STATE:
          if (action.ready) {
            return setCache(action.result);
          }
          return state;
        case types2.SET_STATE_SUCCESS:
          if (action.ready) {
            //save in cache / storage
            return setCache(action.result)
          }
          return state;
        default:
          return state || [];
      }
    }

    var check = function() {
      if (JSON.stringify(lastState) !== JSON.stringify(cache)) {
        // save to remote url
        Iapp().getProps().saveState(lastState)
      }
    }
    var timer, delay = expires;
    var lastState;

    var reducer = function(state, action) {

      // debounce changes -- check for diffs
      // then save to source url
      clearTimeout(timer);
      timer = setTimeout(function() {
        check.call();
      }, delay);

      state = reducer1(state, action)
      state = reducer2(state, action)

      return lastState = state;
    }

    var Iapp;

    function init(app) {
      Iapp = app
      app().getProps().loadState()
    }

    return {
      reducer, actions, types, init, invalidate: function() {
        setCache()
      }
    }
  }

  return remoteReducer;
})(window)
<commit_msg>Update redux-remote.js<commit_after>/*
Mag.JS Redux Remote Connect reducer v0.1
http://github.com/magnumjs/mag.js
(c) Michael Glazer
License: MIT
*/

//TODO: add more action types for hookins from user
// e.g. REMOTE_LOAD_FAILED, REMOTE_SAVE_SUCCESS, REMOTE_SAVE_PENDING ..

Redux.remoteReducer = (function(window) {

  var remoteReducer = function(options) {

    var storageType = window[options.storageType || 'sessionStorage']
    var initialState = options.cache
    var expires = options.expires || 3000;
    var SOURCE_URL = options.source

    var types1 = options.types;
    var actions1 = options.actions;

    var key = options.key;
    var reducer1 = key ? options[key] : options.reducer;

    // add to types
    var types2 = {
        SAVE_STATE: 'SAVE_STATE',
        SET_STATE_REQUEST: 'SET_STATE_REQUEST',
        SET_STATE_SUCCESS: 'SET_STATE_SUCCESS'
      }
      //add to actions
    var actions2 = {
      saveState: function(lastState) {
        return {
          type: types2.SAVE_STATE,
          promise: saveData(lastState),
          lastState : lastState
        }
      },
      loadState: function(param) {
        return {
          types: [types2.SET_STATE_REQUEST, types2.SET_STATE_SUCCESS],
          promise: getData(param),
          param : param
        }
      }
    }

    //Privates :
    var cache;

    function setCache(data) {
      storageType.setItem(SOURCE_URL, JSON.stringify(cache))
      return cache = data;
    }

    function getCache() {
      return cache = JSON.parse(storageType.getItem(SOURCE_URL))
    }

    var cache;

    function saveData(data) {

      //Reset caches:
      setCache(data)

      return mag.request({
        url: SOURCE_URL,
        method: 'put',
        data: JSON.stringify(data),
        headers: [{
          "Content-Type": "application/json; charset=utf-8"
        }]
      })

    }

    function getData() {
      if (getCache()) {
        var def = mag.deferred();
        def.resolve(getCache())
        return def.promise
      } else {
        return mag.request({
          url: SOURCE_URL
        })
      }
    }

    var types = mag.merge(types1, types2)
    var actions = mag.merge(actions1, actions2)
      // add to existing reducer
    function reducer2(state, action) {

      switch (action.type) {
        case types2.SAVE_STATE:
          if (action.ready) {
            return setCache(action.result);
          }
          return state;
        case types2.SET_STATE_SUCCESS:
          if (action.ready) {
            //save in cache / storage
            return setCache(action.result)
          }
          return state;
        default:
          return state || [];
      }
    }

    var check = function() {
      if (JSON.stringify(lastState) !== JSON.stringify(cache)) {
        // save to remote url
        Iapp().getProps().saveState(lastState)
      }
    }
    var timer, delay = expires;
    var lastState;

    var reducer = function(state, action) {

      // debounce changes -- check for diffs
      // then save to source url
      clearTimeout(timer);
      timer = setTimeout(function() {
        check.call();
      }, delay);

      state = reducer1(state, action)
      state = reducer2(state, action)

      return lastState = state;
    }

    var Iapp;

    function init(app) {
      Iapp = app
      app().getProps().loadState()
    }

    return {
      reducer, actions, types, init, invalidate: function() {
        setCache()
      }
    }
  }

  return remoteReducer;
})(window)
<|endoftext|>


<commit_before>import {Pos, spanStylesAt, sliceBefore, sliceAfter, sliceBetween, childrenBetween} from "../model"

import {TransformResult, Transform} from "./transform"
import {defineStep, Step} from "./step"
import {PosMap, MovedRange, ReplacedRange} from "./map"
import {replaceHasEffect, samePathDepth} from "./tree"

function findMovedChunks(oldNode, oldPath, newNode, startDepth) {
  let moved = []
  let newPath = oldPath.path.slice(0, startDepth)

  for (let depth = startDepth;; depth++) {
    let joined = depth == oldPath.depth ? 0 : 1
    let cut = depth == oldPath.depth ? oldPath.offset : oldPath.path[depth]
    let afterCut = oldNode.maxOffset - cut
    let newOffset = newNode.maxOffset - afterCut

    let from = oldPath.shorten(depth, joined)
    let to = new Pos(newPath, newOffset + joined)
    if (from.cmp(to)) moved.push(new MovedRange(from, afterCut - joined, to))

    if (!joined) return moved

    oldNode = oldNode.child(cut)
    newNode = newNode.child(newOffset)
    newPath = newPath.concat(newOffset)
  }
}

export function replace(node, from, to, root, repl, depth = 0) {
  if (depth == root.length) {
    let before = sliceBefore(node, from, depth)
    let after = sliceAfter(node, to, depth), result
    if (!repl.nodes.every(n => before.type.canContain(n))) return null
    if (repl.nodes.length)
      result = before.append(repl.nodes, Math.min(repl.openLeft, from.depth - depth))
                     .append(after.children, Math.min(repl.openRight, to.depth - depth))
    else
      result = before.append(after.children, Math.min(to.depth, from.depth) - depth)
    return {doc: result, moved: findMovedChunks(node, to, result, depth)}
  } else {
    let pos = root[depth]
    let result = replace(node.child(pos), from, to, root, repl, depth + 1)
    if (!result) return null
    return {doc: node.replace(pos, result.doc), moved: result.moved}
  }
}

const nullRepl = {nodes: [], openLeft: 0, openRight: 0}

defineStep("replace", {
  apply(doc, step) {
    let rootPos = step.pos, root = rootPos.path
    if (step.from.depth < root.length || step.to.depth < root.length)
      return null
    for (let i = 0; i < root.length; i++)
      if (step.from.path[i] != root[i] || step.to.path[i] != root[i])
        return null

    let result = replace(doc, step.from, step.to, rootPos.path, step.param || nullRepl)
    if (!result) return null
    let {doc: out, moved} = result
    let end = moved.length ? moved[moved.length - 1].dest : step.to
    let replaced = new ReplacedRange(step.from, step.to, step.from, end, rootPos, rootPos)
    return new TransformResult(out, new PosMap(moved, [replaced]))
  },
  invert(step, oldDoc, map) {
    let depth = step.pos.depth
    return new Step("replace", step.from, map.map(step.to).pos, step.from.shorten(depth), {
      nodes: childrenBetween(oldDoc.path(step.pos.path), step.from, step.to, depth),
      openLeft: step.from.depth - depth,
      openRight: step.to.depth - depth
    })
  },
  paramToJSON(param) {
    return param && {nodes: param.nodes && param.nodes.map(n => n.toJSON()),
                     openLeft: param.openLeft, openRight: param.openRight}
  },
  paramFromJSON(schema, json) {
    return json && {nodes: json.nodes && json.nodes.map(schema.nodeFromJSON),
                    openLeft: json.openLeft, openRight: json.openRight}
  }
})

function shiftFromStack(stack, depth) {
  let shifted = stack[depth] = stack[depth].splice(0, 1, [])
  for (let i = depth - 1; i >= 0; i--)
    shifted = stack[i] = stack[i].replace(0, shifted)
}

// FIXME find a not so horribly confusing way to express this
function buildInserted(nodesLeft, source, start, end) {
  let sliced = sliceBetween(source, start, end, false)
  let nodesRight = []
  for (let node = sliced, i = 0; i <= start.path.length; i++, node = node.firstChild)
    nodesRight.push(node)
  let same = samePathDepth(start, end)
  let searchLeft = nodesLeft.length - 1, searchRight = nodesRight.length - 1
  let result = null

  let inner = nodesRight[searchRight]
  if (inner.isTextblock && inner.length && nodesLeft[searchLeft].isTextblock) {
    result = nodesLeft[searchLeft--].copy(inner.children)
    --searchRight
    shiftFromStack(nodesRight, searchRight)
  }

  for (;;) {
    let node = nodesRight[searchRight], type = node.type, matched = null
    let outside = searchRight <= same
    for (let i = searchLeft; i >= 0; i--) {
      let left = nodesLeft[i]
      if (outside ? left.type.canContainChildren(node) : left.type == type) {
        matched = i
        break
      }
    }
    if (matched != null) {
      if (!result) {
        result = nodesLeft[matched].copy(node.children)
        searchLeft = matched - 1
      } else {
        while (searchLeft >= matched) {
          result = nodesLeft[searchLeft].copy(searchLeft == matched ? [result].concat(node.children) : [result])
          searchLeft--
        }
      }
    }
    if (matched != null || node.length == 0) {
      if (outside) break
      if (searchRight) shiftFromStack(nodesRight, searchRight - 1)
    }
    searchRight--
  }

  let repl = {nodes: result ? result.children : [],
              openLeft: start.depth - searchRight,
              openRight: end.depth - searchRight}
  return {repl, depth: searchLeft + 1}
}

function moveText(tr, doc, before, after) {
  let root = samePathDepth(before, after)
  let cutAt = after.shorten(null, 1)
  while (cutAt.path.length > root && doc.path(cutAt.path).length == 1)
    cutAt = cutAt.shorten(null, 1)
  tr.split(cutAt, cutAt.path.length - root)
  let start = after, end = new Pos(start.path, doc.path(start.path).maxOffset)
  let parent = doc.path(start.path.slice(0, root))
  let wanted = parent.pathNodes(before.path.slice(root))
  let existing = parent.pathNodes(start.path.slice(root))
  while (wanted.length && existing.length && wanted[0].sameMarkup(existing[0])) {
    wanted.shift()
    existing.shift()
  }
  if (existing.length || wanted.length)
    tr.step("ancestor", start, end, null, {
      depth: existing.length,
      wrappers: wanted.map(n => n.copy())
    })
  for (let i = root; i < before.path.length; i++)
    tr.join(before.shorten(i, 1))
}

/**
 * Delete content between two positions.
 *
 * @param  {Pos} from
 * @param  {Pos} to
 * @return this
 */
Transform.prototype.delete = function(from, to) {
  if (from.cmp(to)) this.replace(from, to)
  return this
}

/**
 * Replace the content between two positions.
 */
Transform.prototype.replace = function(from, to, source, start, end) {
  let repl, depth, doc = this.doc, maxDepth = samePathDepth(from, to)
  if (source) {
    ;({repl, depth} = buildInserted(doc.pathNodes(from.path), source, start, end))
    while (depth > maxDepth) {
      if (repl.nodes.length)
        repl = {nodes: [doc.path(from.path.slice(0, depth)).copy(repl.nodes)],
                openLeft: repl.openLeft + 1, openRight: repl.openRight + 1}
      depth--
    }
  } else {
    repl = nullRepl
    depth = maxDepth
  }
  let root = from.shorten(depth), docAfter = doc, after = to
  if (repl.nodes.length || replaceHasEffect(doc, from, to)) {
    let result = this.step("replace", from, to, root, repl)
    docAfter = result.doc
    after = result.map.map(to).pos
  }

  // If no text nodes before or after end of replacement, don't glue text
  if (!doc.path(to.path).isTextblock) return this
  if (!(repl.nodes.length ? source.path(end.path).isTextblock : doc.path(from.path).isTextblock)) return this

  let nodesAfter = doc.path(root.path).pathNodes(to.path.slice(depth)).slice(1)
  let nodesBefore
  if (repl.nodes.length) {
    let inserted = repl.nodes
    nodesBefore = []
    for (let i = 0; i < repl.openRight; i++) {
      let last = inserted[inserted.length - 1]
      nodesBefore.push(last)
      inserted = last.children
    }
  } else {
    nodesBefore = doc.path(root.path).pathNodes(from.path.slice(depth)).slice(1)
  }

  if (nodesBefore.length &&
      (nodesAfter.length != nodesBefore.length ||
       !nodesAfter.every((n, i) => n.sameMarkup(nodesBefore[i])))) {
    let {path, offset} = after.shorten(root.depth), before
    for (let node = docAfter.path(path), i = 0;; i++) {
      if (i == nodesBefore.length) {
        before = new Pos(path, offset)
        break
      }
      path.push(offset - 1)
      node = node.child(offset - 1)
      offset = node.maxOffset
    }
    moveText(this, docAfter, before, after)
  }
  return this
}

Transform.prototype.replaceWith = function(from, to, nodes) {
  if (!Array.isArray(nodes)) nodes = [nodes]
  if (!Pos.samePath(from.path, to.path)) return this
  this.step("replace", from, to, from, {nodes: nodes, openLeft: 0, openRight: 0})
  return this
}

/**
 * Insert a node at a given position.
 *
 * @param  {Pos}   pos
 * @param  {mixed} nodes
 * @return {this}
 */
Transform.prototype.insert = function(pos, nodes) {
  return this.replaceWith(pos, pos, nodes)
}

Transform.prototype.insertInline = function(pos, nodes) {
  if (!Array.isArray(nodes)) nodes = [nodes]
  let styles = spanStylesAt(this.doc, pos)
  nodes = nodes.map(n => n.styled(styles))
  return this.insert(pos, nodes)
}

Transform.prototype.insertText = function(pos, text) {
  return this.insertInline(pos, this.doc.type.schema.text(text))
}
<commit_msg>Prevent replace from creating invalid empty nodes<commit_after>import {Pos, spanStylesAt, sliceBefore, sliceAfter, sliceBetween, childrenBetween} from "../model"

import {TransformResult, Transform} from "./transform"
import {defineStep, Step} from "./step"
import {PosMap, MovedRange, ReplacedRange} from "./map"
import {replaceHasEffect, samePathDepth} from "./tree"

function findMovedChunks(oldNode, oldPath, newNode, startDepth) {
  let moved = []
  let newPath = oldPath.path.slice(0, startDepth)

  for (let depth = startDepth;; depth++) {
    let joined = depth == oldPath.depth ? 0 : 1
    let cut = depth == oldPath.depth ? oldPath.offset : oldPath.path[depth]
    let afterCut = oldNode.maxOffset - cut
    let newOffset = newNode.maxOffset - afterCut

    let from = oldPath.shorten(depth, joined)
    let to = new Pos(newPath, newOffset + joined)
    if (from.cmp(to)) moved.push(new MovedRange(from, afterCut - joined, to))

    if (!joined) return moved

    oldNode = oldNode.child(cut)
    newNode = newNode.child(newOffset)
    newPath = newPath.concat(newOffset)
  }
}

export function replace(node, from, to, root, repl, depth = 0) {
  if (depth == root.length) {
    let before = sliceBefore(node, from, depth)
    let after = sliceAfter(node, to, depth), result
    if (!repl.nodes.every(n => before.type.canContain(n))) return null
    if (repl.nodes.length)
      result = before.append(repl.nodes, from.depth - depth, repl.openLeft)
                     .append(after.children, repl.openRight, to.depth - depth)
    else
      result = before.append(after.children, from.depth - depth, to.depth - depth)
    if (!result.length && !result.type.canBeEmpty)
      result = result.copy(result.type.defaultContent())
    return {doc: result, moved: findMovedChunks(node, to, result, depth)}
  } else {
    let pos = root[depth]
    let result = replace(node.child(pos), from, to, root, repl, depth + 1)
    if (!result) return null
    return {doc: node.replace(pos, result.doc), moved: result.moved}
  }
}

const nullRepl = {nodes: [], openLeft: 0, openRight: 0}

defineStep("replace", {
  apply(doc, step) {
    let rootPos = step.pos, root = rootPos.path
    if (step.from.depth < root.length || step.to.depth < root.length)
      return null
    for (let i = 0; i < root.length; i++)
      if (step.from.path[i] != root[i] || step.to.path[i] != root[i])
        return null

    let result = replace(doc, step.from, step.to, rootPos.path, step.param || nullRepl)
    if (!result) return null
    let {doc: out, moved} = result
    let end = moved.length ? moved[moved.length - 1].dest : step.to
    let replaced = new ReplacedRange(step.from, step.to, step.from, end, rootPos, rootPos)
    return new TransformResult(out, new PosMap(moved, [replaced]))
  },
  invert(step, oldDoc, map) {
    let depth = step.pos.depth
    return new Step("replace", step.from, map.map(step.to).pos, step.from.shorten(depth), {
      nodes: childrenBetween(oldDoc.path(step.pos.path), step.from, step.to, depth),
      openLeft: step.from.depth - depth,
      openRight: step.to.depth - depth
    })
  },
  paramToJSON(param) {
    return param && {nodes: param.nodes && param.nodes.map(n => n.toJSON()),
                     openLeft: param.openLeft, openRight: param.openRight}
  },
  paramFromJSON(schema, json) {
    return json && {nodes: json.nodes && json.nodes.map(schema.nodeFromJSON),
                    openLeft: json.openLeft, openRight: json.openRight}
  }
})

function shiftFromStack(stack, depth) {
  let shifted = stack[depth] = stack[depth].splice(0, 1, [])
  for (let i = depth - 1; i >= 0; i--)
    shifted = stack[i] = stack[i].replace(0, shifted)
}

// FIXME find a not so horribly confusing way to express this
function buildInserted(nodesLeft, source, start, end) {
  let sliced = sliceBetween(source, start, end, false)
  let nodesRight = []
  for (let node = sliced, i = 0; i <= start.path.length; i++, node = node.firstChild)
    nodesRight.push(node)
  let same = samePathDepth(start, end)
  let searchLeft = nodesLeft.length - 1, searchRight = nodesRight.length - 1
  let result = null

  let inner = nodesRight[searchRight]
  if (inner.isTextblock && inner.length && nodesLeft[searchLeft].isTextblock) {
    result = nodesLeft[searchLeft--].copy(inner.children)
    --searchRight
    shiftFromStack(nodesRight, searchRight)
  }

  for (;;) {
    let node = nodesRight[searchRight], type = node.type, matched = null
    let outside = searchRight <= same
    for (let i = searchLeft; i >= 0; i--) {
      let left = nodesLeft[i]
      if (outside ? left.type.canContainChildren(node) : left.type == type) {
        matched = i
        break
      }
    }
    if (matched != null) {
      if (!result) {
        result = nodesLeft[matched].copy(node.children)
        searchLeft = matched - 1
      } else {
        while (searchLeft >= matched) {
          result = nodesLeft[searchLeft].copy(searchLeft == matched ? [result].concat(node.children) : [result])
          searchLeft--
        }
      }
    }
    if (matched != null || node.length == 0) {
      if (outside) break
      if (searchRight) shiftFromStack(nodesRight, searchRight - 1)
    }
    searchRight--
  }

  let repl = {nodes: result ? result.children : [],
              openLeft: start.depth - searchRight,
              openRight: end.depth - searchRight}
  return {repl, depth: searchLeft + 1}
}

function moveText(tr, doc, before, after) {
  let root = samePathDepth(before, after)
  let cutAt = after.shorten(null, 1)
  while (cutAt.path.length > root && doc.path(cutAt.path).length == 1)
    cutAt = cutAt.shorten(null, 1)
  tr.split(cutAt, cutAt.path.length - root)
  let start = after, end = new Pos(start.path, doc.path(start.path).maxOffset)
  let parent = doc.path(start.path.slice(0, root))
  let wanted = parent.pathNodes(before.path.slice(root))
  let existing = parent.pathNodes(start.path.slice(root))
  while (wanted.length && existing.length && wanted[0].sameMarkup(existing[0])) {
    wanted.shift()
    existing.shift()
  }
  if (existing.length || wanted.length)
    tr.step("ancestor", start, end, null, {
      depth: existing.length,
      wrappers: wanted.map(n => n.copy())
    })
  for (let i = root; i < before.path.length; i++)
    tr.join(before.shorten(i, 1))
}

/**
 * Delete content between two positions.
 *
 * @param  {Pos} from
 * @param  {Pos} to
 * @return this
 */
Transform.prototype.delete = function(from, to) {
  if (from.cmp(to)) this.replace(from, to)
  return this
}

/**
 * Replace the content between two positions.
 */
Transform.prototype.replace = function(from, to, source, start, end) {
  let repl, depth, doc = this.doc, maxDepth = samePathDepth(from, to)
  if (source) {
    ;({repl, depth} = buildInserted(doc.pathNodes(from.path), source, start, end))
    while (depth > maxDepth) {
      if (repl.nodes.length)
        repl = {nodes: [doc.path(from.path.slice(0, depth)).copy(repl.nodes)],
                openLeft: repl.openLeft + 1, openRight: repl.openRight + 1}
      depth--
    }
  } else {
    repl = nullRepl
    depth = maxDepth
  }
  let root = from.shorten(depth), docAfter = doc, after = to
  if (repl.nodes.length || replaceHasEffect(doc, from, to)) {
    let result = this.step("replace", from, to, root, repl)
    docAfter = result.doc
    after = result.map.map(to).pos
  }

  // If no text nodes before or after end of replacement, don't glue text
  if (!doc.path(to.path).isTextblock) return this
  if (!(repl.nodes.length ? source.path(end.path).isTextblock : doc.path(from.path).isTextblock)) return this

  let nodesAfter = doc.path(root.path).pathNodes(to.path.slice(depth)).slice(1)
  let nodesBefore
  if (repl.nodes.length) {
    let inserted = repl.nodes
    nodesBefore = []
    for (let i = 0; i < repl.openRight; i++) {
      let last = inserted[inserted.length - 1]
      nodesBefore.push(last)
      inserted = last.children
    }
  } else {
    nodesBefore = doc.path(root.path).pathNodes(from.path.slice(depth)).slice(1)
  }

  if (nodesBefore.length &&
      (nodesAfter.length != nodesBefore.length ||
       !nodesAfter.every((n, i) => n.sameMarkup(nodesBefore[i])))) {
    let {path, offset} = after.shorten(root.depth), before
    for (let node = docAfter.path(path), i = 0;; i++) {
      if (i == nodesBefore.length) {
        before = new Pos(path, offset)
        break
      }
      path.push(offset - 1)
      node = node.child(offset - 1)
      offset = node.maxOffset
    }
    moveText(this, docAfter, before, after)
  }
  return this
}

Transform.prototype.replaceWith = function(from, to, nodes) {
  if (!Array.isArray(nodes)) nodes = [nodes]
  if (!Pos.samePath(from.path, to.path)) return this
  this.step("replace", from, to, from, {nodes: nodes, openLeft: 0, openRight: 0})
  return this
}

/**
 * Insert a node at a given position.
 *
 * @param  {Pos}   pos
 * @param  {mixed} nodes
 * @return {this}
 */
Transform.prototype.insert = function(pos, nodes) {
  return this.replaceWith(pos, pos, nodes)
}

Transform.prototype.insertInline = function(pos, nodes) {
  if (!Array.isArray(nodes)) nodes = [nodes]
  let styles = spanStylesAt(this.doc, pos)
  nodes = nodes.map(n => n.styled(styles))
  return this.insert(pos, nodes)
}

Transform.prototype.insertText = function(pos, text) {
  return this.insertInline(pos, this.doc.type.schema.text(text))
}
<|endoftext|>