diff --git a/dev/Common/Audio.js b/dev/Common/Audio.js
index 8e437fc88..13863510d 100644
--- a/dev/Common/Audio.js
+++ b/dev/Common/Audio.js
@@ -74,9 +74,7 @@
// Audio.prototype.record = function ()
// {
// this.getUserMedia({audio:true}, function () {
-// window.console.log(arguments);
// }, function(oError) {
-// window.console.log(arguments);
// });
// };
diff --git a/dev/Common/Globals.js b/dev/Common/Globals.js
index 19a194552..acb582852 100644
--- a/dev/Common/Globals.js
+++ b/dev/Common/Globals.js
@@ -1,4 +1,6 @@
+/* global RL_COMMUNITY */
+
(function () {
'use strict';
diff --git a/dev/Common/Translator.js b/dev/Common/Translator.js
index b9fd187ea..5a5e9d87f 100644
--- a/dev/Common/Translator.js
+++ b/dev/Common/Translator.js
@@ -10,6 +10,7 @@
ko = require('ko'),
Enums = require('Common/Enums'),
+ Utils = require('Common/Utils'),
Globals = require('Common/Globals')
;
@@ -24,6 +25,8 @@
this.trigger = ko.observable(false);
this.i18n = _.bind(this.i18n, this);
+
+ this.init();
}
Translator.prototype.data = {};
@@ -293,29 +296,41 @@
{
var
self = this,
- $html = $('html'),
- fEmptyFunction = function () {},
iStart = (new Date()).getTime()
;
- $html.addClass('rl-changing-language');
+ Globals.$html.addClass('rl-changing-language');
$.ajax({
'url': require('Common/Links').langLink(sLanguage, bAdmin),
'dataType': 'script',
'cache': true
})
- .fail(fFail || fEmptyFunction)
+ .fail(fFail || Utils.emptyFunction)
.done(function () {
_.delay(function () {
+
self.reloadData();
- (fDone || fEmptyFunction)();
- $html.removeClass('rl-changing-language');
+
+ (fDone || Utils.emptyFunction)();
+
+ Globals.$html
+ .removeClass('rl-changing-language')
+ .removeClass('rl-rtl rl-ltr')
+ .addClass(-1 < Utils.inArray(sLanguage, ['ar', 'he', 'ur']) ? 'rl-rtl' : 'rl-ltr')
+// .attr('dir', -1 < Utils.inArray(sLanguage, ['ar', 'he', 'ur']) ? 'rtl' : 'ltr')
+ ;
+
}, 500 < (new Date()).getTime() - iStart ? 1 : 500);
})
;
};
+ Translator.prototype.init = function ()
+ {
+ Globals.$html.addClass('rl-' + (Globals.$html.attr('dir') || 'ltr'));
+ };
+
module.exports = new Translator();
}());
diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js
index 49119012d..ed59539b0 100644
--- a/dev/Common/Utils.js
+++ b/dev/Common/Utils.js
@@ -852,7 +852,11 @@
},
convertPre = function () {
- return (arguments && 1 < arguments.length) ? arguments[1].toString().replace(/[\n]/gm, '
') : '';
+ return (arguments && 1 < arguments.length) ?
+ arguments[1].toString()
+ .replace(/[\n]/gm, '
')
+ .replace(/[\r]/gm, '')
+ : '';
},
fixAttibuteValue = function () {
@@ -868,7 +872,7 @@
sText = sHtml
.replace(/\u0002([\s\S]*)\u0002/gm, '\u200C$1\u200C')
.replace(/
]*><\/p>/gi, '') - .replace(/
]*>([\s\S\r\n]*)<\/pre>/gmi, convertPre)
+ .replace(/]*>([\s\S\r\n\t]*)<\/pre>/gmi, convertPre)
.replace(/[\s]+/gm, ' ')
.replace(/((?:href|data)\s?=\s?)("[^"]+?"|'[^']+?')/gmi, fixAttibuteValue)
.replace(/
]*>/gmi, '\n')
diff --git a/dev/Component/Select.js b/dev/Component/Select.js
index a987e7669..046f7603a 100644
--- a/dev/Component/Select.js
+++ b/dev/Component/Select.js
@@ -7,6 +7,7 @@
_ = require('_'),
Utils = require('Common/Utils'),
+ Translator = require('Common/Translator'),
AbstractInput = require('Component/AbstractInput')
;
@@ -26,6 +27,12 @@
this.optionsText = oParams.optionsText || null;
this.optionsValue = oParams.optionsValue || null;
+ this.optionsCaption = oParams.optionsCaption || null;
+
+ if (this.optionsCaption)
+ {
+ this.optionsCaption = Translator.i18n(this.optionsCaption);
+ }
this.defautOptionsAfterRender = Utils.defautOptionsAfterRender;
}
diff --git a/dev/Storage/Settings.js b/dev/Storage/Settings.js
index ef12c2a8f..3fecebfc2 100644
--- a/dev/Storage/Settings.js
+++ b/dev/Storage/Settings.js
@@ -16,7 +16,6 @@
{
this.oSettings = window['rainloopAppData'] || {};
this.oSettings = Utils.isNormal(this.oSettings) ? this.oSettings : {};
-// window.console.log(this.oSettings);
}
SettingsStorage.prototype.oSettings = null;
diff --git a/dev/Stores/User/Message.js b/dev/Stores/User/Message.js
index 718ba6310..53b019820 100644
--- a/dev/Stores/User/Message.js
+++ b/dev/Stores/User/Message.js
@@ -526,6 +526,7 @@
var
bNew = false,
bIsHtml = false,
+ sTag = 'div',
bHasExternals = false,
bHasInternals = false,
oBody = null,
@@ -584,9 +585,6 @@
bHasExternals = !!oData.Result.HasExternals;
bHasInternals = !!oData.Result.HasInternals;
- oBody = $('').hide().addClass('rl-cache-class');
- oBody.data('rl-cache-count', ++Globals.iMessageBodyCacheCount);
-
if (Utils.isNormal(oData.Result.Html) && '' !== oData.Result.Html)
{
bIsHtml = true;
@@ -625,6 +623,10 @@
).html()
;
}
+ else
+ {
+ sResultHtml = '' + sResultHtml + '
';
+ }
sPlain = '';
@@ -633,12 +635,20 @@
oMessage.isPgpSigned(bPgpSigned);
oMessage.isPgpEncrypted(bPgpEncrypted);
}
+ else
+ {
+ sResultHtml = '' + sResultHtml + '
';
+ }
}
else
{
bIsHtml = false;
+ sResultHtml = '' + sResultHtml + '
';
}
+ oBody = $('').hide().addClass('rl-cache-class');
+ oBody.data('rl-cache-count', ++Globals.iMessageBodyCacheCount);
+
oBody
.html(Utils.findEmailAndLinks(sResultHtml))
.addClass('b-text-part ' + (bIsHtml ? 'html' : 'plain'))
@@ -753,8 +763,6 @@
{
if (sFolder && sUid)
{
- window.console.log(sFolder, sUid);
-
this.message(this.staticMessage.populateByMessageListItem(null));
this.message().folderFullNameRaw = sFolder;
this.message().uid = sUid;
diff --git a/dev/Stores/User/Pgp.js b/dev/Stores/User/Pgp.js
index 44815d225..c6ae88594 100644
--- a/dev/Stores/User/Pgp.js
+++ b/dev/Stores/User/Pgp.js
@@ -82,6 +82,28 @@
}), true));
};
+ /**
+ * @param {string} sEmail
+ * @return {?}
+ */
+ PgpUserStore.prototype.findPublicKeyByEmailNotNative = function (sEmail)
+ {
+ return _.find(this.openpgpkeysPublic(), function (oItem) {
+ return oItem && sEmail === oItem.email;
+ }) || null;
+ };
+
+ /**
+ * @param {string} sEmail
+ * @return {?}
+ */
+ PgpUserStore.prototype.findPrivateKeyByEmailNotNative = function (sEmail)
+ {
+ return _.find(this.openpgpkeysPrivate(), function (oItem) {
+ return oItem && sEmail === oItem.email;
+ }) || null;
+ };
+
/**
* @param {string} sEmail
* @param {string=} sPassword
diff --git a/dev/Styles/OpenPgpKey.less b/dev/Styles/OpenPgpKey.less
index 8b2d753da..b9ff7a9f2 100644
--- a/dev/Styles/OpenPgpKey.less
+++ b/dev/Styles/OpenPgpKey.less
@@ -19,6 +19,72 @@
}
}
+ .b-compose-open-pgp-content {
+ &.modal {
+ width: 700px;
+ }
+
+ .key-list {
+
+ background-color: #f9f9f9;
+ border-radius: 5px;
+ padding: 10px 15px;
+ margin-top: 10px;
+ min-height: 40px;
+
+ &-wrp {
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ &.empty {
+ text-align: center;
+ padding-top: 10px;
+ color: #aaa;
+ font-size: 16px;
+ }
+ }
+
+ &__item {
+
+ color: #333;
+ white-space: nowrap;
+
+ &-delete {
+ display: inline;
+ cursor: pointer;
+ margin-right: 5px;
+ }
+
+ &-name {
+ margin-right: 5px;
+ color: #333;
+ display: inline;
+
+ &.empty {
+ color: red;
+ }
+ }
+
+ &-error {
+ margin-right: 5px;
+ color: red;
+ display: inline;
+ }
+
+ &-hash {
+ margin-right: 5px;
+ color: #aaa;
+ display: inline;
+ }
+ }
+ }
+
+ .key-actions {
+ margin-top: 10px;
+ min-height: 40px;
+ }
+ }
+
.b-message-open-pgp-content {
&.modal {
width: 700px;
diff --git a/dev/View/Popup/ComposeOpenPgp.js b/dev/View/Popup/ComposeOpenPgp.js
index 55a3f638e..2ead22bc8 100644
--- a/dev/View/Popup/ComposeOpenPgp.js
+++ b/dev/View/Popup/ComposeOpenPgp.js
@@ -28,88 +28,137 @@
{
AbstractView.call(this, 'Popups', 'PopupsComposeOpenPgp');
+ var self = this;
+
+ this.optionsCaption = Translator.i18n('@i18n/Add a public key');
+
this.notification = ko.observable('');
- this.sign = ko.observable(true);
- this.encrypt = ko.observable(true);
+ this.sign = ko.observable(false);
+ this.encrypt = ko.observable(false);
this.password = ko.observable('');
this.password.focus = ko.observable(false);
this.buttonFocus = ko.observable(false);
- this.from = ko.observable('');
- this.to = ko.observableArray([]);
this.text = ko.observable('');
+ this.selectedPublicKey = ko.observable(null);
- this.resultCallback = null;
+ this.signKey = ko.observable(null);
+ this.encryptKeys = ko.observableArray([]);
+
+ this.encryptKeysView = ko.computed(function () {
+ return _.compact(_.map(this.encryptKeys(), function (oKey) {
+ return oKey ? oKey.key : null;
+ }));
+ }, this);
+
+ this.publicKeysOptions = ko.computed(function () {
+ return _.compact(_.map(PgpStore.openpgpkeysPublic(), function (oKey) {
+ return -1 < Utils.inArray(oKey, self.encryptKeysView()) ? null : {
+ 'id': oKey.guid,
+ 'name': '(' + oKey.id.substr(-6) + ') ' + oKey.user,
+ 'key': oKey
+ };
+ }));
+ });
this.submitRequest = ko.observable(false);
+ this.resultCallback = null;
+
// commands
this.doCommand = Utils.createCommand(this, function () {
var
- self = this,
bResult = true,
oPrivateKey = null,
+ aPrivateKeys = [],
aPublicKeys = []
;
this.submitRequest(true);
- if (bResult && this.sign() && '' === this.from())
- {
- this.notification(Translator.i18n('PGP_NOTIFICATIONS/SPECIFY_FROM_EMAIL'));
- bResult = false;
- }
-
if (bResult && this.sign())
{
- oPrivateKey = PgpStore.findPrivateKeyByEmail(this.from(), this.password());
- if (!oPrivateKey)
+ if (!this.signKey())
+ {
+ this.notification(Translator.i18n('PGP_NOTIFICATIONS/SPECIFY_FROM_EMAIL'));
+ bResult = false;
+ }
+ else if (!this.signKey().key)
{
this.notification(Translator.i18n('PGP_NOTIFICATIONS/NO_PRIVATE_KEY_FOUND_FOR', {
- 'EMAIL': this.from()
+ 'EMAIL': this.signKey().email
}));
bResult = false;
}
- }
- if (bResult && this.encrypt() && 0 === this.to().length)
- {
- this.notification(Translator.i18n('PGP_NOTIFICATIONS/SPECIFY_AT_LEAST_ONE_RECIPIENT'));
- bResult = false;
+ if (bResult)
+ {
+ aPrivateKeys = this.signKey().key.getNativeKeys();
+ oPrivateKey = aPrivateKeys[0] || null;
+
+ try
+ {
+ if (oPrivateKey)
+ {
+ oPrivateKey.decrypt(Utils.pString(this.password()));
+ }
+ }
+ catch (e)
+ {
+ oPrivateKey = null;
+ }
+
+ if (!oPrivateKey)
+ {
+ this.notification(Translator.i18n('PGP_NOTIFICATIONS/NO_PRIVATE_KEY_FOUND'));
+ bResult = false;
+ }
+ }
}
if (bResult && this.encrypt())
{
- aPublicKeys = [];
- _.each(this.to(), function (sEmail) {
- var aKeys = PgpStore.findPublicKeysByEmail(sEmail);
- if (0 === aKeys.length && bResult)
- {
- self.notification(Translator.i18n('PGP_NOTIFICATIONS/NO_PUBLIC_KEYS_FOUND_FOR', {
- 'EMAIL': sEmail
- }));
+ if (0 === this.encryptKeys().length)
+ {
+ this.notification(Translator.i18n('PGP_NOTIFICATIONS/NO_PUBLIC_KEYS_FOUND'));
+ bResult = false;
+ }
+ else if (this.encryptKeys())
+ {
+ aPublicKeys = [];
+ _.each(this.encryptKeys(), function (oKey) {
+ if (oKey && oKey.key)
+ {
+ aPublicKeys = aPublicKeys.concat(_.compact(_.flatten(oKey.key.getNativeKeys())));
+ }
+ else if (oKey && oKey.email)
+ {
+ self.notification(Translator.i18n('PGP_NOTIFICATIONS/NO_PUBLIC_KEYS_FOUND_FOR', {
+ 'EMAIL': oKey.email
+ }));
+
+ bResult = false;
+ }
+ });
+
+ if (bResult && (0 === aPublicKeys.length || this.encryptKeys().length !== aPublicKeys.length))
+ {
bResult = false;
}
-
- aPublicKeys = aPublicKeys.concat(aKeys);
- });
-
- if (bResult && (0 === aPublicKeys.length || this.to().length !== aPublicKeys.length))
- {
- bResult = false;
}
}
- _.delay(function () {
+ if (bResult && self.resultCallback)
+ {
+ _.delay(function () {
- if (self.resultCallback && bResult)
- {
var oPromise = null;
+
try
{
if (oPrivateKey && 0 === aPublicKeys.length)
@@ -154,17 +203,56 @@
}));
}
}
- }
- self.submitRequest(false);
+ self.submitRequest(false);
- }, 10);
+ }, 10);
+ }
+
+ return bResult;
}, function () {
return !this.submitRequest() && (this.sign() || this.encrypt());
});
+ this.addCommand = Utils.createCommand(this, function () {
+
+ var
+ sKeyId = this.selectedPublicKey(),
+ aKeys = this.encryptKeys(),
+ oOption = sKeyId ? _.find(this.publicKeysOptions(), function (oItem) {
+ return oItem && sKeyId === oItem.id;
+ }) : null
+ ;
+
+ if (oOption)
+ {
+ aKeys.push({
+ 'empty': !oOption.key,
+ 'selected': ko.observable(!!oOption.key),
+ 'user': oOption.key.user,
+ 'hash': oOption.key.id.substr(-6),
+ 'key': oOption.key
+ });
+
+ this.encryptKeys(aKeys);
+ }
+
+ }, function () {
+ return !this.submitRequest() && this.selectedPublicKey();
+ });
+
+ this.selectedPublicKey.subscribe(function (sValue) {
+ if (sValue)
+ {
+ this.addCommand();
+ }
+ }, this);
+
this.sDefaultKeyScope = Enums.KeyState.PopupComposeOpenPGP;
+ this.defautOptionsAfterRender = Utils.defautOptionsAfterRender;
+
+ this.deletePublickKey = _.bind(this.deletePublickKey, this);
kn.constructorEnd(this);
}
@@ -172,16 +260,24 @@
kn.extendAsViewModel(['View/Popup/ComposeOpenPgp', 'PopupsComposeOpenPgpViewModel'], ComposeOpenPgpPopupView);
_.extend(ComposeOpenPgpPopupView.prototype, AbstractView.prototype);
+ ComposeOpenPgpPopupView.prototype.deletePublickKey = function (oKey)
+ {
+ this.encryptKeys.remove(oKey);
+ };
+
ComposeOpenPgpPopupView.prototype.clearPopup = function ()
{
this.notification('');
+ this.sign(false);
+ this.encrypt(false);
+
this.password('');
this.password.focus(false);
this.buttonFocus(false);
- this.from('');
- this.to([]);
+ this.signKey(null);
+ this.encryptKeys([]);
this.text('');
this.submitRequest(false);
@@ -231,6 +327,8 @@
var
aRec = [],
+ sEmail = '',
+ oKey = null,
oEmail = new EmailModel()
;
@@ -258,8 +356,44 @@
return '' === oEmail.email ? false : oEmail.email;
}));
- this.from(oIdentity ? oIdentity.email() : '');
- this.to(aRec);
+ if (oIdentity && oIdentity.email())
+ {
+ sEmail = oIdentity.email();
+ oKey = PgpStore.findPrivateKeyByEmailNotNative(sEmail);
+ if (oKey)
+ {
+ this.signKey({
+ 'user': oKey.user || sEmail,
+ 'hash': oKey.id.substr(-6),
+ 'key': oKey
+ });
+ }
+ }
+
+ if (this.signKey())
+ {
+ this.sign(true);
+ }
+
+ if (aRec && 0 < aRec.length)
+ {
+ this.encryptKeys(_.compact(_.map(aRec, function (sEmail) {
+ var oKey = PgpStore.findPublicKeyByEmailNotNative(sEmail) || null;
+ return {
+ 'empty': !oKey,
+ 'selected': ko.observable(!!oKey),
+ 'user': oKey ? (oKey.user || sEmail) : sEmail,
+ 'hash': oKey ? oKey.id.substr(-6) : '',
+ 'key': oKey
+ };
+ })));
+
+ if (0 < this.encryptKeys().length)
+ {
+ this.encrypt(true);
+ }
+ }
+
this.text(sText);
};
diff --git a/gulpfile.js b/gulpfile.js
index 6bce4423f..540b0a738 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -47,6 +47,7 @@ var
plumber = require('gulp-plumber'),
gulpif = require('gulp-if'),
eol = require('gulp-eol'),
+ livereload = require('gulp-livereload'),
gutil = require('gulp-util')
;
@@ -242,6 +243,7 @@ gulp.task('css:main-begin', ['less:main'], function() {
// .pipe(csslint.reporter())
.pipe(eol('\n', true))
.pipe(gulp.dest(cfg.paths.staticCSS))
+ .pipe(livereload())
;
});
@@ -330,7 +332,7 @@ gulp.task('js:webpack:clear', function() {
.pipe(require('gulp-rimraf')());
});
-gulp.task('js:webpack', ['js:webpack:clear'], function(callback) {
+gulp.task('js:webpack', [/*'js:webpack:clear'*/], function(callback) {
var
webpack = require('webpack'),
@@ -678,12 +680,14 @@ gulp.task('owncloud+', ['package:community-off', 'owncloud-']);
//WATCH
gulp.task('watch', ['fast'], function() {
cfg.watch = true;
+ livereload.listen();
gulp.watch(cfg.paths.globjs, {interval: 1000}, ['js:app', 'js:admin']);
gulp.watch(cfg.paths.less.main.watch, {interval: 1000}, ['css:main']);
});
gulp.task('watch+', ['fast+'], function() {
cfg.watch = true;
+ livereload.listen();
gulp.watch(cfg.paths.globjs, {interval: 1000}, ['js:app', 'js:admin']);
gulp.watch(cfg.paths.less.main.watch, {interval: 1000}, ['css:main']);
});
diff --git a/package.json b/package.json
index 9e208ed56..918f2d74e 100644
--- a/package.json
+++ b/package.json
@@ -73,6 +73,7 @@
"gulp-through": "~0.3.0",
"lodash": "~3.9.3",
"gulp-if": "~1.2.5",
- "node-notifier": "~4.2.3"
+ "node-notifier": "~4.2.3",
+ "gulp-livereload": "~3.8.0"
}
}
diff --git a/rainloop/v/0.0.0/app/i18n/langs.ini b/rainloop/v/0.0.0/app/i18n/langs.ini
index 9f19d3d74..88bea1b60 100644
--- a/rainloop/v/0.0.0/app/i18n/langs.ini
+++ b/rainloop/v/0.0.0/app/i18n/langs.ini
@@ -104,6 +104,9 @@ LANG_BS = "Bosanski"
LANG_BS_BS = "Bosanski"
LANG_BS_BA = "Bosanski"
+LANG_AR = "العربية"
+LANG_AR_AR = "العربية"
+
[LANGS_NAMES_EN]
LANG_EN = "English"
LANG_EN_US = "English (US)"
@@ -210,3 +213,6 @@ LANG_SR_RS = "Serbian"
LANG_BS = "Bosnian"
LANG_BS_BS = "Bosnian"
LANG_BS_BA = "Bosnian"
+
+LANG_AR = "Arabic"
+LANG_AR_AR = "Arabic"
diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php
index 5f2c47814..040f04945 100644
--- a/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php
+++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php
@@ -266,7 +266,8 @@ class Service
'{{BaseAppOpenPgpScriptLink}}' => $aData['OpenPgpJsLink'],
'{{BaseAppMainCommonScriptLink}}' => $aData['AppJsCommonLink'],
'{{BaseAppMainScriptLink}}' => $aData['AppJsLink'],
- '{{BaseDir}}' => \in_array($aData['Language'], array('ar', 'he', 'ur')) ? 'rtl' : 'ltr'
+ '{{BaseDir}}' => 'ltr'
+// '{{BaseDir}}' => \in_array($aData['Language'], array('ar', 'he', 'ur')) ? 'rtl' : 'ltr'
);
$aTemplateParameters['{{BaseHash}}'] = \md5(
diff --git a/rainloop/v/0.0.0/app/templates/Index.html b/rainloop/v/0.0.0/app/templates/Index.html
index 081b99915..da86d980c 100644
--- a/rainloop/v/0.0.0/app/templates/Index.html
+++ b/rainloop/v/0.0.0/app/templates/Index.html
@@ -1,5 +1,5 @@
-
+
diff --git a/rainloop/v/0.0.0/app/templates/Views/Components/Select.html b/rainloop/v/0.0.0/app/templates/Views/Components/Select.html
index 62d3d4a82..5d6532716 100644
--- a/rainloop/v/0.0.0/app/templates/Views/Components/Select.html
+++ b/rainloop/v/0.0.0/app/templates/Views/Components/Select.html
@@ -4,7 +4,7 @@
+ optionsCaption: optionsCaption, css: className, optionsAfterRender: defautOptionsAfterRender">
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsComposeOpenPgp.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsComposeOpenPgp.html
index 68e6051a0..22020aba9 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsComposeOpenPgp.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsComposeOpenPgp.html
@@ -8,34 +8,83 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
+
+ No private key found
+
+
+
+
+ ()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No public keys selected
+
+
+
+
+
+
+
+
+ ()
+
+
+
+
+
+ (Public key not found)
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/vendors/flags/flags-fixed.css b/vendors/flags/flags-fixed.css
index 799431bbc..2402e12e0 100644
--- a/vendors/flags/flags-fixed.css
+++ b/vendors/flags/flags-fixed.css
@@ -17,6 +17,7 @@
.flag.flag-en-uk {background-position: -176px -44px}
.flag.flag-en-ca {background-position: -48px -22px}
+.flag.flag-ar, .flag.flag-ar-ar {background-position: -160px 0}
.flag.flag-bg, .flag.flag-bg-bg {background-position: -80px -11px}
.flag.flag-nl, .flag.flag-nl-nl {background-position: -80px -110px}
.flag.flag-pl, .flag.flag-pl-pl {background-position: -32px -121px}