MediaWiki:Wikimarks.js: Difference between revisions

From Coral Island Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(2 intermediate revisions by the same user not shown)
Line 1: Line 1:
(function ($, mw, dev) {
(function ($, mw, dev) {
    'use strict';
'use strict';


    var conf = mw.config.get([
var conf = mw.config.get([
    'skin',
'wgPageName',
            'stylepath',
'wgScriptPath',
            'wgPageName',
'wgServer',
            'wgScriptPath',
'wgUserName'
            'wgServer',
]);
            'wgUserName'
        ]),


        testUser = false;
/**
* Insert Wikimarks into the DOM and attach the relevant events
*/
function addHtml($menu) {
var $wikimarks = $('#mw-navigation > .collapsible-nav');


    /**
$wikimarks.append($menu);
    * Insert Wikimarks into the DOM and attach the relevant events
    */
    function addHtml($menu) {
        var $wikimarks = $('#mw-navigation > .collapsible-nav');


        $wikimarks.append($menu);
// everything is now done
// so fire an event so people can interact/extend it further
mw.hook('wikimarks.loaded').fire($wikimarks);
}


        // everything is now done
/**
        // so fire an event so people can interact/extend it further
* Prepare the parsed HTML and attach to the DOM
        mw.hook('wikimarks.loaded').fire($wikimarks);
*/
    }
function prepareHtml(html) {
var $parsed = $(html);


    /**
// remove the parser output wrapping element
    * Prepare the parsed HTML and attach to the DOM
if ($parsed.hasClass('mw-parser-output')) {
    */
$parsed = $parsed.children();
    function prepareHtml(html) {
}
        var $parsed = $(html);


        // remove the parser output wrapping element
var $menu = $('<nav id="p-Wikimarks" role="navigation" aria-labelledby="p-Wikimarks-label"></nav>')
        if ($parsed.hasClass('mw-parser-output')) {
.addClass('vector-menu mw-portlet mw-portlet-Wikimarks vector-menu-portal portal expanded')
            $parsed = $parsed.children();
.append(
        }
$('<h3></h3>', {id:'p-Wikimarks-label', 'class':'vector-menu-heading', tabindex: '0'})
.append('<a href="/wiki/User:'+config.wgUserName+'/Wikimarks" aria-haspopup="true" aria-controls="p-Wikimarks-list" role="button" aria-pressed="true" aria-expanded="true"><span class="vector-menu-heading-label">Wikimarks</span></a>'),
$('<div></div>').addClass('vector-menu-content').append($parsed)
);


        var $menu = $('<nav id="p-Wikimarks" role="navigation" aria-labelledby="p-Wikimarks-label"></nav>')
// add classes to elements
                .addClass('vector-menu mw-portlet mw-portlet-Wikimarks vector-menu-portal portal expanded')
$menu
                .append(
.children('div.vector-menu-content')
                $('<h3></h3>', {id:'p-Wikimarks-label', 'class':'vector-menu-heading', tabindex: '0'})
.children('ul')
                .append('<a href="#" aria-haspopup="true" aria-controls="p-Wikimarks-list" role="button" aria-pressed="true" aria-expanded="true"><span class="vector-menu-heading-label">Wikimarks</span></a>'),
.addClass('vector-menu-content-list')
                $('<div></div>').addClass('vector-menu-content').append($parsed)
.attr('id', 'p-Wikimarks-list')
            );
.children('li').addClass('mw-list-item')
.children('a')
.siblings('ul')
.remove(); // disallow nesting


        // add classes to elements
// remove href from text converted to links
        $menu
$menu.find('a[href="' + conf.wgScriptPath + '/wiki/"]')
            .children('div.vector-menu-content')
.removeAttr('href')
            .children('ul')
.css('cursor', 'pointer');
                .addClass('vector-menu-content-list')
                .attr('id', 'p-Wikimarks-list')
                .children('li')
                    .children('a')
                        .siblings('ul')
                        .remove(); // disallow nesting


        // remove href from text converted to links
        $menu.find('a[href="' + conf.wgScriptPath + '/wiki/"]')
            .removeAttr('href')
            .css('cursor', 'pointer');


$menu.find('a')
// titles don't add anything to the links
.removeAttr('title')
// remove external link class for ease of reading the source html
.removeClass('extiw');


        $menu.find('a')
addHtml($menu);
            // titles don't add anything to the links
}
            .removeAttr('title')
            // remove external link class for ease of reading the source html
            .removeClass('extiw');


        if (testUser) {
/**
            return;
* Pass the preprocess wikimarks to action=parse to be converted into wikitext
        }
*/
function parseWikimarks(data) {
var params = {
action: 'parse',
contentmodel: 'wikitext',
prop: 'text',
text: data
};


        addHtml($menu);
(new mw.Api())
    }
.post(params)
.done(function (data) {
var text = data.parse.text['*'];


    /**
// remove preprocessor comment
    * Pass the preprocess wikimarks to action=parse to be converted into wikitext
// should be able to hide it in api config
    */
// but that's broken in mw1.19
    function parseWikimarks(data) {
text = text.replace(/<!--[\s\S]*?-->/g, '').trim();
        var params = {
            action: 'parse',
            contentmodel: 'wikitext',
            prop: 'text',
            text: data
        };


        (new mw.Api())
prepareHtml(text);
            .post(params)
});
            .done(function (data) {
}
                var text = data.parse.text['*'];


                // remove preprocessor comment
/**
                // should be able to hide it in api config
* Preprocesses a wikimarks page to make it compatible with the wikitext parser
                // but that's broken in mw1.19
*/
                text = text.replace(/<!--[\s\S]*?-->/g, '').trim();
function preprocessData(data) {
data = data.trim().split(/\n+/);


                if (testUser) {
var invalidLink = false,
                    mw.log(text);
parsed = [],
                }
// handles:
// - /wiki/ (wiki pages)
// - index.php, api.php, and wikia.php (API)
// - /f and /d (discussions)
relativeUrlRe = /\/(wiki\/|(?:index|api|wikia)\.php|f|d)/;


                prepareHtml(text);
data.forEach(function (elem) {
            });
// ignore comments
    }
if (elem.indexOf('//') === 0 || elem.indexOf('#') === 0) {
return;
}


    /**
// handle external links
    * Preprocesses a wikimarks page to make it compatible with the wikitext parser
elem = elem.replace(/^(\*+)\s*\[([^\s]+)\s+(.+?)\]\s*$/, function (_, p1, p2, p3) {
    */
// handle query strings
    function preprocessData(data) {
if (p2.indexOf('?') === 0) {
        data = data.trim().split(/\n+/);
return p1 + '[{{fullurl:' + conf.wgPageName + '|' + p2.slice(1) + '}} ' + p3 + ']';
}


        var invalidLink = false,
// allow appending to existing query strings as well
            parsed = [],
if (p2.indexOf('&') === 0) {
            // handles:
return p1 + '[' + location.href + p2 + ' ' + p3 + ']';
            // - /wiki/ (wiki pages)
}
            // - index.php, api.php, and wikia.php (API)
            // - /f and /d (discussions)
            relativeUrlRe = /\/(wiki\/|(?:index|api|wikia)\.php|f|d)/;


        data.forEach(function (elem) {
// handle relative URLs
            // ignore comments
if (p2.search(relativeUrlRe) === 0) {
            if (elem.indexOf('//') === 0 || elem.indexOf('#') === 0) {
p2 = conf.wgServer + conf.wgScriptPath + p2;
                return;
}
            }


            // handle external links
// else just return it unchanged
            elem = elem.replace(/^(\*+)\s*\[([^\s]+)\s+(.+?)\]\s*$/, function (_, p1, p2, p3) {
return p1 + ' [' + p2 + ' ' + p3 + ']';
                // handle query strings
});
                if (p2.indexOf('?') === 0) {
                    return p1 + '[{{fullurl:' + conf.wgPageName + '|' + p2.slice(1) + '}} ' + p3 + ']';
                }


                // allow appending to existing query strings as well
// don't touch raw html
                if (p2.indexOf('&') === 0) {
// assumes that all html will begin with a tag, e.g. <span...
                    return p1 + '[' + location.href + p2 + ' ' + p3 + ']';
if (!/^\*+\s*</.test(elem)) {
                }
// parse old style links to wikitext for backwards compatibility
elem = elem.replace(/^(\*+)\s*([^\[]+?)\s*=\s*(.+?)\s*$/, function (_, p1, p2, p3) {
// handle absolute URLs
// 'http://' or 'https://' or '//'
if (p3.search(/(?:https?:)?\/\//) === 0) {
return p1 + ' [' + p3 + ' ' + p2 + ']';
}


                // handle relative URLs
// handle query strings
                if (p2.search(relativeUrlRe) === 0) {
if (p3.indexOf('?') === 0) {
                    p2 = conf.wgServer + conf.wgScriptPath + p2;
return p1 + '[{{fullurl:' + conf.wgPageName + '|' + p3.slice(1) + '}} ' + p2 + ']';
                }
}


                // else just return it unchanged
// allow appending to existing query strings as well
                return p1 + ' [' + p2 + ' ' + p3 + ']';
if (p2.indexOf('&') === 0) {
            });
return p1 + '[' + location.href + p2 + ' ' + p3 + ']';
}


            // don't touch raw html
// attempt to fix instances of Foo?bar=baz
            // assumes that all html will begin with a tag, e.g. <span...
// domain added below
            if (!/^\*+\s*</.test(elem)) {
if (p3.indexOf('?') > -1) {
                // parse old style links to wikitext for backwards compatibility
p3 = '/wiki/' + p3;
                elem = elem.replace(/^(\*+)\s*([^\[]+?)\s*=\s*(.+?)\s*$/, function (_, p1, p2, p3) {
}
                    // handle absolute URLs
                    // 'http://' or 'https://' or '//'
                    if (p3.search(/(?:https?:)?\/\//) === 0) {
                        return p1 + ' [' + p3 + ' ' + p2 + ']';
                    }


                    // handle query strings
// handle relative URLs
                    if (p3.indexOf('?') === 0) {
if (p3.search(relativeUrlRe) === 0) {
                        return p1 + '[{{fullurl:' + conf.wgPageName + '|' + p3.slice(1) + '}} ' + p2 + ']';
p3 = conf.wgServer + conf.wgScriptPath + p3;
                    }
return p1 + ' [' + p3 + ' ' + p2 + ']';
}


                    // allow appending to existing query strings as well
// ## BREAKING CHANGE ##
                    if (p2.indexOf('&') === 0) {
// don't allow 'javascript:' urls
                        return p1 + '[' + location.href + p2 + ' ' + p3 + ']';
// ridiculously difficult to parse these in js without using `eval`
                    }
if (p3.search(/(?:javascript:)?(?:url|win)\(/) === 0) {
p3 = '#invalidLink';
invalidLink = true;
}


                    // attempt to fix instances of Foo?bar=baz
// else we expect a normal wikilink
                    // domain added below
return p1 + ' [[' + p3 + '|' + p2 + ']]';
                    if (p3.indexOf('?') > -1) {
});
                        p3 = '/wiki/' + p3;
}
                    }


                    // handle relative URLs
// remove css comment
                    if (p3.search(relativeUrlRe) === 0) {
// caused by loading wikimarks config through RL and pretending it's CSS
                        p3 = conf.wgServer + conf.wgScriptPath + p3;
if (elem.search(/^\/\*.+?\*\/$/) === 0) {
                        return p1 + ' [' + p3 + ' ' + p2 + ']';
elem = '';
                    }
}


                    // ## BREAKING CHANGE ##
// substitute in global variables
                    // don't allow 'javascript:' urls
// syntax: {$VAR} where VAR is a global variable
                    // ridiculously difficult to parse these in js without using `eval`
// @todo limit to stuff available in mw.config?
                    if (p3.search(/(?:javascript:)?(?:url|win)\(/) === 0) {
elem = elem.replace(/\{\$(.+?)\}/g, function (_, p1) {
                        p3 = '#invalidLink';
// fix for properties of globals
                        invalidLink = true;
var parts = p1.split('.'),
                    }
test = window,
prop,
i;


                    // else we expect a normal wikilink
for (i = 0; i < parts.length; i += 1) {
                    return p1 + ' [[' + p3 + '|' + p2 + ']]';
prop = parts[i];
                });
            }


            // remove css comment
// @todo how secure is this?
            // caused by loading wikimarks config through RL and pretending it's CSS
if (test.hasOwnProperty(prop)) {
            if (elem.search(/^\/\*.+?\*\/$/) === 0) {
test = test[prop];
                elem = '';
} else {
            }
break;
}
}


            // substitute in global variables
if (['string', 'number'].indexOf(typeof test) > -1) {
            // syntax: {$VAR} where VAR is a global variable
return test;
            // @todo limit to stuff available in mw.config?
} else {
            elem = elem.replace(/\{\$(.+?)\}/g, function (_, p1) {
return mw.config.get(p1);
                // fix for properties of globals
}
                var parts = p1.split('.'),
});
                    test = window,
                    prop,
                    i;


                for (i = 0; i < parts.length; i += 1) {
// make simple text strings into a null link so it doesn't break the styling
                    prop = parts[i];
elem = elem.replace(/^(\*+)\s*([A-Za-z0-9\s]+)\s*$/, '$1 [[#|$2]]');


                    // @todo how secure is this?
parsed.push(elem);
                    if (test.hasOwnProperty(prop)) {
});
                        test = test[prop];
                    } else {
                        break;
                    }
                }


                if (['string', 'number'].indexOf(typeof test) > -1) {
data = parsed.join('\n').trim();
                    return test;
mw.log(data);
                } else {
                return mw.config.get(p1);
                }
            });


            // make simple text strings into a null link so it doesn't break the styling
if (invalidLink) {
            elem = elem.replace(/^(\*+)\s*([A-Za-z0-9\s]+)\s*$/, '$1 [[#|$2]]');
// @todo do something
}


            parsed.push(elem);
return data;
        });
}


        data = parsed.join('\n').trim();
/**
        mw.log(data);
* Load the users wikimarks
*/
function loadWikimarks(username) {
var load = 'https://coralisland.wiki/w/api.php',
params = {
action: 'query',
format: 'json',
prop: 'revisions',
rvprop: 'content',
// don't encode anything in the username here, $.ajax does it anyway
// otherwise stuff gets encoded twice and no results are returned
titles: 'User:' + (username || conf.wgUserName).replace(/ /g, '_') + '/Wikimarks',
indexpageids: 1,
origin: '*',
// Cache results for 5 minutes in CDN and browser
maxage: 300,
smaxage: 300
};


        if (invalidLink) {
            // @todo do something
        }


        return data;
$.ajax(load, {
    }
data: params
}).always(function (data) {
console.log(data, 'ajax data');
var res = '',
revisionData = data.query && data.query.pages[data.query.pageids[0]].revisions;


    /**
if (revisionData && revisionData.length>0) {
    * Load the users wikimarks
res = revisionData[0]['*'];
    */
} else {
    function loadWikimarks(username) {
return; // No wikimarks, end
        var load = 'https://coralisland.wiki/w/api.php',
}
            params = {
                action: 'query',
                format: 'json',
                prop: 'revisions',
                rvprop: 'content',
                // don't encode anything in the username here, $.ajax does it anyway
                // otherwise stuff gets encoded twice and no results are returned
                titles: 'User:' + (username || conf.wgUserName).replace(/ /g, '_') + '/Wikimarks',
                indexpageids: 1,
                origin: '*',
                // Cache results for 5 minutes in CDN and browser
                maxage: 300,
                smaxage: 300
            };


        if (username) {
res = preprocessData(res);
            testUser = true;
parseWikimarks(res);
        }
});
}


        mw.log('params', params);
/**
* Shows loading status until the wikimarks have loaded
*/
function showLoading() {
var $nav = $('.wds-community-header__local-navigation .wds-tabs, .fandom-community-header__local-navigation .wds-tabs'),
$li = $('<li>');


        $.ajax(load, {
$li.addClass('wds-tabs__tab wikimarks')
            data: params
.css({
        }).always(function (data) {
backgroundImage: 'url("https://vignette.wikia.nocookie.net/dev/images/8/82/Facebook_throbber.gif")',
        console.log(data, 'ajax data');
backgroundPosition: 'center center',
            var res = '',
backgroundRepeat: 'no-repeat',
                revisionData = data.query && data.query.pages[data.query.pageids[0]].revisions;
})
.append(
$('<div>')
.addClass('wds-dropdown')
.append(
$('<div>')
.addClass('wds-tabs__tab-label wds-dropdown__toggle first-level-item')
.append(
$('<a>')
.attr(
'href',
'https://dev.fandom.com/wiki/User:' + conf.wgUserName + '/Wikimarks'
)
.css('visibility', 'hidden')
.append(
$('<span>')
.text('WIKIMARKS')
)
)
)
);


            if (revisionData && revisionData.length>0) {
// hide the explore tab (the new "on the wiki" tab)
                res = revisionData[0]['*'];
// TODO: send in a ticket to get a class for this
            } else {
//   as it feels super fragile
            return; // No wikimarks, end
            }
// find the list with "random page" link and hide the whole list (explore tab)
$('.wds-list [data-tracking="explore-random"]').closest('.wds-dropdown').hide();
// add our new tab to the start of the nav
$nav.prepend($li);
}


            res = preprocessData(res);
/**
            parseWikimarks(res);
* Load stylesheets
        });
*/
    }
function loadStyles() {
 
mw.util.addCSS(
    /**
'.wikimarks a[data-uncrawlable-url], .wikimarks span[data-uncrawlable-url] {'+
    * Shows loading status until the wikimarks have loaded
    */
    function showLoading() {
        var $nav = $('.wds-community-header__local-navigation .wds-tabs, .fandom-community-header__local-navigation .wds-tabs'),
            $li = $('<li>');
 
        $li.addClass('wds-tabs__tab wikimarks')
            .css({
                backgroundImage: 'url("https://vignette.wikia.nocookie.net/dev/images/8/82/Facebook_throbber.gif")',
                backgroundPosition: 'center center',
                backgroundRepeat: 'no-repeat',
            })
            .append(
                $('<div>')
                    .addClass('wds-dropdown')
                    .append(
                        $('<div>')
                            .addClass('wds-tabs__tab-label wds-dropdown__toggle first-level-item')
                            .append(
                                $('<a>')
                                    .attr(
                                        'href',
                                        'https://dev.fandom.com/wiki/User:' + conf.wgUserName + '/Wikimarks'
                                    )
                                    .css('visibility', 'hidden')
                                    .append(
                                        $('<span>')
                                            .text('WIKIMARKS')
                                    )
                            )
                    )
            );
 
        // hide the explore tab (the new "on the wiki" tab)
        // TODO: send in a ticket to get a class for this
        //      as it feels super fragile
       
        // find the list with "random page" link and hide the whole list (explore tab)
        $('.wds-list [data-tracking="explore-random"]').closest('.wds-dropdown').hide();
        // add our new tab to the start of the nav
        $nav.prepend($li);
    }
 
    /**
    * Load stylesheets
    */
    function loadStyles() {
        mw.util.addCSS(
        '.wikimarks a[data-uncrawlable-url], .wikimarks span[data-uncrawlable-url] {'+
'align-items: center;'+
'align-items: center;'+
'border-radius: 3px;'+
'border-radius: 3px;'+
Line 342: Line 325:
'}'
'}'
);
);
    }
}


    /**
/**
    * Checks for the correct environment before allowing the script to continue
* Checks for the correct environment before allowing the script to continue
    */
*/
    function init() {
function init() {
        // prevent anyone trying to load this for anons
// prevent anyone trying to load this for anons
        if (!conf.wgUserName) {
if (!conf.wgUserName) {
            return;
return;
        }
}


        if (!$('#mw-navigation').length) {
if (!$('#mw-navigation').length) {
            mw.log('Wikimarks: sidebar not found, aborting...');
mw.log('Wikimarks: sidebar not found, aborting...');
            return;
return;
        }
}
       
        loadStyles();
loadStyles();
        loadWikimarks();
loadWikimarks();
    }
}


    mw.loader.using(['mediawiki.api', 'mediawiki.util'], function () {
mw.loader.using(['mediawiki.api', 'mediawiki.util'], function () {
        $(init);
$(init);
    });
});


    dev.loadWikimarks = loadWikimarks;
dev.loadWikimarks = loadWikimarks;


}(this.jQuery, this.mediaWiki, this.dev = this.dev || {}));
}(this.jQuery, this.mediaWiki, this.dev = this.dev || {}));

Latest revision as of 20:29, 12 July 2024

(function ($, mw, dev) {
	'use strict';

	var conf = mw.config.get([
			'wgPageName',
			'wgScriptPath',
			'wgServer',
			'wgUserName'
		]);

	/**
	 * Insert Wikimarks into the DOM and attach the relevant events
	 */
	function addHtml($menu) {
		var $wikimarks = $('#mw-navigation > .collapsible-nav');

		$wikimarks.append($menu);

		// everything is now done
		// so fire an event so people can interact/extend it further
		mw.hook('wikimarks.loaded').fire($wikimarks);
	}

	/**
	 * Prepare the parsed HTML and attach to the DOM
	 */
	function prepareHtml(html) {
		var $parsed = $(html);

		// remove the parser output wrapping element
		if ($parsed.hasClass('mw-parser-output')) {
			$parsed = $parsed.children();
		}

		var $menu = $('<nav id="p-Wikimarks" role="navigation" aria-labelledby="p-Wikimarks-label"></nav>')
				.addClass('vector-menu mw-portlet mw-portlet-Wikimarks vector-menu-portal portal expanded')
				.append(
					$('<h3></h3>', {id:'p-Wikimarks-label', 'class':'vector-menu-heading', tabindex: '0'})
						.append('<a href="/wiki/User:'+config.wgUserName+'/Wikimarks" aria-haspopup="true" aria-controls="p-Wikimarks-list" role="button" aria-pressed="true" aria-expanded="true"><span class="vector-menu-heading-label">Wikimarks</span></a>'),
					$('<div></div>').addClass('vector-menu-content').append($parsed)
				 );

		// add classes to elements
		$menu
			.children('div.vector-menu-content')
			.children('ul')
				.addClass('vector-menu-content-list')
				.attr('id', 'p-Wikimarks-list')
				.children('li').addClass('mw-list-item')
					.children('a')
						.siblings('ul')
						.remove(); // disallow nesting

		// remove href from text converted to links
		$menu.find('a[href="' + conf.wgScriptPath + '/wiki/"]')
			.removeAttr('href')
			.css('cursor', 'pointer');


		$menu.find('a')
			// titles don't add anything to the links
			.removeAttr('title')
			// remove external link class for ease of reading the source html
			.removeClass('extiw');

		addHtml($menu);
	}

	/**
	 * Pass the preprocess wikimarks to action=parse to be converted into wikitext
	 */
	function parseWikimarks(data) {
		var params = {
			action: 'parse',
			contentmodel: 'wikitext',
			prop: 'text',
			text: data
		};

		(new mw.Api())
			.post(params)
			.done(function (data) {
				var text = data.parse.text['*'];

				// remove preprocessor comment
				// should be able to hide it in api config
				// but that's broken in mw1.19
				text = text.replace(/<!--[\s\S]*?-->/g, '').trim();

				prepareHtml(text);
			});
	}

	/**
	 * Preprocesses a wikimarks page to make it compatible with the wikitext parser
	 */
	function preprocessData(data) {
		data = data.trim().split(/\n+/);

		var invalidLink = false,
			parsed = [],
			// handles:
			// - /wiki/ (wiki pages)
			// - index.php, api.php, and wikia.php (API)
			// - /f and /d (discussions)
			relativeUrlRe = /\/(wiki\/|(?:index|api|wikia)\.php|f|d)/;

		data.forEach(function (elem) {
			// ignore comments
			if (elem.indexOf('//') === 0 || elem.indexOf('#') === 0) {
				return;
			}

			// handle external links
			elem = elem.replace(/^(\*+)\s*\[([^\s]+)\s+(.+?)\]\s*$/, function (_, p1, p2, p3) {
				// handle query strings
				if (p2.indexOf('?') === 0) {
					return p1 + '[{{fullurl:' + conf.wgPageName + '|' + p2.slice(1) + '}} ' + p3 + ']';
				}

				// allow appending to existing query strings as well
				if (p2.indexOf('&') === 0) {
					return p1 + '[' + location.href + p2 + ' ' + p3 + ']';
				}

				// handle relative URLs
				if (p2.search(relativeUrlRe) === 0) {
					p2 = conf.wgServer + conf.wgScriptPath + p2;
				}

				// else just return it unchanged
				return p1 + ' [' + p2 + ' ' + p3 + ']';
			});

			// don't touch raw html
			// assumes that all html will begin with a tag, e.g. <span...
			if (!/^\*+\s*</.test(elem)) {
				// parse old style links to wikitext for backwards compatibility
				elem = elem.replace(/^(\*+)\s*([^\[]+?)\s*=\s*(.+?)\s*$/, function (_, p1, p2, p3) {
					// handle absolute URLs
					// 'http://' or 'https://' or '//'
					if (p3.search(/(?:https?:)?\/\//) === 0) {
						return p1 + ' [' + p3 + ' ' + p2 + ']';
					}

					// handle query strings
					if (p3.indexOf('?') === 0) {
						return p1 + '[{{fullurl:' + conf.wgPageName + '|' + p3.slice(1) + '}} ' + p2 + ']';
					}

					// allow appending to existing query strings as well
					if (p2.indexOf('&') === 0) {
						return p1 + '[' + location.href + p2 + ' ' + p3 + ']';
					}

					// attempt to fix instances of Foo?bar=baz
					// domain added below
					if (p3.indexOf('?') > -1) {
						p3 = '/wiki/' + p3;
					}

					// handle relative URLs
					if (p3.search(relativeUrlRe) === 0) {
						p3 = conf.wgServer + conf.wgScriptPath + p3;
						return p1 + ' [' + p3 + ' ' + p2 + ']';
					}

					// ## BREAKING CHANGE ##
					// don't allow 'javascript:' urls
					// ridiculously difficult to parse these in js without using `eval`
					if (p3.search(/(?:javascript:)?(?:url|win)\(/) === 0) {
						p3 = '#invalidLink';
						invalidLink = true;
					}

					// else we expect a normal wikilink
					return p1 + ' [[' + p3 + '|' + p2 + ']]';
				});
			}

			// remove css comment
			// caused by loading wikimarks config through RL and pretending it's CSS
			if (elem.search(/^\/\*.+?\*\/$/) === 0) {
				elem = '';
			}

			// substitute in global variables
			// syntax: {$VAR} where VAR is a global variable
			// @todo limit to stuff available in mw.config?
			elem = elem.replace(/\{\$(.+?)\}/g, function (_, p1) {
				// fix for properties of globals
				var parts = p1.split('.'),
					test = window,
					prop,
					i;

				for (i = 0; i < parts.length; i += 1) {
					prop = parts[i];

					// @todo how secure is this?
					if (test.hasOwnProperty(prop)) {
						test = test[prop];
					} else {
						break;
					}
				}

				if (['string', 'number'].indexOf(typeof test) > -1) {
					return test;
				} else {
					return mw.config.get(p1);
				}
			});

			// make simple text strings into a null link so it doesn't break the styling
			elem = elem.replace(/^(\*+)\s*([A-Za-z0-9\s]+)\s*$/, '$1 [[#|$2]]');

			parsed.push(elem);
		});

		data = parsed.join('\n').trim();
		mw.log(data);

		if (invalidLink) {
			// @todo do something
		}

		return data;
	}

	/**
	 * Load the users wikimarks
	 */
	function loadWikimarks(username) {
		var load = 'https://coralisland.wiki/w/api.php',
			params = {
				action: 'query',
				format: 'json',
				prop: 'revisions',
				rvprop: 'content',
				// don't encode anything in the username here, $.ajax does it anyway
				// otherwise stuff gets encoded twice and no results are returned
				titles: 'User:' + (username || conf.wgUserName).replace(/ /g, '_') + '/Wikimarks',
				indexpageids: 1,
				origin: '*',
				// Cache results for 5 minutes in CDN and browser
				maxage: 300,
				smaxage: 300
			};


		$.ajax(load, {
			data: params
		}).always(function (data) {
			console.log(data, 'ajax data');
			var res = '',
				revisionData = data.query && data.query.pages[data.query.pageids[0]].revisions;

			if (revisionData && revisionData.length>0) {
				res = revisionData[0]['*'];
			} else {
				return; // No wikimarks, end
			}

			res = preprocessData(res);
			parseWikimarks(res);
		});
	}

	/**
	 * Shows loading status until the wikimarks have loaded
	 */
	function showLoading() {
		var $nav = $('.wds-community-header__local-navigation .wds-tabs, .fandom-community-header__local-navigation .wds-tabs'),
			$li = $('<li>');

		$li.addClass('wds-tabs__tab wikimarks')
			.css({
				backgroundImage: 'url("https://vignette.wikia.nocookie.net/dev/images/8/82/Facebook_throbber.gif")',
				backgroundPosition: 'center center',
				backgroundRepeat: 'no-repeat',
			})
			.append(
				$('<div>')
					.addClass('wds-dropdown')
					.append(
						$('<div>')
							.addClass('wds-tabs__tab-label wds-dropdown__toggle first-level-item')
							.append(
								$('<a>')
									.attr(
										'href',
										'https://dev.fandom.com/wiki/User:' + conf.wgUserName + '/Wikimarks'
									)
									.css('visibility', 'hidden')
									.append(
										$('<span>')
											.text('WIKIMARKS')
									)
							)
					)
			);

		// hide the explore tab (the new "on the wiki" tab)
		// TODO: send in a ticket to get a class for this
		//	   as it feels super fragile
		
		// find the list with "random page" link and hide the whole list (explore tab)
		$('.wds-list [data-tracking="explore-random"]').closest('.wds-dropdown').hide();
		// add our new tab to the start of the nav
		$nav.prepend($li);
	}

	/**
	 * Load stylesheets
	 */
	function loadStyles() {
		mw.util.addCSS(
			'.wikimarks a[data-uncrawlable-url], .wikimarks span[data-uncrawlable-url] {'+
				'align-items: center;'+
				'border-radius: 3px;'+
				'display: flex;'+
				'line-height: 1.75em;'+
				'padding: 9px 6px;'+
			'}'
		);
	}

	/**
	 * Checks for the correct environment before allowing the script to continue
	 */
	function init() {
		// prevent anyone trying to load this for anons
		if (!conf.wgUserName) {
			return;
		}

		if (!$('#mw-navigation').length) {
			mw.log('Wikimarks: sidebar not found, aborting...');
			return;
		}
		
		loadStyles();
		loadWikimarks();
	}

	mw.loader.using(['mediawiki.api', 'mediawiki.util'], function () {
		$(init);
	});

	dev.loadWikimarks = loadWikimarks;

}(this.jQuery, this.mediaWiki, this.dev = this.dev || {}));