Exemplo n.º 1
0
let TranslationTable = Model({
  init : function(sourceLocale, targetLocale, translations, locales) {
    this.sourceLocale = sourceLocale;
    this.targetLocale = targetLocale;
    this.translations = translations;
    this.locales = locales;

    this.editableManager = new EditableManager();
    this.commandHead = {};
    this.boundKeyHandler = this.keyHandler.bind(this);
  },

  /**
   * Render the table and wire all its events
   *
   * @return {Object}
   */
  render : function() {
    this.el || (this.el = require('Templates/TranslationTable')({
      locales : this.locales,
      sourceLocale : this.sourceLocale,
      targetLocale : this.targetLocale,
      translations : this.translations,
      factory : {
        editable : function(el, translation) {
          if (!translation.value) {
            let source = this.translations[translation.identifier][this.sourceLocale];

            // try to exchange the source to have well defined package key
            // and file name
            for (let i = 0; !source; i++) {
              let identifier = Object.getOwnPropertyNames(this.translations)[i];
              source = this.translations[identifier][this.targetLocale]
            }

            translation.packageKey = source.packageKey;
            translation.localeCode = this.targetLocale;
            translation.sourceName = source.sourceName;
            translation.value = '';
          }

          let editable = new Editable(el, translation, this.editableManager);
          editable.on('change', this.addCommand.bind(this));
          this.editableManager.add(editable);
        }.bind(this),

        localeSelector : function(select) {
          select.addEventListener('change', function() {
            this.trigger('locale:change', select.value);
          }.bind(this));
        }.bind(this)
      }
    }));

    this.el.getElementsByTagName('select')[0].value = this.targetLocale;

    document.addEventListener('keyup', this.boundKeyHandler);
    this.editableManager.attach();
    return this;
  },

  /**
   * Unwire all the tables events
   *
   * @return {Object}
   */
  detach : function() {
    document.removeEventListener('keyup', this.boundKeyHandler);
    this.editableManager.detach();
  },

  /**
   * Keyboard shortcut bindings
   *
   * @param {Object} e The event
   * @return {Object}
   */
  keyHandler : function(e) {
    if (e.ctrlKey) {
      let keyCode = e.wich || e.keyCode || e.charCode;

      switch(keyCode) {
        case 90: this.undo(); break;
        case 89: this.redo(); break;
      }
    }

    return this;
  },

  /**
   * Add a delta command for this table
   *
   * @param {Object} delta The delta command
   * @return {Object}
   */
  addCommand : function(delta) {
    this.commandHead.next = delta;
    delta.previous = this.commandHead;
    this.commandHead = delta;

    return this;
  },

  /**
   * Undo the last delta command
   *
   * @return {Object}
   */
  undo : function() {
    let command = this.commandHead && this.commandHead.previous;
    this.commandHead && this.commandHead.editable.setValue(this.commandHead.value.from);
    this.commandHead = command;

    return this;
  },

  /**
   * Redo the latest undone delta command
   *
   * @return {Object}
   */
  redo : function() {
    let command = this.commandHead && this.commandHead.next;
    command && command.editable.setValue(command.value.to);
    command && (this.commandHead = command);

    return this;
  },

  /**
   * Serialize all commands into a minimal set
   *
   * @return {Object}
   */
  save : function() {
    // normalize deltas for transport to server
    let command = this.commandHead;
    let data = {};
    while(command && command.identifier) {
      if (!data[command.identifier]) {
        data[command.identifier] = {
          packageKey : command.packageKey,
          locale : command.locale,
          sourceName : command.sourceName,
          identifier : command.identifier,
          value : command.value.to
        };
      }

      command = command.previous;
    }

    return data;
  }
});
Exemplo n.º 2
0
},{"../Editable/Editable":1,"../EditableManager/EditableManager":3,"Templates/TranslationTable":9,"fishbone":40}],5:[function(require,module,exports){
'use strict';

var Model = require('fishbone');
var TranslationTable = require('./TranslationTable/TranslationTable.js');

/**
 * Translations
 *
 * An application that allows for multi-language and multi-locale
 * translation. It is designed to run in a TYPO3 Neos backend Environment.
 */
var Translations = Model({

  /**
   * Constructor
   */
  init: function init(el) {
    this.el = el;
    this.tableViews = {};
    this.form = this.el.getElementsByTagName('form')[0];

    var localeNode = this.el.querySelector('[data-json="locales"]');
    this.locales = JSON.parse(localeNode.textContent || localeNode.innerText);

    var translationNode = this.el.querySelector('[data-json="translations"]');
    this.translations = JSON.parse(translationNode.textContent || translationNode.innerText);

    this.defaultTarget = this.el.dataset.locale || this.locales[1];
    this.render(this.locales[0], this.defaultTarget);
  },

  /**
   * Render the app
   *
   * @return {Object}
   */
  render: function render(sourceLocale, targetLocale) {
    this.tableViews[sourceLocale] || (this.tableViews[sourceLocale] = {});
    if (!this.tableViews[sourceLocale][targetLocale]) {
      this.tableViews[sourceLocale][targetLocale] = new TranslationTable(sourceLocale, targetLocale, this.translations, this.locales);

      this.tableViews[sourceLocale][targetLocale].on('locale:change', this.changeLocale.bind(this));
    }

    var enhance = this.el.querySelector('[data-enhance]') || this.view;
    var enhanceParent = enhance.parentNode;
    this.view = require('Templates/MainApplication')({
      table: this.tableViews[sourceLocale][targetLocale].render().el,
      factory: {
        button: (function (button) {
          button.addEventListener('click', this.save.bind(this));
        }).bind(this)
      }
    });

    this.currentTable = this.tableViews[sourceLocale][targetLocale];

    enhanceParent.replaceChild(this.view, enhance);
    return this;
  },

  /**
   * Switch to a different locale
   *
   * @return {Object}
   */
  changeLocale: function changeLocale(locale) {
    this.currentTable.detach();
    this.render(this.locales[0], locale);

    return this;
  },

  /**
   * Save all changes made to the translations
   *
   * @return {Object}
   */
  save: function save() {
    var data = {};
    for (var i = 0, sourceLocale = undefined; sourceLocale = Object.getOwnPropertyNames(this.tableViews)[i]; i++) {
      var tableViews = this.tableViews[sourceLocale];
      for (var j = 0, targetLocale = undefined; targetLocale = Object.getOwnPropertyNames(tableViews)[j]; j++) {
        var tableView = tableViews[targetLocale];
        data[targetLocale] || (data[targetLocale] = tableView.save());
      }
    }

    this.form.appendChild(require('../Templates/IntermediateForm')(data));
    this.form.submit();
  }
});

module.exports = Translations;

},{"../Templates/IntermediateForm":7,"./TranslationTable/TranslationTable.js":4,"Templates/MainApplication":8,"fishbone":40}],6:[function(require,module,exports){
Exemplo n.º 3
0
},{"domjs/lib/html5":13}],3:[function(require,module,exports){
'use strict';

var Model = require('fishbone');

/**
 * EditableManager
 *
 * Manage Editable views
 */
var EditableManager = Model({

  /**
   * Constructor
   */
  init: function init() {
    this.editables = {
      ordered: [],
      byIdentifier: {},
      currentlyFocused: null
    };

    this.boundHandleDocumentClick = this.handleDocumentClick.bind(this);
    this.boundHandleKeyUp = this.handleKeyUp.bind(this);
  },

  /**
   * Wire all events for this object
   *
   * @return {Object}
   */
  attach: function attach() {
    document.addEventListener('click', this.boundHandleDocumentClick);
    document.addEventListener('keyup', this.boundHandleKeyUp);

    return this;
  },

  /**
   * Unwire all events for this object
   *
   * @return {Object}
   */
  detach: function detach() {
    document.removeEventListener('click', this.boundHandleDocumentClick);
    document.removeEventListener('keyup', this.boundHandleKeyUp);

    return this;
  },

  /**
   * Handle the user clicking anywhere
   *
   * @param {Object} e The event
   * @return {Object}
   */
  handleDocumentClick: function handleDocumentClick(e) {
    if ('input' !== e.target.tagName.toLowerCase()) {
      this.freezeCurrent();
    }

    return this;
  },

  /**
   * Keyboard shortcut bindings
   *
   * @param {Object} e The event
   * @return {Object}
   */
  handleKeyUp: function handleKeyUp(e) {
    var keyCode = e.wich || e.keyCode || e.charCode;

    switch (keyCode) {
      case 9:
      case 13:
        this.freezeCurrent();
        this.editables.currentlyFocused.next && this.editables.currentlyFocused.next.requestThaw();
        break;

      case 27:
        this.resetCurrent();break;
    }

    return this;
  },

  /**
   * Add a new editable
   *
   * @param {Object} ediable The editable
   * @return {Object}
   */
  add: function add(editable) {
    this.editables.ordered.push(editable);
    this.editables.ordered[this.editables.ordered.length - 2] && (this.editables.ordered[this.editables.ordered.length - 2].next = editable) && (editable.previous = this.editables.ordered[this.editables.ordered.length - 2]);

    this.editables.byIdentifier[editable.translation.identifier] = editable;
  },

  /**
   * Reset currently focused editable to its original state
   *
   * @return {Object}
   */
  resetCurrent: function resetCurrent() {
    this.editables.currentlyFocused && this.editables.currentlyFocused.reset();
    return this;
  },

  /**
   * Leave edit mode for currently active editable and keep its
   * edited state.
   *
   * @return {Object}
   */
  freezeCurrent: function freezeCurrent() {
    this.editables.currentlyFocused && this.editables.currentlyFocused.freeze();
    return this;
  },

  /**
   * Switch currently focused editable
   *
   * @param {Object} editable The editable
   * @return {Object}
   */
  requestFocus: function requestFocus(editable) {
    if (!this.editables.currentlyFocused) {
      this.editables.currentlyFocused = editable;

      editable.focus();

      return this;
    }

    if (editable !== this.editables.currentlyFocused) {
      this.editables.currentlyFocused.blur();
      this.editables.currentlyFocused = editable;
      editable.focus();

      return this;
    }

    return this;
  },

  /**
   * Enter edit mode for a given editable
   *
   * @param {Object} editable The editable
   * @return {Object}
   */
  requestThaw: function requestThaw(editable) {
    if (editable !== this.editables.currentlyFocused) {
      this.requestFocus(editable);
    }

    editable.thaw();
    return this;
  }
});

module.exports = EditableManager;

},{"fishbone":40}],4:[function(require,module,exports){
Exemplo n.º 4
0
},{"fishbone":40}],4:[function(require,module,exports){
'use strict';

var Model = require('fishbone');

var Editable = require('../Editable/Editable');
var EditableManager = require('../EditableManager/EditableManager');

/**
 * TranslationTable
 *
 * A view displaying a source and a target locale side by side as
 * table columns. The column displaying the target locale consists
 * of editable cells.
 */
var TranslationTable = Model({
  init: function init(sourceLocale, targetLocale, translations, locales) {
    this.sourceLocale = sourceLocale;
    this.targetLocale = targetLocale;
    this.translations = translations;
    this.locales = locales;

    this.editableManager = new EditableManager();
    this.commandHead = {};
    this.boundKeyHandler = this.keyHandler.bind(this);
  },

  /**
   * Render the table and wire all its events
   *
   * @return {Object}
   */
  render: function render() {
    this.el || (this.el = require('Templates/TranslationTable')({
      locales: this.locales,
      sourceLocale: this.sourceLocale,
      targetLocale: this.targetLocale,
      translations: this.translations,
      factory: {
        editable: (function (el, translation) {
          if (!translation.value) {
            var source = this.translations[translation.identifier][this.sourceLocale];

            // try to exchange the source to have well defined package key
            // and file name
            for (var i = 0; !source; i++) {
              var identifier = Object.getOwnPropertyNames(this.translations)[i];
              source = this.translations[identifier][this.targetLocale];
            }

            translation.packageKey = source.packageKey;
            translation.localeCode = this.targetLocale;
            translation.sourceName = source.sourceName;
            translation.value = '';
          }

          var editable = new Editable(el, translation, this.editableManager);
          editable.on('change', this.addCommand.bind(this));
          this.editableManager.add(editable);
        }).bind(this),

        localeSelector: (function (select) {
          select.addEventListener('change', (function () {
            this.trigger('locale:change', select.value);
          }).bind(this));
        }).bind(this)
      }
    }));

    this.el.getElementsByTagName('select')[0].value = this.targetLocale;

    document.addEventListener('keyup', this.boundKeyHandler);
    this.editableManager.attach();
    return this;
  },

  /**
   * Unwire all the tables events
   *
   * @return {Object}
   */
  detach: function detach() {
    document.removeEventListener('keyup', this.boundKeyHandler);
    this.editableManager.detach();
  },

  /**
   * Keyboard shortcut bindings
   *
   * @param {Object} e The event
   * @return {Object}
   */
  keyHandler: function keyHandler(e) {
    if (e.ctrlKey) {
      var keyCode = e.wich || e.keyCode || e.charCode;

      switch (keyCode) {
        case 90:
          this.undo();break;
        case 89:
          this.redo();break;
      }
    }

    return this;
  },

  /**
   * Add a delta command for this table
   *
   * @param {Object} delta The delta command
   * @return {Object}
   */
  addCommand: function addCommand(delta) {
    this.commandHead.next = delta;
    delta.previous = this.commandHead;
    this.commandHead = delta;

    return this;
  },

  /**
   * Undo the last delta command
   *
   * @return {Object}
   */
  undo: function undo() {
    var command = this.commandHead && this.commandHead.previous;
    this.commandHead && this.commandHead.editable.setValue(this.commandHead.value.from);
    this.commandHead = command;

    return this;
  },

  /**
   * Redo the latest undone delta command
   *
   * @return {Object}
   */
  redo: function redo() {
    var command = this.commandHead && this.commandHead.next;
    command && command.editable.setValue(command.value.to);
    command && (this.commandHead = command);

    return this;
  },

  /**
   * Serialize all commands into a minimal set
   *
   * @return {Object}
   */
  save: function save() {
    // normalize deltas for transport to server
    var command = this.commandHead;
    var data = {};
    while (command && command.identifier) {
      if (!data[command.identifier]) {
        data[command.identifier] = {
          packageKey: command.packageKey,
          locale: command.locale,
          sourceName: command.sourceName,
          identifier: command.identifier,
          value: command.value.to
        };
      }

      command = command.previous;
    }

    return data;
  }
});

module.exports = TranslationTable;

},{"../Editable/Editable":1,"../EditableManager/EditableManager":3,"Templates/TranslationTable":9,"fishbone":40}],5:[function(require,module,exports){
Exemplo n.º 5
0
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';

var Model = require('fishbone');

/**
 * Editable
 *
 * A View that handles the editing capabilities of editable cells
 * in a translation table
 */
var Editable = Model({

  /**
   * Constructor
   */
  init: function init(el, translation, manager) {
    this.el = el;
    this.parent = this.el.parentNode;
    this.statefulEl = this.el;
    this.manager = manager;

    this.isEditState = false;
    this.isDirty = false;
    this.translation = translation;
    this.translation.origin = translation.value;

    this.el.addEventListener('click', this.requestThaw.bind(this));
  },

  /**
   * Ask editable manager to activate edit mode
   *
   * @return {Object}
   */
  requestThaw: function requestThaw(e) {
    e && e.stopPropagation();
    this.manager.requestThaw(this);
    return this;
  },

  /**
   * Ask edit manager to gain focus
   *
   * @return {Object}
   */
  requestFocus: function requestFocus(e) {
    e && e.stopPropagation();
    this.manager.requestFocus(this);

    return this;
  },

  /**
   * Set the current value
   *
   * @return {Object}
   */
  setValue: function setValue(value) {
    this.isDirty = this.translation.origin !== value;
    this.translation.value = value;
    this.reset();

    return this;
  },

  /**
   * Gain focus
   *
   * @return {Object}
   */
  focus: function focus() {
    return this;
  },

  /**
   * Lose focus
   *
   * @return {Object}
   */
  blur: function blur() {
    this.freeze();
    return this;
  },

  /**
   * Enter edit mode
   *
   * @return {Object}
   */
  thaw: function thaw() {
    if (!this.isEditState) {
      var input = require('./Templates/EditState.js')(this.translation).firstChild;
      this.parent.replaceChild(input, this.el);
      this.statefulEl = input;

      this.statefulEl.focus();
      this.statefulEl.select();
      this.parent.classList.add('is-editing');
      this.isEditState = true;
    }

    return this;
  },

  /**
   * Leave edit mode
   *
   * @return {Object}
   */
  freeze: function freeze() {
    if (this.statefulEl !== this.el) {
      var value = {
        from: this.translation.value,
        to: this.statefulEl.value
      };

      this.setValue(value.to);

      if (this.isDirty) {
        this.trigger('change', {
          editable: this,
          packageKey: this.translation.packageKey,
          locale: this.translation.localeCode,
          sourceName: this.translation.sourceName,
          identifier: this.translation.identifier,
          value: value
        });
      }
    }

    return this;
  },

  /**
   * Reset original state
   *
   * @return {Object}
   */
  reset: function reset() {
    this.parent.replaceChild(this.el, this.statefulEl);
    this.statefulEl = this.el;
    this.el.innerHTML = this.translation.value;
    this.isEditState = false;

    this.parent.classList.remove('is-editing');
    this.parent.classList[this.isDirty ? 'add' : 'remove']('editor-dirty');

    return this;
  },

  /**
   * Destroy the object
   * 
   * @return {Object}
   */
  destroy: function destroy() {
    return this;
  }
});

module.exports = Editable;

},{"./Templates/EditState.js":2,"fishbone":40}],2:[function(require,module,exports){