Skip to content

Commit

Permalink
Merge pull request #70 from wikimedia/localstorage-form-vals-T213045
Browse files Browse the repository at this point in the history
Add localStorage memory to translation form fields
  • Loading branch information
mooeypoo authored Mar 19, 2019
2 parents 7ca14b3 + b8ab010 commit 30f4b55
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 62 deletions.
118 changes: 117 additions & 1 deletion assets/js/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,87 @@ global.OO = require( '../../node_modules/oojs/dist/oojs.js' );
*/
App.Model = function appModel( translations ) {
OO.EventEmitter.call( this );
this.translations = translations;
this.localStorageKey = 'svgtranslate';
this.translations = translations || {};
this.sourceLang = null;
this.targetLang = null;
this.langs = this.getLangs( this.translations );
};

OO.mixinClass( App.Model, OO.EventEmitter );

/**
* Get all available languages (note that translations may not be complete for all).
*
* @param {Object} translations
* @return {Array}
*/
App.Model.prototype.getLangs = function ( translations ) {
var t, l, langs, lang,
tspans = Object.keys( translations ),
out = [];
for ( t = 0; t < tspans.length; t++ ) {
langs = Object.keys( translations[ tspans[ t ] ] );
for ( l = 0; l < langs.length; l++ ) {
lang = langs[ l ];
// If it's not already in the list, add it.
if ( out.indexOf( lang ) === -1 ) {
out.push( lang );
}
}
}
return out;
};

/**
* Set the source language.
*
* @param {string} lang The source language code.
*/
App.Model.prototype.setSourceLang = function ( lang ) {
// Make sure the source lang exists.
if ( this.langs.indexOf( lang ) === -1 ) {
return;
}
// If it's difference from the current value, change it.
if ( lang !== this.sourceLang ) {
this.sourceLang = lang;
this.saveToLocalStorage( 'sourceLang', lang );
this.emit( 'sourceLangSet' );
}
};

/**
* Get the source language code.
*
* @return {string}
*/
App.Model.prototype.getSourceLang = function () {
return this.sourceLang;
};

/**
* Set the target language.
*
* @param {string} lang The target language code.
*/
App.Model.prototype.setTargetLang = function ( lang ) {
if ( lang !== undefined && lang !== '' && lang !== this.targetLang ) {
this.targetLang = lang;
this.saveToLocalStorage( 'targetLang', lang );
this.emit( 'targetLangSet' );
}
};

/**
* Get the target language code.
*
* @return {string}
*/
App.Model.prototype.getTargetLang = function () {
return this.targetLang;
};

/**
* @param {string} nodeId
* @return {Object} With properties 'label' (string) and 'exists' (bool). If exists is false then
Expand All @@ -42,3 +106,55 @@ App.Model.prototype.getSourceTranslation = function ( nodeId ) {
exists: true
};
};

App.Model.prototype.getTargetTranslation = function ( nodeId ) {
if ( this.translations[ nodeId ][ this.targetLang ] === undefined ) {
return '';
}
return this.translations[ nodeId ][ this.targetLang ].text;
};

/**
* Load model values from LocalStorage.
*/
App.Model.prototype.loadFromLocalStorage = function () {
this.setSourceLang( this.getLocalStorageValue( 'sourceLang' ) );
this.setTargetLang( this.getLocalStorageValue( 'targetLang' ) );
};

/**
* Get a value from our LocalStorage item.
* @protected
* @param {string} key
* @return {*}
*/
App.Model.prototype.getLocalStorageValue = function ( key ) {
var val, data;
if ( global.localStorage === undefined ) {
return;
}
data = JSON.parse( global.localStorage.getItem( this.localStorageKey ) );
if ( data !== null && data[ key ] !== undefined ) {
val = data[ key ];
}
return val;
};

/**
* Set a value in our LocalStorage item.
* @protected
* @param {string} key
* @param {*} val
*/
App.Model.prototype.saveToLocalStorage = function ( key, val ) {
var data;
if ( global.localStorage === undefined ) {
return;
}
data = JSON.parse( global.localStorage.getItem( this.localStorageKey ) );
if ( data === null ) {
data = {};
}
data[ key ] = val;
global.localStorage.setItem( this.localStorageKey, JSON.stringify( data ) );
};
91 changes: 39 additions & 52 deletions assets/translate.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
require( './js/Model.js' );

window.alreadyUpdating = false;
window.enableInputs = function () {
window.alreadyUpdating = true;
$( '.translation-fields .oo-ui-fieldLayout .oo-ui-inputWidget' ).each( function () {
var inputWiget = OO.ui.infuse( $( this ) );
inputWiget.setDisabled( false );
} );
window.alreadyUpdating = false;

// We need it only once
window.enableInputs = function () {};
};

