From 69671adf5bea61a0e0e279438aeb65c3078cec35 Mon Sep 17 00:00:00 2001 From: Miguel Freitas Date: Sun, 16 Mar 2014 23:08:58 -0300 Subject: [PATCH] merging language overide and post formating from calm --- home.html | 1 + js/calm.js | 171 +++++++++++ js/interface_localization.js | 32 +- js/jquery.textcomplete.js | 554 +++++++++++++++++++++++++++++++++++ js/options.js | 3 + js/twister_formatpost.js | 67 ++++- js/twister_network.js | 1 + network.html | 1 + options.html | 120 ++++++++ profile-edit.html | 1 + 10 files changed, 924 insertions(+), 27 deletions(-) create mode 100644 js/calm.js create mode 100644 js/jquery.textcomplete.js create mode 100644 js/options.js create mode 100644 options.html diff --git a/home.html b/home.html index 30f9787..9fb0ad8 100644 --- a/home.html +++ b/home.html @@ -44,6 +44,7 @@ Fulano da Silva View + Options Setup account Following users Network config diff --git a/js/calm.js b/js/calm.js new file mode 100644 index 0000000..b4545bf --- /dev/null +++ b/js/calm.js @@ -0,0 +1,171 @@ +$(function(){ + $('.dropdown-menu').on('keydown', function(e){ + e = event || window.event; + e.stopPropagation(); + }) + $('.post-text').on('click', 'a', function(e){ + e.stopPropagation(); + }); + + $('#showqr').on('click', function(){ + if($('#qrcode img')[0]) return; + var skey = document.getElementById('skey').innerText; + new QRCode(document.getElementById("qrcode"), skey); + }); + + function dhtIndicatorBg(){ + var bgcolor = ''; + if(twisterDhtNodes <= 10){bgcolor = '#770900' + }else if(twisterDhtNodes <= 20){bgcolor = '#773400' + }else if(twisterDhtNodes <= 30){bgcolor = '#774c00' + }else if(twisterDhtNodes <= 40){bgcolor = '#776400' + }else if(twisterDhtNodes <= 50){bgcolor = '#707500' + }else if(twisterDhtNodes <= 60){bgcolor = '#3f6900' + }else if(twisterDhtNodes <= 70){bgcolor = '#005f15' + }else if(twisterDhtNodes >= 71){bgcolor = '#009922' + } + $('.userMenu-dhtindicator').animate({'background-color': bgcolor }); + }; + setTimeout(dhtIndicatorBg, 300); + setTimeout(function() {setInterval(dhtIndicatorBg, 2000)}, 400); +}) + +function modalDMIntr() { + $(".cancel").on('click', function(event){ + if(!$(event.target).hasClass("cancel")) return; + if($(".modal-content").attr("style") != undefined){$(".modal-content").removeAttr("style")}; + $('.modal-back').css('display', 'none'); + }); + $('.modal-back').on('click', function(){ + if($('.modal-content .direct-messages-list')[0]) return; + directMessagesPopup(); + $(".modal-content").removeAttr("style"); + }); +}; +//sound notifications + +function soundNotifOptions() { + if(!localStorage['sndDM']) localStorage['sndDM'] = false; + if(!localStorage['sndMention']) localStorage['sndMention'] = false; + $('#notifyForm select').each(function(){ + this.value = localStorage[this.id]; + }); + + var player = $('#player'); + player[0].pause(); + $('#player').empty(); + + + $('form#notifyForm').on('change','select',function(){ + localStorage.setItem(this.id, this.value); + + if(this.value == false) {player[0].pause(); return;} + if (player[0].canPlayType('audio/mpeg;')) { + player.attr('type', 'audio/mpeg'); + player.attr('src', 'sound/'+this.value+'.mp3'); + } else { + player.attr('type', 'audio/ogg'); + player.attr('src', 'sound/'+this.value+'.ogg'); + } + + player[0].play(); + }); +} + +function volumeControl () { + var playerVol = $('#playerVol'); + playerVol[0].value = localStorage[playerVol[0].id] ? localStorage[playerVol[0].id] : 100; + $('.volValue').text((localStorage[playerVol[0].id] * 100).toFixed()); + + playerVol.on('change',function(){ + localStorage.setItem(this.id, this.value); + $('#player')[0].volume = (this.value); + $('.volValue').text((this.value * 100).toFixed()); + }); +} + +function DMsNotif() { + if(localStorage['sndDM'] == "false") return; + var player = $('#player'); + $('#player').empty(); + + if (player[0].canPlayType('audio/mpeg;')) { + player.attr('type', 'audio/mpeg'); + player.attr('src', 'sound/'+localStorage['sndDM']+'.mp3'); + } else { + player.attr('type', 'audio/ogg'); + player.attr('src', 'sound/'+localStorage['sndDM']+'.ogg'); + } + player[0].volume = localStorage['playerVol']; + player[0].play(); +} + +function mensNotif() { + if(localStorage['sndMention'] == "false") return; + var player = $('#player'); + $('#playerSec').empty(); + + if (player[0].canPlayType('audio/mpeg;')) { + player.attr('type', 'audio/mpeg'); + player.attr('src', 'sound/'+localStorage['sndMention']+'.mp3'); + } else { + player.attr('type', 'audio/ogg'); + player.attr('src', 'sound/'+localStorage['sndMention']+'.ogg'); + } + player[0].volume = localStorage['playerVol']; + player[0].play(); +}; + +function keysSend() { + if(!localStorage['keysSend']) localStorage['keysSend'] = 1; + $('#keysOpt select')[0].value = localStorage['keysSend']; + + $('#keysOpt select').on('change', function(){ + localStorage[this.id] = this.value; + + }) +} + +function mensAutocomplete() { + var storFollArr = JSON.parse(localStorage[localStorage.defaultScreenName]), suggests = []; + + for(var i = 0; i < storFollArr.followingUsers.length; i++){ + if(storFollArr.followingUsers[i] == localStorage.defaultScreenName) continue; + suggests.push(storFollArr.followingUsers[i]); + } + suggests.reverse(); + $('textarea').textcomplete([ + { // html + mentions: suggests, + match: /\B@(\w*)$/, + search: function (term, callback) { + callback($.map(this.mentions, function (mention) { + return mention.indexOf(term) === 0 ? mention : null; + })); + }, + index: 1, + replace: function (mention) { + return '@' + mention + ' '; + } + } +]) +} + +function setLang() { + + $('#language').val(localStorage['locLang'] || 'auto') + $('#language').on('change', function(){ + localStorage['locLang'] = $(this).val(); + }) +} + +function InitOptions () { + soundNotifOptions(); + volumeControl(); + keysSend(); + setLang(); +} +function homeIntInit () { + modalDMIntr (); + setTimeout(mensAutocomplete, 800); +} \ No newline at end of file diff --git a/js/interface_localization.js b/js/interface_localization.js index 881c128..6286786 100644 --- a/js/interface_localization.js +++ b/js/interface_localization.js @@ -6,21 +6,25 @@ // translators: add your language code here such as "es" for Spanish, "ru" for Russian var knownLanguages = ["en","es","nl","it","fr","ru","de","zh","ja","pt-BR"]; -// detect language with JavaScript -var preferredLanguage = window.navigator.userLanguage || window.navigator.language || "en"; -if(knownLanguages.indexOf(preferredLanguage) > -1){ - // en for en or similar - preferredLanguage = preferredLanguage; -} -else if(knownLanguages.indexOf(preferredLanguage.split("-")[0]) > -1){ - // en for en-US or similar - preferredLanguage = preferredLanguage.split("-")[0]; -} -else{ - // did not find match - preferredLanguage = "en"; +if(!localStorage['locLang'] || localStorage['locLang'] == 'auto'){ + if(!localStorage['locLang']) localStorage['locLang'] = 'auto'; + // detect language with JavaScript + preferredLanguage = window.navigator.userLanguage || window.navigator.language || "en"; + if(knownLanguages.indexOf(preferredLanguage) > -1){ + // en for en or similar + preferredLanguage = preferredLanguage; + } + else if(knownLanguages.indexOf(preferredLanguage.split("-")[0]) > -1){ + // en for en-US or similar + preferredLanguage = preferredLanguage.split("-")[0]; + } + else{ + // did not find match + preferredLanguage = "en"; + } +}else{ + preferredLanguage = localStorage['locLang']; } - // set up Polyglot polyglot = new Polyglot(); var wordset = {}; diff --git a/js/jquery.textcomplete.js b/js/jquery.textcomplete.js new file mode 100644 index 0000000..66bed0f --- /dev/null +++ b/js/jquery.textcomplete.js @@ -0,0 +1,554 @@ +/*! + * jQuery.textcomplete.js + * + * Repositiory: https://github.com/yuku-t/jquery-textcomplete + * License: MIT + * Author: Yuku Takahashi + */ + +;(function ($) { + + 'use strict'; + + /** + * Exclusive execution control utility. + */ + var lock = function (func) { + var free, locked; + free = function () { locked = false; }; + return function () { + var args; + if (locked) return; + locked = true; + args = toArray(arguments); + args.unshift(free); + func.apply(this, args); + }; + }; + + /** + * Convert arguments into a real array. + */ + var toArray = function (args) { + var result; + result = Array.prototype.slice.call(args); + return result; + }; + + /** + * Get the styles of any element from property names. + */ + var getStyles = (function () { + var color; + color = $('
').css(['color']).color; + if (typeof color !== 'undefined') { + return function ($el, properties) { + return $el.css(properties); + }; + } else { // for jQuery 1.8 or below + return function ($el, properties) { + var styles; + styles = {}; + $.each(properties, function (i, property) { + styles[property] = $el.css(property); + }); + return styles; + }; + } + })(); + + /** + * Default template function. + */ + var identity = function (obj) { return obj; }; + + /** + * Memoize a search function. + */ + var memoize = function (func) { + var memo = {}; + return function (term, callback) { + if (memo[term]) { + callback(memo[term]); + } else { + func.call(this, term, function (data) { + memo[term] = (memo[term] || []).concat(data); + callback.apply(null, arguments); + }); + } + }; + }; + + /** + * Determine if the array contains a given value. + */ + var include = function (array, value) { + var i, l; + if (array.indexOf) return array.indexOf(value) != -1; + for (i = 0, l = array.length; i < l; i++) { + if (array[i] === value) return true; + } + return false; + }; + + /** + * Textarea manager class. + */ + var Completer = (function () { + var html, css, $baseWrapper, $baseList, _id; + + html = { + wrapper: '
', + list: '' + }; + css = { + wrapper: { + position: 'relative' + }, + list: { + position: 'absolute', + top: 0, + left: 0, + zIndex: '100', + display: 'none' + } + }; + $baseWrapper = $(html.wrapper).css(css.wrapper); + $baseList = $(html.list).css(css.list); + _id = 0; + + function Completer($el) { + var focus; + this.el = $el.get(0); // textarea element + focus = this.el === document.activeElement; + // Cannot wrap $el at initialize method lazily due to Firefox's behavior. + this.$el = wrapElement($el); // Focus is lost + this.id = 'textComplete' + _id++; + this.strategies = []; + if (focus) { + this.initialize(); + this.$el.focus(); + } else { + this.$el.one('focus.textComplete', $.proxy(this.initialize, this)); + } + } + + /** + * Completer's public methods + */ + $.extend(Completer.prototype, { + + /** + * Prepare ListView and bind events. + */ + initialize: function () { + var $list, globalEvents; + $list = $baseList.clone(); + this.listView = new ListView($list, this); + this.$el + .before($list) + .on({ + 'keyup.textComplete': $.proxy(this.onKeyup, this), + 'keydown.textComplete': $.proxy(this.listView.onKeydown, + this.listView) + }); + globalEvents = {}; + globalEvents['click.' + this.id] = $.proxy(this.onClickDocument, this); + globalEvents['keyup.' + this.id] = $.proxy(this.onKeyupDocument, this); + $(document).on(globalEvents); + }, + + /** + * Register strategies to the completer. + */ + register: function (strategies) { + this.strategies = this.strategies.concat(strategies); + }, + + /** + * Show autocomplete list next to the caret. + */ + renderList: function (data) { + if (this.clearAtNext) { + this.listView.clear(); + this.clearAtNext = false; + } + if (data.length) { + if (!this.listView.shown) { + this.listView + .setPosition(this.getCaretPosition()) + .clear() + .activate(); + this.listView.strategy = this.strategy; + } + data = data.slice(0, this.strategy.maxCount); + this.listView.render(data); + } + + if (!this.listView.data.length && this.listView.shown) { + this.listView.deactivate(); + } + }, + + searchCallbackFactory: function (free) { + var self = this; + return function (data, keep) { + self.renderList(data); + if (!keep) { + // This is the last callback for this search. + free(); + self.clearAtNext = true; + } + }; + }, + + /** + * Keyup event handler. + */ + onKeyup: function (e) { + var searchQuery, term; + if (this.skipSearch(e)) { return; } + + searchQuery = this.extractSearchQuery(this.getTextFromHeadToCaret()); + if (searchQuery.length) { + term = searchQuery[1]; + if (this.term === term) return; // Ignore shift-key or something. + this.term = term; + this.search(searchQuery); + } else { + this.term = null; + this.listView.deactivate(); + } + }, + + /** + * Suppress searching if it returns true. + */ + skipSearch: function (e) { + if (this.skipNextKeyup) { + this.skipNextKeyup = false; + return true; + } + switch (e.keyCode) { + case 40: + case 38: + return true; + } + }, + + onSelect: function (value) { + var pre, post, newSubStr; + pre = this.getTextFromHeadToCaret(); + post = this.el.value.substring(this.el.selectionEnd); + + newSubStr = this.strategy.replace(value); + if ($.isArray(newSubStr)) { + post = newSubStr[1] + post; + newSubStr = newSubStr[0]; + } + pre = pre.replace(this.strategy.match, newSubStr); + this.$el.val(pre + post) + .trigger('change') + .trigger('textComplete:select', value); + this.el.focus(); + this.el.selectionStart = this.el.selectionEnd = pre.length; + this.skipNextKeyup = true; + }, + + /** + * Global click event handler. + */ + onClickDocument: function (e) { + if (e.originalEvent && !e.originalEvent.keepTextCompleteDropdown) { + this.listView.deactivate(); + } + }, + + /** + * Global keyup event handler. + */ + onKeyupDocument: function (e) { + if (this.listView.shown && e.keyCode === 27) { // ESC + this.listView.deactivate(); + this.$el.focus(); + } + }, + + /** + * Remove all event handlers and the wrapper element. + */ + destroy: function () { + var $wrapper; + this.$el.off('.textComplete'); + $(document).off('.' + this.id); + if (this.listView) { this.listView.destroy(); } + $wrapper = this.$el.parent(); + $wrapper.after(this.$el).remove(); + this.$el.data('textComplete', void 0); + this.$el = null; + }, + + // Helper methods + // ============== + + /** + * Returns caret's relative coordinates from textarea's left top corner. + */ + getCaretPosition: function () { + // Browser native API does not provide the way to know the position of + // caret in pixels, so that here we use a kind of hack to accomplish + // the aim. First of all it puts a div element and completely copies + // the textarea's style to the element, then it inserts the text and a + // span element into the textarea. + // Consequently, the span element's position is the thing what we want. + + if (this.el.selectionEnd === 0) return; + var properties, css, $div, $span, position, dir; + + dir = this.$el.attr('dir') || this.$el.css('direction'); + properties = ['border-width', 'font-family', 'font-size', 'font-style', + 'font-variant', 'font-weight', 'height', 'letter-spacing', + 'word-spacing', 'line-height', 'text-decoration', 'text-align', + 'width', 'padding-top', 'padding-right', 'padding-bottom', + 'padding-left', 'margin-top', 'margin-right', 'margin-bottom', + 'margin-left' + ]; + css = $.extend({ + position: 'absolute', + overflow: 'auto', + 'white-space': 'pre-wrap', + top: 0, + left: -9999, + direction: dir + }, getStyles(this.$el, properties)); + + $div = $('
').css(css).text(this.getTextFromHeadToCaret()); + $span = $('').text('.').appendTo($div); + this.$el.before($div); + position = $span.position(); + position.top += $span.height() - this.$el.scrollTop(); + if (dir === 'rtl') { position.left -= this.listView.$el.width(); } + $div.remove(); + return position; + }, + + getTextFromHeadToCaret: function () { + var text, selectionEnd, range; + selectionEnd = this.el.selectionEnd; + if (typeof selectionEnd === 'number') { + text = this.el.value.substring(0, selectionEnd); + } else if (document.selection) { + range = this.el.createTextRange(); + range.moveStart('character', 0); + range.moveEnd('textedit'); + text = range.text; + } + return text; + }, + + /** + * Parse the value of textarea and extract search query. + */ + extractSearchQuery: function (text) { + // If a search query found, it returns used strategy and the query + // term. If the caret is currently in a code block or search query does + // not found, it returns an empty array. + + var i, l, strategy, match; + for (i = 0, l = this.strategies.length; i < l; i++) { + strategy = this.strategies[i]; + match = text.match(strategy.match); + if (match) { return [strategy, match[strategy.index]]; } + } + return []; + }, + + search: lock(function (free, searchQuery) { + var term; + this.strategy = searchQuery[0]; + term = searchQuery[1]; + this.strategy.search(term, this.searchCallbackFactory(free)); + }) + }); + + /** + * Completer's private functions + */ + var wrapElement = function ($el) { + return $el.wrap($baseWrapper.clone().css('display', $el.css('display'))); + }; + + return Completer; + })(); + + /** + * Dropdown menu manager class. + */ + var ListView = (function () { + + function ListView($el, completer) { + this.data = []; + this.$el = $el; + this.index = 0; + this.completer = completer; + + this.$el.on('click.textComplete', 'li.textcomplete-item', + $.proxy(this.onClick, this)); + } + + $.extend(ListView.prototype, { + shown: false, + + render: function (data) { + var html, i, l, index, val; + + html = ''; + for (i = 0, l = data.length; i < l; i++) { + val = data[i]; + if (include(this.data, val)) continue; + index = this.data.length; + this.data.push(val); + html += '
  • '; + html += this.strategy.template(val); + html += '
  • '; + if (this.data.length === this.strategy.maxCount) break; + } + this.$el.append(html); + if (!this.data.length) { + this.deactivate(); + } else { + this.activateIndexedItem(); + } + }, + + clear: function () { + this.data = []; + this.$el.html(''); + this.index = 0; + return this; + }, + + activateIndexedItem: function () { + this.$el.find('.active').removeClass('active'); + this.getActiveItem().addClass('active'); + }, + + getActiveItem: function () { + return $(this.$el.children().get(this.index)); + }, + + activate: function () { + if (!this.shown) { + this.$el.show(); + this.completer.$el.trigger('textComplete:show'); + this.shown = true; + } + return this; + }, + + deactivate: function () { + if (this.shown) { + this.$el.hide(); + this.completer.$el.trigger('textComplete:hide'); + this.shown = false; + this.data = this.index = null; + } + return this; + }, + + setPosition: function (position) { + this.$el.css(position); + return this; + }, + + select: function (index) { + var self = this; + this.completer.onSelect(this.data[index]); + // Deactive at next tick to allow other event handlers to know whether + // the dropdown has been shown or not. + setTimeout(function () { self.deactivate(); }, 0); + }, + + onKeydown: function (e) { + if (!this.shown) return; + if (e.keyCode === 38) { // UP + e.preventDefault(); + if (this.index === 0) { + this.index = this.data.length-1; + } else { + this.index -= 1; + } + this.activateIndexedItem(); + } else if (e.keyCode === 40) { // DOWN + e.preventDefault(); + if (this.index === this.data.length - 1) { + this.index = 0; + } else { + this.index += 1; + } + this.activateIndexedItem(); + } else if (e.keyCode === 13 || e.keyCode === 9) { // ENTER or TAB + e.preventDefault(); + + this.select(parseInt(this.getActiveItem().data('index'), 10)); + } + }, + + onClick: function (e) { + var $e = $(e.target); + e.originalEvent.keepTextCompleteDropdown = true; + if (!$e.hasClass('textcomplete-item')) { + $e = $e.parents('li.textcomplete-item'); + } + this.select(parseInt($e.data('index'), 10)); + }, + + destroy: function () { + this.deactivate(); + this.$el.off('click.textComplete').remove(); + this.$el = null; + } + }); + + return ListView; + })(); + + $.fn.textcomplete = function (strategies) { + var i, l, strategy, dataKey; + + dataKey = 'textComplete'; + + if (strategies === 'destroy') { + return this.each(function () { + var completer = $(this).data(dataKey); + if (completer) { completer.destroy(); } + }); + } + + for (i = 0, l = strategies.length; i < l; i++) { + strategy = strategies[i]; + if (!strategy.template) { + strategy.template = identity; + } + if (strategy.index == null) { + strategy.index = 2; + } + if (strategy.cache) { + strategy.search = memoize(strategy.search); + } + strategy.maxCount || (strategy.maxCount = 10); + } + + return this.each(function () { + var $this, completer; + $this = $(this); + completer = $this.data(dataKey); + if (!completer) { + completer = new Completer($this); + $this.data(dataKey, completer); + } + completer.register(strategies); + }); + }; + +})(window.jQuery || window.Zepto); diff --git a/js/options.js b/js/options.js new file mode 100644 index 0000000..d9286d5 --- /dev/null +++ b/js/options.js @@ -0,0 +1,3 @@ +$(function() { + +}); diff --git a/js/twister_formatpost.js b/js/twister_formatpost.js index 8c7f413..f7c63ce 100644 --- a/js/twister_formatpost.js +++ b/js/twister_formatpost.js @@ -98,7 +98,7 @@ function postToElem( post, kind ) { elem.find(".post-context").show(); var retweetedByElem = elem.find(".post-retransmited-by"); retweetedByElem.attr("href", $.MAL.userUrl(retweeted_by)); - retweetedByElem.text(retweeted_by); + retweetedByElem.text('@'+retweeted_by); } return elem; @@ -143,8 +143,11 @@ function htmlFormatMsg( msg, output, mentions ) { var tmp; var match = null; var index; - var reAll = new RegExp("(#|@|http[s]?://)"); - var reHttp = new RegExp("http[s]?://"); + var strUrlRegexp = "http[s]?://"; + var strEmailRegexp = "\\S+@\\S+\\.\\S+"; + var reAll = new RegExp("(?:^|[ \\n\\t.,:\\/?!])(#|@|" + strUrlRegexp + "|" + strEmailRegexp + ")"); + var reHttp = new RegExp(strUrlRegexp); + var reEmail = new RegExp(strEmailRegexp); msg = escapeHtmlEntities(msg); @@ -152,9 +155,10 @@ function htmlFormatMsg( msg, output, mentions ) { match = reAll.exec(msg); if( match ) { - if( match[0] == "@" ) { - output.append(msg.substr(0, match.index)); - tmp = msg.substr(match.index+1); + index = (match[0] === match[1]) ? match.index : match.index + 1; + if( match[1] == "@" ) { + output.append(msg.substr(0, index)); + tmp = msg.substr(index+1); var username = _extractUsername(tmp); if( username.length ) { if( mentions.indexOf(username) < 0 ) @@ -167,11 +171,14 @@ function htmlFormatMsg( msg, output, mentions ) { msg = tmp.substr(String(username).length); continue; } + output.append('@'); + msg = tmp; + continue; } - if( reHttp.exec(match[0]) ) { - output.append(msg.substr(0, match.index)); - tmp = msg.substring(match.index); + if( reHttp.exec(match[1]) ) { + output.append(msg.substr(0, index)); + tmp = msg.substring(index); var space = tmp.indexOf(" "); var url; if( space != -1 ) url = tmp.substring(0,space); else url = tmp; @@ -186,11 +193,29 @@ function htmlFormatMsg( msg, output, mentions ) { continue; } } + + if( reEmail.exec(match[1]) ) { + output.append(msg.substr(0, index)); + tmp = msg.substring(index); + var space = tmp.indexOf(" "); + var email; + if( space != -1 ) email = tmp.substring(0,space); else email = tmp; + if( email.length ) { + var extLinkTemplate = $("#external-page-link-template").clone(true); + extLinkTemplate.removeAttr("id"); + extLinkTemplate.attr("href","mailto:" + email); + extLinkTemplate.text(email); + extLinkTemplate.attr("title",email); + output.append(extLinkTemplate); + msg = tmp.substr(String(email).length); + continue; + } + } - if( match[0] == "#" ) { - output.append(msg.substr(0, match.index)); - tmp = msg.substr(match.index+1); - var hashtag = _extractUsername(tmp); + if( match[1] == "#" ) { + output.append(msg.substr(0, index)); + tmp = msg.substr(index+1); + var hashtag = _extractHashtag(tmp); if( hashtag.length ) { var hashtagLinkTemplate = $("#hashtag-link-template").clone(true); hashtagLinkTemplate.removeAttr("id"); @@ -200,6 +225,9 @@ function htmlFormatMsg( msg, output, mentions ) { msg = tmp.substr(String(hashtag).length); continue; } + output.append('#'); + msg = tmp; + continue; } } @@ -225,6 +253,19 @@ function _extractUsername(s) { return username.toLowerCase(); } +// internal function for htmlFormatMsg +function _extractHashtag(s) { + var hashtag = ""; + for( var i = 0; i < s.length; i++ ) { + if( " \n\t.,:/?!".indexOf(s[i]) < 0 ) { + hashtag += s[i]; + } else { + break; + } + } + return hashtag; +} + function escapeHtmlEntities(str) { return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); } diff --git a/js/twister_network.js b/js/twister_network.js index e110987..0bb717e 100644 --- a/js/twister_network.js +++ b/js/twister_network.js @@ -30,6 +30,7 @@ function requestNetInfo(cbFunc, cbArg) { $(".known-peers").text(twisterdAddrman); $(".blocks").text(twisterdBlocks); $(".dht-nodes").text(twisterDhtNodes); + $(".userMenu-dhtindicator a").text(twisterDhtNodes); $(".version").text(twisterDisplayVersion); if( !twisterdConnections ) { diff --git a/network.html b/network.html index 6843b7a..9c86c51 100644 --- a/network.html +++ b/network.html @@ -46,6 +46,7 @@ Fulano da Silva View + Options Setup account Following users Network config diff --git a/options.html b/options.html new file mode 100644 index 0000000..e4860d1 --- /dev/null +++ b/options.html @@ -0,0 +1,120 @@ + + + + + twister login + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    +

    Use language

    +
    +
    + +
    +
    +
    +
    + +
    + +
    +

    Sound notifications

    +
    +
    +

    Mentions

    +
    +

    Direct Messages

    + + 0 +
    + +
    +
    +
    + +
    +
    +

    Keys

    +
    +
    +

    Send key

    + +
    +
    +
    +
    +
    + + + diff --git a/profile-edit.html b/profile-edit.html index 135fc97..a8d4275 100644 --- a/profile-edit.html +++ b/profile-edit.html @@ -46,6 +46,7 @@ Fulano da Silva View + Options Setup account Following users Network config