(function( $, mw ) {
'use strict';
var api = new mw.Api({});
var app = {
width: 30,
styleSheet: mw.util.addCSS(`
/* VARIABLES */
:root {
--notepad-width: 30vw;
--speed: 50ms;
}
/* TRANSITIONS */
body,
#p-personal,
#right-navigation,
.mw-header,
.mw-workspace-container {
transition: all var(--speed) ease-out;
}
#notepad-window {
z-index: 101;
transition: margin var(--speed) ease-out,
width var(--speed) ease-out;
}
/* RULES FOR LEGACY VECTOR */
body.notepad.skin-vector-legacy {
margin-right: var(--notepad-width);
}
body.notepad.skin-vector-legacy #p-personal {
margin-right: var(--notepad-width);
}
body.notepad.skin-vector-legacy #right-navigation {
margin-right: var(--notepad-width);
}
body.notepad.skin-vector-legacy #simpleSearch {
width: 10em;
}
/* RULES FOR NEW VECTOR */
@media (max-width:2000px) {
body.notepad.skin-vector:not(.skin-vector-legacy) {
margin-right: var(--notepad-width);
}
body.notepad.skin-vector:not(.skin-vector-legacy) .mw-header #p-search {
min-width: inherit;
width: inherit;
}
}
@media (min-width:2000px) {
body.notepad.skin-vector:not(.skin-vector-legacy) .mw-header {
padding-right: max(calc(20px var(--notepad-width) - ((100vw - 100%) / 2)), 0px);
}
body.notepad.skin-vector:not(.skin-vector-legacy) .mw-workspace-container {
padding-right: max(calc(20px var(--notepad-width) - ((100vw - 100%) / 2)), 0px);
}
}
/* NOTEPAD STYLES */
body:not(.notepad) #notepad-window {
margin-right: calc(-1 * var(--notepad-width));
visibility: hidden;;
transition:
visibility var(--speed) ease-out,
margin var(--speed) ease-out,
width var(--speed) ease-out;
}
#notepad-icon {
position:fixed;
right:-2px;
top:40px;
transform-origin: 100% 0;
transform: rotate(90deg) translateX(100%);
border: 2px solid #a7d7f9;
border-radius: 0 0 5px 5px;
box-shadow: 0 0 4px 2px #eee;
padding: 0px 10px 1px;
background-color: #f5faff;
color: #0645ad;
font-size: 13px;
z-index:102;
transition: all var(--speed) ease-out;
}
#notepad-icon:hover {
cursor: pointer;
background-color: #a7d7f9;
}
body.notepad #notepad-icon {
right: var(--notepad-width)
}
body.notepad #notepad-icon, #notepad-icon.selected {
background-color: #a7d7f9;
font-weight: bold;
}
#notepad-window {
position:fixed;
top:0;
right:0;
bottom: 0;
width: var(--notepad-width);
border-left: 2px solid #a7d7f9;
box-shadow: 0px 0px 4px 2px #eee;
background-color: #fcfdfe;
}
#notepad-window:before {
top: -100px;
content: '';
position:absolute;
left: -30px;
width: 0;
height: 0;
border: 15px solid transparent;
border-right-color: #a7d7f9;
margin-top:-15px;
}
#notepad-window:after {
top: -100px;
content: '';
position:absolute;
left: -25px;
width: 0;
height: 0;
border: 15px solid transparent;
border-right-color: transparent;
margin-top:-15px;
}
#notepad-window #notepad-slider {
position:absolute;
top: 20px;
left: -10px;
bottom: 0;
width: 15px;
opacity: 0;
cursor: ew-resize;
}
#notepad-window #notepad-container {
height: 100%;
width: 100%;
padding: 0px 10px;
line-height: 1.6;
font-size: 0.875em;
}
#notepad-window article {
position:absolute;
top: 50px;
bottom: 0px;
width: calc(100% - 15px);
overflow: auto;
}
#notepad-window textarea:focus {
outline-color: transparent;
}
#notepad-container:not([status=saving]) #notepad-saving {
display:none;
}
#notepad-container #notepad-saving {
position:fixed;
top: 0px;
right: 0px;
font-size: 10px;
padding: 2px;
width: 40px;
text-align:center;
background-color: #fef6e7;
}
#notepad-container:not([tab=edit]) #notepad-edit,
#notepad-container:not([tab=render]) #notepad-render,
#notepad-container:not([tab=all]) #notepad-all {
visibility: hidden;
opacity: 0;
}
#notepad-container article {
transition: visibility var(--speed) ease-out,
opacity var(--speed) ease-out;
}
#notepad-container #notepad-header {
border-bottom: 1px solid #ccc;
padding: 8px 0;
margin: 0;
white-space: nowrap;
}
#notepad-container #notepad-header a {
padding: 10px 20px;
font-weight: bold;
color: black;
font-size: 12px;
border-bottom: 2px solid #0645ad00;
transition: color var(--speed) ease-out,
border var(--speed) ease-out;
}
#notepad-container[tab=edit] #notepad-link-edit,
#notepad-container[tab=render] #notepad-link-render,
#notepad-container[tab=all] #notepad-link-all,
#notepad-container #notepad-header a:hover {
color: #0645ad;
border-bottom: 2px solid #0645ad;
text-decoration: none;
}
#notepad-edit textarea {
height: 100%;
width: 100%;
background-color: transparent;
border: none;
resize: none;
}
#notepad-container #loading {
position:absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #a7d7f9;
}
#notepad-container #loading img {
width: 30px;
height: 30px;
opacity:0.5;
}
#notepad-container #notepad-all section {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
width: calc(100% - 32px);
margin:10px auto;
padding:10px;
clear: both;
overflow: auto;
}
#notepad-container #notepad-all section:hover {
box-shadow: 0 4px 8px 0 rgba(6,69,173,0.2);
outline: 2px solid #a7d7f9;
}
#notepad-container #notepad-all section h4 {
color: #0645ad;
padding: 0;
}
#notepad-container #notepad-all section > .content {
font-family: monospace;
overflow: hidden;
margin-bottom: 5px;
}
#notepad-container #notepad-all section:not([expanded]) > .content {
max-height: 90px;
}
#notepad-container #notepad-all section > .updated {
font-size: 0.8em;
color: gray;
float: left;
}
#notepad-container #notepad-all section > .links {
font-size: 0.8em;
float: right;
margin-left: 10px;
}
#notepad-container #notepad-all section > .links.confirm {
color: red;
font-weight: bold;
}
`),
init: function(oninit, onload) {
app.onload = onload;
mw.loader.using(["mediawiki.util", "mediawiki.user"]).then(function () {
app.$link = $('<div>', {'id': 'notepad-icon', 'title': 'Open notepad [ctrl alt n]'})
.append($('<span>').text('Note'))
.append($('<span>').text('pad'))
.appendTo("body");
app.$link.click(app.click);
document.addEventListener('keydown', function(e) {
if (e.altKey && e.ctrlKey && e.code == 'KeyN') {
app.$link.click(); //Ctrl Alt N
}
});
oninit();
app.load();
})
},
click: function(e) {
e.preventDefault();
app.start();
},
start: function(tab) {
app.load();
app.resize();
if (tab) {
$('#notepad-link-' tab).click();
}
$("body").toggleClass('notepad');
},
load: function() {
if (!app.loaded) {
app.loaded=true;
app.$window = $("<div>", {'id': 'notepad-window'}).appendTo("body");
app.$container = $("<div>", {'id': 'notepad-container'}).appendTo(app.$window);
app.$window.append(
$("<div>", {'id': 'notepad-slider'})
.mousedown(function(e) {
if ($('body').hasClass('notepad')) {
function mouseup() {
$(document).off("mousemove", app.mousemove);
app.resize();
}
if (e.which==1) {
mouseup();
app.mousemove = function(e) {
e.preventDefault();
var x = e.pageX;
if (x * 2 < window.innerWidth) x = (window.innerWidth/2) 1
if (window.innerWidth - x < 100) x = window.innerWidth - 99
var vw = ((1 - ((x 15) / window.innerWidth)) * 100);
app.resize(vw "vw");
}
$(document).mousemove(app.mousemove);
$(document).mouseup(mouseup);
}
} else {
app.start();
}
})
);
app.onload();
}
},
resize: function(val, suppresstrigger) {
if (val) {
$(':root').css('--notepad-width', val);
}
if (!suppresstrigger) window.dispatchEvent(new Event('resize'));
}
}
var util = {
get pagename() {
var ns = mw.config.get('wgNamespaceNumber');
var page = mw.config.get('wgPageName');
var user = mw.config.get('wgRelevantUserName');
if (ns % 2 == 1) {
var p1 = mw.config.get('wgFormattedNamespaces')[ns].replace(' ', '_');
var p0 = mw.config.get('wgFormattedNamespaces')[ns-1].replace(' ', '_');
var r = new RegExp("^" p1 ':');
page = page.replace(r, ns==1 ? p0 : p0 ':');
console.log(page);
}
if (ns == -1 && user) {
page = 'User:' user.replace(' ', '_');
}
return page;
},
get now() {
function pad(n) {
if (n<10) {
return '0' n;
} else {
return '' n;
}
}
var d = new Date()
return d.getUTCFullYear() '-' pad(d.getUTCMonth() 1) '-' pad(d.getUTCDate()) 'T'
pad(d.getUTCHours()) ':' pad(d.getUTCMinutes()) ':' pad(d.getUTCSeconds()) 'Z';
}
}
var notes = {
get: function() {
var s = mw.user.options.get("userjs-pagenotes");
if (s && s.length) {
var obj = JSON.parse(s);
return obj[util.pagename]
} else {
return "";
}
},
refresh: function() {
var deferred = new $.Deferred();
api.get({
action: 'query',
meta: 'userinfo',
uiprop: 'options'
}).done(function (response, data) {
var s = data.responseJSON.query.userinfo.options['userjs-pagenotes'];
mw.user.options.set('userjs-pagenotes', s);
deferred.resolve(s);
});
return deferred.promise();
},
save: function(content) {
var deferred = new $.Deferred();
var cur = mw.user.options.get("userjs-pagenotes");
var obj = {};
if (cur && cur.length) {
obj = JSON.parse(cur);
}
if (!obj[util.pagename] || (obj[util.pagename] && (content != obj[util.pagename].content))) {
if (content) {
obj[util.pagename] = {
'updated': util.now,
'content': content
};
} else {
delete obj[util.pagename]
}
var out = JSON.stringify(obj);
mw.user.options.set("userjs-pagenotes", out);
api.saveOption("userjs-pagenotes", out).then(function() {
deferred.resolve();
});
} else {
deferred.resolve();
}
return deferred.promise();
},
delete: function(key) {
var deferred = new $.Deferred();
var cur = mw.user.options.get("userjs-pagenotes");
var obj = {};
if (cur && cur.length) {
obj = JSON.parse(cur);
}
if (obj[key]) {
delete obj[key];
var out = JSON.stringify(obj);
mw.user.options.set("userjs-pagenotes", out);
api.saveOption("userjs-pagenotes", out).then(function() {
deferred.resolve();
});
} else {
deferred.resolve();
}
return deferred.promise();
},
init: function() {
var $link = app.$link;
if (notes.get()) {
$link.addClass('selected');
}
var param = mw.util.getParamValue('notepad');
if (param) {
app.start(param);
}
},
render: function(wikitext, $target) {
var deferred = new $.Deferred();
if (wikitext && wikitext.length) {
api.parse(wikitext, {
'contentmodel': 'wikitext',
'preview': true,
'pst': true
})
.then(function(data) {
$target.empty().append($(data));
$target.find('.mw-editsection').remove();
deferred.resolve();
})
} else {
$target.empty();
deferred.resolve();
}
return deferred.promise();
},
onload: function() {
var $container = app.$container
$container.attr('tab', 'edit');
var $topbar = $("<nav>", {'id': 'notepad-header'})
.appendTo($container)
.append($("<a>", {'id': 'notepad-link-edit'}).text("Notes")
.click(function () {
$container.attr('tab', 'edit')
}))
.append($("<a>", {'id': 'notepad-link-render'}).text("Preview")
.click(function() {
$container.attr('tab', 'render');
tabs.render.draw();
}))
.append($("<a>", {'id': 'notepad-link-all'}).text("Recent notes")
.click(function() {
$container.attr('tab', 'all');
tabs.edit.save()
.then(tabs.allnotes.draw);
}))
var tabs = {
edit: new function() {
//draw edit window
var $editwindow = $("<article>", {'id': 'notepad-edit'}).appendTo($container);
var $saving = $('<div>', {'id': 'notepad-saving'}).appendTo($editwindow);
$saving.append('Saving...');
var $textarea = $("<textarea>",
{'id': 'notepad-text', 'placeholder': 'Record a private note here...'})
.appendTo($editwindow);
this.draw = function() {
if (notes.get()) {
$textarea.val(notes.get().content);
} else {
$textarea.val('');
}
}
this.draw();
this.save = function() {
var deferred = new $.Deferred();
var val = $textarea.val();
notes.save(val).then(function() {
$container.attr('status', 'saved');
deferred.resolve();
});
return deferred.promise();
}
var edit = this;
var timer = 0;
$textarea.change(function() {
clearTimeout(timer);
$container.attr('status', 'saving');
edit.save();
});
$textarea.keyup(function() {
clearTimeout(timer);
$container.attr('status', 'saving');
timer = setTimeout(function() {
edit.save()
}, 1000);
});
},
render: new function() {
//draw render window
var $renderwindow = $("<article>", {'id': 'notepad-render'}).appendTo($container);
$('<div>', {'id': 'loading'})
.append($('<img>', {'src': "https://upload.wikimedia.org/wikipedia/commons/3/30/Chromiumthrobber.svg"}))
.appendTo($renderwindow);
this.draw = function() {
var s = "";
if (notes.get()) s = notes.get().content;
notes.render(s, $renderwindow);
}
this.draw();
},
allnotes: new function() {
//draw all notes window
var $allnotes = $("<article>", {'id': 'notepad-all'}).appendTo($container);
$('<div>', {'id': 'loading'})
.append($('<img>', {'src': "https://upload.wikimedia.org/wikipedia/commons/3/30/Chromiumthrobber.svg"}))
.appendTo($allnotes);
this.draw = function() {
$allnotes.empty();
var s = mw.user.options.get("userjs-pagenotes");
if (s && s.length) {
var list = JSON.parse(s);
var keys = Object.keys(list).sort(
function(a,b) {
if (list[a].updated > list[b].updated) return -1;
if (list[a].updated < list[b].updated) return 1;
return 0;
}
);
for (var i=0; i<keys.length; i ) {
var $sec = $('<section>', {'key': keys[i]}).appendTo($allnotes);
var $h4 = $('<h4>').appendTo($sec);
if (keys[i] === util.pagename) {
$h4.append(
$('<a>', {'href': '#'})
.text(keys[i].replace(/_/g, ' '))
.click(function (e) {
e.preventDefault();
$('#notepad-link-edit').click();
})
);
} else {
$h4.append(
$('<a>', {'href': '/wiki/' keys[i] '?notepad=notes'})
.text(keys[i].replace(/_/g,' '))
);
}
var $preview = $('<div>', {'class': 'content'}).appendTo($sec);
var content = list[keys[i]].content;
$preview.append(content);
var updated = list[keys[i]].updated.replace(/[TZ]/g,' ');
$('<div>', {'class': 'updated'}).text(updated).appendTo($sec);
$('<a>', {'class': 'links', 'href': '#'})
.text('delete')
.click(function (e) {
e.preventDefault();
var $link = $(e.target);
var $section = $link.parent();
if ($link.text() === 'delete') {
$link.text('confirm delete');
$link.addClass('confirm');
$section.focusout(function () {
$link.text('delete');
$link.removeClass('confirm');
$section.off('focusout');
});
} else {
var key = $section.attr('key');
notes.delete($section.attr('key'))
.then(function() {
if (key === util.pagename) {
tabs.edit.draw();
}
});
$section.remove();
}
})
.appendTo($sec);
if ($preview[0].scrollHeight > $preview[0].offsetHeight) {
$('<a>', {'class': 'links', 'href': '#'})
.text('expand')
.click(function (e) {
e.preventDefault();
var $section = $(e.target.parentNode);
if ($section.attr('expanded')==='') {
$(e.target).text('expand');
$section.removeAttr('expanded');
} else {
$(e.target).text('collapse');
$section.attr('expanded', '');
}
})
.appendTo($sec);
}
}
}
}
}
}
//refresh from server on focus
window.addEventListener('focus', function() {
notes.refresh().then(function () {
tabs.edit.draw();
tabs.render.draw();
tabs.allnotes.draw();
});
});
},
}
app.init(notes.init, notes.onload);
} (jQuery, mediaWiki ));