/**
* Add ULS to the target-language button.
*/
Expand All @@ -25,33 +12,11 @@ $( function () {
}
function switchToNewTargetLang( ulsElement, language ) {
// 1. Save the language name and code in the widget and the hidden form field.
ulsElement.setLabel( $.uls.data.languages[ language ][ 2 ] );
ulsElement.setData( language );
ulsElement.setValue( language );
$( "input[name='target-lang']" ).val( language );

// 2. Switch what's displayed in the form when a new language is selected in the ULS.
$( '.translation-fields .oo-ui-fieldLayout' ).each( function () {
var field = OO.ui.infuse( $( this ) ).getField(),
tspanId = field.data[ 'tspan-id' ];
if ( appConfig.translations[ tspanId ] &&
appConfig.translations[ tspanId ][ language ]
) {
// If there's a translation available, set the field's value.
field.setValue( appConfig.translations[ tspanId ][ language ].text );
} else {
// Otherwise, blank the field.
field.setValue( '' );
}
} );

// 3. Update the image by faking a blur event on a form input.
// 2. Update the image by faking a blur event on a form input.
$( '.translation-fields .oo-ui-fieldLayout .oo-ui-inputWidget input:first' ).trigger( 'blur' );

// 4. Mark the translation state as not unsaved.
// 3. Mark the translation state as not unsaved.
appConfig.unsaved = false;

window.enableInputs();
}
function onSelectTargetLang( language ) {
var ulsElement = this;
Expand Down Expand Up @@ -79,12 +44,13 @@ $( function () {
} );

/**
* Switch displayed 'from' language.
* Add model event handlers to the widgets.
*/
$( function () {
var sourceLangWidget,
var sourceLangWidget, targetLangWidget,
model = new App.Model( appConfig.translations ),
$sourceLangWidget = $( '.source-lang-widget' );
$sourceLangWidget = $( '.source-lang-widget' ),
$targetLangWidget = $( '.target-lang-widget' );

// Change the source lang value.
if ( $sourceLangWidget.length === 1 ) {
Expand All @@ -94,12 +60,29 @@ $( function () {
} );
}

// Change field labels when the source lang changes.
// Change the target lang value.
if ( $targetLangWidget.length === 1 ) {
targetLangWidget = OO.ui.infuse( $targetLangWidget[ 0 ] );
targetLangWidget.on( 'change', function () {
model.setTargetLang( targetLangWidget.getValue() );
} );
model.on( 'targetLangSet', function () {
// Modify the form value that will be submitted.
$( "input[name='target-lang']" ).val( model.getTargetLang() );
// We can assume ULS contains the language, because that's how it's been set.
targetLangWidget.setLabel( $.uls.data.languages[ model.getTargetLang() ][ 2 ] );
targetLangWidget.setData( model.getTargetLang() );
} );
}

// Add event handlers for the translation fields.
$( '.translation-fields .oo-ui-fieldLayout' ).each( function () {
var fieldLayout = OO.ui.infuse( $( this ) );
var fieldLayout = OO.ui.infuse( $( this ) ),
nodeId = fieldLayout.getField().data[ 'tspan-id' ];

// Change field labels when the source lang changes.
model.on( 'sourceLangSet', function () {
var nodeId = fieldLayout.getField().data[ 'tspan-id' ],
sourceTranslation = model.getSourceTranslation( nodeId );
var sourceTranslation = model.getSourceTranslation( nodeId );
if ( !sourceTranslation.exists ) {
fieldLayout.setLabel(
$.i18n( 'source-lang-not-found', [ sourceTranslation.label ] )
Expand All @@ -110,7 +93,19 @@ $( function () {
fieldLayout.$element.removeClass( 'source-lang-not-found' );
}
} );

// Change field values when the target lang changes.
model.on( 'targetLangSet', function () {
var targetTranslation = model.getTargetTranslation( nodeId );
fieldLayout.getField().setValue( targetTranslation );
fieldLayout.getField().setDisabled( false );
} );
} );

// After adding all the event handlers above, update the widget values.
model.loadFromLocalStorage();
sourceLangWidget.setValue( model.getSourceLang() );
targetLangWidget.setValue( model.getTargetLang() );
} );

/**
Expand Down Expand Up @@ -155,21 +150,13 @@ $( window ).on( 'load', function () {
} );
};

if ( targetLangWidget.getValue() !== 'fallback' ) {
// 'fallback' means no language preselected, otherwise enable the controls
inputWiget.setDisabled( false );
}

// Update the preview image on field blur and after two seconds of no typing.
inputWiget.$input.on( 'blur', updatePreviewImage );
inputWiget.on( 'change', OO.ui.debounce( updatePreviewImage, 2000 ) );
inputWiget.on( 'change', function () {
appConfig.unsaved = true;
} );
} );
// Trigger a pretend blur on the first input field, in order to force a refresh of the preview
// on page load (to catch browser-cached input values).
$( '.translation-fields .oo-ui-fieldLayout .oo-ui-inputWidget input:first' ).trigger( 'blur' );
} );

/**
Expand Down
Loading

0 comments on commit 30f4b55

Please sign in to comment.