diff --git a/i/css/i.css b/i/css/i.css new file mode 100644 index 0000000..7f28428 --- /dev/null +++ b/i/css/i.css @@ -0,0 +1,46 @@ +body { + background: #f8f8f8; + color: #000000; + font-size: 1em; + font-family: sans-serif; +} +h3 { + font-size: 1.5em; + font-weight: 400; + line-height: 1.1; + color: #444; +} + +a { + color: #1b8250; +} + +li { + padding-bottom: 0.75em; +} + +.btn:hover, .btn:focus, .btn.focus { + color: #666; + text-decoration: none; +} + +.btn-primary { + background: #43a047; + color: #f2f2f2; +} +.btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { + background: #388e3c; + color: #000000; + box-shadow: none; +} +.btn-primary:active, .btn-primary.active { + background: #2e7d32; + box-shadow: none; +} +.text-center { + text-align: center; +} +.hint { + font-size: 0.9em; + color: #444; +} diff --git a/i/css/styles.css b/i/css/styles.css new file mode 100644 index 0000000..abdd5f7 --- /dev/null +++ b/i/css/styles.css @@ -0,0 +1,66 @@ +/* Mostly cherrypicked from bootstrap https://getbootstrap.com/css/ */ +.btn { + display: inline-block; + margin-bottom: 0; + font-weight: normal; + text-align: center; + vertical-align: middle; text-transform: uppercase; + border-right: none; + border-bottom: none; + color: #666; + text-decoration: none; + transition: all .2s; + touch-action: manipulation; + cursor: pointer; + border: 1px solid transparent; + white-space: nowrap; + padding: 6px 16px; + font-size: 13px; + line-height: 1.846; + border-radius: 3px; + -webkit-user-select: none; + -moz-user-select: none; +} + +.btn:hover, .btn:focus, .btn.focus { + color: #666; + text-decoration: none; +} + +textarea, textarea.form-control, input.form-control, input[type="text"], input[type="password"], input[type="email"], input[type="number"], .form-control[type="text"], .form-control[type="password"], .form-control[type="email"], .form-control[type="tel"] { + padding: 0; + border: none; + border-radius: 0; + -webkit-box-shadow: inset 0 -1px 0 #ddd; + box-shadow: inset 0 -1px 0 #ddd; + font-size: 1.1em; +} + +.form-control { + display: block; + width: 100%; + height: 37px; + line-height: 1.846; + color: #666; + background-color: transparent; + background-image: none; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075); + box-shadow: inset 0 1px 1px rgba(0,0,0,0.075); + -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} + +input, button { + -webkit-font-smoothing: antialiased; + letter-spacing: .1px; + text-rendering: optimizeLegibility; +} + +a { + text-decoration: none; +} + +a:hover, a:focus { + color: #165d16; + text-decoration: underline; +} diff --git a/i/index.html b/i/index.html new file mode 100644 index 0000000..b50b7e2 --- /dev/null +++ b/i/index.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + +
+ +

+

+ +

+

+ + + +

+

+
+ + + + + diff --git a/i/js/i18n-text.js b/i/js/i18n-text.js new file mode 100644 index 0000000..3003f14 --- /dev/null +++ b/i/js/i18n-text.js @@ -0,0 +1,6 @@ +/*! + i18n-text - v0.4.3 - 2014-08-14 + https://vogdb@bitbucket.org/vogdb/i18n-text.git + Copyright (c) 2014 Sanin Aleksey aka vogdb; Licensed WTFPL +*/ +!function(a){function b(a){return"[object Object]"===Object.prototype.toString.call(a)}function c(a,b){return a.replace(/{{([\w]+)}}/g,function(a,c){var d=b[c];return d?d:a})}function d(a,b){throw new Error(c(a,b))}function e(a){return-1!==a.indexOf("file://")}function f(a){return e(a)||e(window.location.href)&&a.indexOf(!1)}var g={parseAndEval:function(a,b){return a.replace(/{{([^{}]+#[^}]*(?:|.*#.*)+)}}/g,function(a,c){return g.eval(c,b)})},eval:function(a,b){for(var c=a.split("|"),e=c.length-1;e>=0;e--){var f=c[e].split("#"),i=f[0];if(g.isValidFormula(i)||d(h.error.INVALID_PLURAL,{plural:i}),g.evalFormula(i,b))return f[1]}return a},isValidFormula:function(a){return/[ \dn<>=*-+!?]+/g.test(a)},evalFormula:function(a,b){return new Function("n","return "+a)(b)}},h=function(a){this._loadedLocales={},this._currentLocale=null,this._msgPath=null,this._subscribers={},this._init(a)};h.prototype.getLocale=function(){return this._currentLocale},h.prototype.setLocale=function(a){this.hasLocale(a)?this._setLocale(a):(this.once(h.event.LOCALE_LOAD,function(b){b.error||b.locale!==a||this._setLocale(b.locale)}.bind(this)),this.loadLocale(a))},h.prototype._setLocale=function(a){this._currentLocale=a,this._fire(h.event.LOCALE_CHANGE,{locale:a})},h.prototype.hasLocale=function(a){return!!this._loadedLocales[a]},h.prototype.loadLocale=function(a,b){b=b||this._msgPath;var d=b+"/"+a+".json";h.loadFile({url:d,success:function(b){b.length>0?(this._loadedLocales[a]={},this._mergeKeys(JSON.parse(b),this._loadedLocales[a],""),this._fire(h.event.LOCALE_LOAD,{locale:a})):this._fire(h.event.LOCALE_LOAD,{error:c(h.error.EMPTY_MESSAGES,{file:d})})}.bind(this),error:function(a){this._fire(h.event.LOCALE_LOAD,{error:c(h.error.NO_MESSAGES,{file:d,error:a})})}.bind(this)})},h.prototype._mergeKeys=function(a,c,d){for(var e in a)if(b(a[e])){var f;f=d?d+h.KEY_SEPARATOR+e:e,this._mergeKeys(a[e],c,f)}else{var g;g=d?d+h.KEY_SEPARATOR+e:e,c[g]=a[e]}},h.prototype.text=function(a,e,f){arguments[1]&&!b(arguments[1])&&(f=arguments[1],e=void 0);var i=f||this.getLocale();i||d(h.error.NO_LOCALE_IS_SET),this.hasLocale(i)||this.loadLocale(i);var j=this._loadedLocales[i][a];return void 0===j&&d(h.error.INVALID_KEY,{key:a,locale:i}),e?(void 0!==e.n&&(j=g.parseAndEval(j,e.n)),c(j,e)):j},h.prototype.on=function(a,b){this._subscribers[a]||(this._subscribers[a]=[]),this._subscribers[a].push(b)},h.prototype._fire=function(a,b){if(this._subscribers[a])for(var c=this._subscribers[a],d=c.length-1;d>=0;d--)c[d].call(null,b)},h.prototype.off=function(a,b){if(this._subscribers[a]){if(b){var c=this._subscribers[a].indexOf(b);this._subscribers[a].splice(c,1)}b&&0!==this._subscribers.length||delete this._subscribers[a]}},h.prototype.once=function(a,b){this.on(a,function(c){this.off(a,b),b(c)}.bind(this))},h.prototype._init=function(a){a||d(h.error.NO_OPTS),a.path?this._msgPath=a.path:d(h.error.NO_PATH)},h.loadFile=function(a){var b=new XMLHttpRequest;b.open("GET",a.url),b.onreadystatechange=function(){4===b.readyState&&(200==b.status||f(a.url)&&b.responseText.length>0?a.success(b.responseText):a.error(b.responseText))},b.send(null)},h.error={NO_OPTS:"Options are not present",NO_PATH:"Options path is not present",NO_MESSAGES:"{{file}} is unreachable. Error {{error}}",EMPTY_MESSAGES:"{{file}} is empty",INVALID_KEY:"{{key}} key is not present in locale {{locale}}",NO_LOCALE_IS_SET:"Locale is not set.",INVALID_PLURAL:"Invalid plural form: {{plural}}"},h.event={LOCALE_LOAD:"localeload",LOCALE_CHANGE:"localechange"},h.KEY_SEPARATOR=".","function"==typeof define&&define.amd?define(function(){return h}):a.I18nText=h}("undefined"==typeof window?this:window); \ No newline at end of file diff --git a/i/js/main.js b/i/js/main.js new file mode 100644 index 0000000..f201015 --- /dev/null +++ b/i/js/main.js @@ -0,0 +1,121 @@ +(function() { + 'use strict'; + + var initialized = false; + var i18n; + + // i18n key prefix for MUC ("muc.") or 1:1 chat ("chat.") + var key_prefix; + var display_data = null; + + function show_clients(client_array) { + var list = document.getElementById('client_list'); + for (var id = 0; id < client_array.length; id++) { + var item = document.createElement('li'); + item.innerHTML = client_array[id]; + list.appendChild(item); + } + } + + function load_clients(url) { + var request = new XMLHttpRequest(); + request.open('GET', url); + request.onreadystatechange = function () { + if (request.readyState === 4) { + if (request.status === 200 || (isLocalFileRequest(url) && request.responseText.length > 0)) { + show_clients(JSON.parse(request.responseText)); + } + } + }; + request.send(null); + } + + function load_hash() { + var muc = false; + key_prefix = "chat."; + var jid = window.location.search || window.location.hash; + jid = decodeURIComponent(jid.substring(jid.indexOf('#') + 1, jid.length)); + + if (jid === "") + jid = "decentralized@conference.moemoekyun.moe?join" + + try { + base_decoded = window.atob(jid); + if (base_decoded.search('@') >= 0) + jid = base_decoded; + } catch (err) { + // ignore error, JID wasn't base64 encoded + } + if (jid.search("\\?join") >= 0) { + muc = true; + key_prefix = "muc."; + } + + // TODO: proper error checking / display / Creation of invitations + if (jid.search("@") <= 0) return {jid: jid, name: jid}; + + var name = jid.split("@")[0]; + name = name.charAt(0).toUpperCase() + name.slice(1); + + return {jid: jid, name: name}; + } + + function translate_ui() { + // translation + document.title = i18n.text(key_prefix + 'title', display_data); + // MUC/chat specific + ['heading', 'button'].forEach(function(id) { + document.getElementById(id).innerHTML = i18n.text(key_prefix + id, display_data); + }); + // and agnostic + ['clients', 'recommend', 'checkfulllist', 'xmppis'].forEach(function(id) { + document.getElementById(id).innerHTML = i18n.text(id, display_data); + }); + } + + function rehash() { + display_data = load_hash(); + document.getElementById('button').href = "xmpp:" + display_data.jid; + document.getElementById('url_in').value = "xmpp:" + display_data.jid; + translate_ui(); + } + + function load_done() { + if (initialized) return; + initialized = true; + + // load i18n and perform translation + i18n = new I18nText({path: 'lang'}); + i18n.once(I18nText.event.LOCALE_CHANGE, function (data) { + rehash(); + }); + i18n.setLocale('ru'); + + // functionality + if (navigator.userAgent.indexOf("Android") >= 0) { + load_clients("json/clients_Android.json") + } + else if (navigator.userAgent.indexOf("Linux") >= 0) { + load_clients("json/clients_Linux.json") + } + else if (navigator.userAgent.indexOf("iPhone") >= 0) { + load_clients("json/clients_iOS.json") + } + else { + load_clients("json/clients_Windows.json") + } + + window.addEventListener("hashchange", rehash, false); + document.getElementById("url_in").addEventListener("focus", function(event) { + event.target.select(); + }); + } + + // Wait for the DOM to be ready + document.addEventListener('DOMContentLoaded', load_done, false); + document.onreadystatechange = function() { + if (document.readyState === 'interactive') { + load_done(); + } + }; +})(); diff --git a/i/json/clients_Android.json b/i/json/clients_Android.json new file mode 100644 index 0000000..49c6069 --- /dev/null +++ b/i/json/clients_Android.json @@ -0,0 +1,3 @@ +[ + "Pix-Art Messenger - современный, полнофункциональный и сфокусированный на удобстве пользования" +] diff --git a/i/json/clients_Linux.json b/i/json/clients_Linux.json new file mode 100644 index 0000000..351c626 --- /dev/null +++ b/i/json/clients_Linux.json @@ -0,0 +1,5 @@ +[ + "Dino - modern, clean and GNOME integrated", + "Kaidan - modern, convergent and cross-platform", + "Gajim - полнофункциональный" +] diff --git a/i/json/clients_Windows.json b/i/json/clients_Windows.json new file mode 100644 index 0000000..422a730 --- /dev/null +++ b/i/json/clients_Windows.json @@ -0,0 +1,3 @@ +[ + "Gajim - полнофункциональный" +] diff --git a/i/json/clients_iOS.json b/i/json/clients_iOS.json new file mode 100644 index 0000000..63e5a29 --- /dev/null +++ b/i/json/clients_iOS.json @@ -0,0 +1,3 @@ +[ + "ChatSecure - Encrypted Messenger for iOS" +] diff --git a/i/lang/ru.json b/i/lang/ru.json new file mode 100644 index 0000000..bf652ad --- /dev/null +++ b/i/lang/ru.json @@ -0,0 +1,19 @@ +{ + "chat": { + "title": "Приглашение от {{name}}", + "heading": "{{name}} хочет связаться с вами", + "button": "Добавить {{name}} в список контактов", + "":"" + }, + "muc": { + "title": "Приглашение в {{name}}", + "heading": "Вы были приглашены в {{name}}", + "button": "Вступить в групповой чат {{name}}", + "":"" + }, + "clients": "Чтобы это произошло, вам нужно установить и настроить джаббер клиент, после этого посетить эту страницу снова.", + "recommend": "Мы рекомендуем один из:", + "checkfulllist": "Джаббер (XMPP) - это удобная и безопасная форма обмена сообщениями. Вы можете выбрать один из множества клиентов и иметь свободный выбор любого сервера для связи со всеми.", + "xmppis": "Основано на i.kaidan.im", + "":"" +}