Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save meyu/21c1a268b2504e6d2c416605b8be4b5b to your computer and use it in GitHub Desktop.
Save meyu/21c1a268b2504e6d2c416605b8be4b5b to your computer and use it in GitHub Desktop.
exercise@freeCodeCamp: Build a Wikipedia Viewer

purpose

For the challenge of freeCodeCamp's "Build a Wikipedia Viewer", and wanna make the searching more lively .

build by

MediaWiki action API

  • a little bit hard for me to understand the whole API of MediaWiki.
  • the searching part, I use the API:Search as a start.
  • API:Search did fit the need, but I want the results could be more intuitive with their own picture. Since API:Search got only text information, I found Extension:PageImages to fetching the thumbnails for each result.
  • Multi Language Searching is necessary, I think. So I made a drop down list to change the API domain. was plan to get full list of languages, from API, that Wikipedia can offer. Yet, found no clue for doing that. 

Materialize v0.98.2

  • cause was fascinated by the Material design, and Materialize was the easiest CSS framework, and, relatively, it did interpret the design really well.
  • the Modals, Cards and inputs fields are stunning, but still took me a lot of CSS tuning work.

Tweet

  • just wanna place a share button, so I made a tweet button.
  • thought it should be easy, but fell down when passing text with special sign as URL parameter.
<!-- 操作區 -->
<header class="col valign-wrapper z-depth-5 brown lighten-4">
<!-- 輸入區 -->
<div class="row">
<div class="input-field col s8">
<i id="iconSearch" class="material-icons prefix">search</i>
<input id="keyword" class="h2" type="text">
<label for="keyword">Search Wikipedia</label>
</div>
<div class="input-field col s4">
<select id="lang"></select>
</div>
</div>
<!-- 補充資訊區 -->
<div class="author moreInfo">
<a id="demoGist" href="#showDetail" class="waves-effect waves-light btn-flat"
data-title="Wikipedia Viewer" data-url="">
<i class="material-icons">code</i></a>
</div>
<div class="supriseMe moreInfo">
<a id="ranWiki" href="#showDetail" class="waves-effect waves-light btn-flat"
data-title="random article" data-url="">
suprise me</a>
</div>
</header>
<!-- 資料區 -->
<main>
<div id="result"></div>
</main>
<!-- 預覽 -->
<div id="showDetail" class="modal bottom-sheet">
<div class="modal-content">
<!-- Preloader -->
<div class="progress" style="display:none;">
<div class="indeterminate"></div>
</div>
<!-- wiki preview -->
<iframe id="showWiki" style="display:none;" src=""></iframe>
<!-- my code gist -->
<div id='myGist' style="display:none;">
<script type="text/javascript" src="//gist.github.com/meyu/21c1a268b2504e6d2c416605b8be4b5b.js"></script>
</div>
<!-- Fixed Action Button -->
<div class="fixed-action-btn">
<a class="btn-floating btn-large red" data-position="bottom" data-delay="50" data-tooltip="I am tooltip">
<i class="large material-icons">menu</i>
</a>
<ul>
<li>
<a class="btn-floating brown lighten-4 modal-action modal-close tooltipped" data-position="left" data-tooltip="close">
<i class="material-icons">close</i>
</a>
</li>
<li>
<a id="tweetMe" class="btn-floating light-blue accent-2 tooltipped" style="display:none;" data-position="left" data-tooltip="tweet this page" target="_blank" href="">
<i class="fa fa-twitter" aria-hidden="true"></i>
</a>
</li>
<li>
<a id="openMe" class="btn-floating orange accent-2 tooltipped" style="display:none;" data-position="left" data-tooltip="open this page" target="_blank" href="">
<i class="material-icons">open_in_new</i>
</a>
</li>
</ul>
</div>
</div>
</div>
var maxResult = 15; //設定搜尋結果的上限
var penURL = "https://codepen.io/meyu/full/gRORMj/"; //本作品的主網址
var myGist = "//gist.github.com/meyu/21c1a268b2504e6d2c416605b8be4b5b";
var wikiURL = "wikipedia.org",
wiki = "wikipedia.org";
$(document).ready(function() {
bindMultiLang();
setModal();
setShyScroll();
$(".tooltipped").tooltip();
$("select").material_select();
});
//////////////////////////////////
// 資料結繫
//////////////////////////////////
// 取得並輸出查詢結果,搜尋方案為:https://www.mediawiki.org/wiki/API:Search
function getWiki(inputKeyword) {
$.ajax({
url: "//" + wikiURL + "/w/api.php",
data: {
action: "query",
list: "search",
srinfo: "suggestion",
srprop: "snippet",
srlimit: maxResult,
srsearch: inputKeyword,
format: "json"
},
dataType: "jsonp",
success: function(data) {
var html = "";
var orderID = 1,
prefixID = "result"; //為每個結果安排一個id
//羅列結果
$.each(data.query.search, function(i, obj) {
html += cardHTML(
obj.title,
obj.snippet,
"//" + wikiURL + "/wiki/" + obj.title,
prefixID + orderID
);
getWikiImage(obj.title, prefixID + orderID); //由於API:Search無法取得文章的代表圖源,故需逐一要圖
orderID += 1;
});
$("#result").html('<div class="row">' + html + "</div>");
}
});
}
// 取得指定文章之主要縮圖,並嵌入文章中。取用方案:https://www.mediawiki.org/wiki/Extension:PageImages
function getWikiImage(title, targetID) {
$.ajax({
url: "//" + wikiURL + "/w/api.php",
data: {
action: "query",
prop: "pageimages",
piprop: "thumbnail",
pithumbsize: 200,
pilimit: 1,
titles: title,
format: "json"
},
dataType: "jsonp",
success: function(data) {
var obj = data.query.pages[Object.keys(data.query.pages)[0]];
var key = "thumbnail";
if (key in obj) {
var src = obj[key].source;
$("#" + targetID).css("background-image", 'url("' + src + '")');
} else {
$("#" + targetID).css("background-image", 'url("#")');
}
}
});
}
// 土炮製作語系清單
// TODO: 仍在尋找能顯示 Wikipedia 可選語系的 API
function bindMultiLang() {
var laHTML =
'<option value="en">English</option>' +
'<option value="zh">中文</option>' +
'<option value="es">Español</option>' +
'<option value="ja">日本語</option>' +
'<option value="de">Deutsch</option>' +
'<option value="ru">Русский</option>' +
'<option value="fr">Français</option>' +
'<option value="it">Italiano</option>' +
'<option value="pt">Português</option>' +
'<option value="pl">Polski</option>';
$("#lang").html(laHTML);
wikiURL = $("#lang option:selected").val() + "." + wiki;
}
// 客製化 Modal 觸發動作,使能結繫 trigger 所指定之連結,並觸發載入特效
function setModal() {
$(".modal").modal({
ready: function(modal, trigger) {
var url = trigger.data("url");
if (url.length !== '' && url != myGist) {
var iframe = document.getElementById("showWiki");
iframe.src = url;
doIframeLoadShow(iframe);
} else {
$("#myGist").show();
}
setOpenMeButton(url);
setTweetMeButton(trigger.data("title"), url);
// 由於抓不到 random 轉址後的網址,故為 random 時不顯示這些按鈕
if (url == $("#ranWiki").data("url")) {
$("#openMe").hide();
$("#tweetMe").hide();
}
},
complete: function() {
$("iframe").hide();
$("#myGist").hide();
$("#openMe").hide();
$("#tweetMe").hide();
}
});
}
// 隨機文章配合語系選擇
$("#ranWiki").data("url", "//" + wikiURL + "/wiki/Special:Random");
$("#demoGist").data("url", myGist);
//////////////////////////////////
// 互動觸發
//////////////////////////////////
// 觸發即時搜尋
var keyword = $("#keyword");
keyword.on("input", function() {
doSearch();
});
function doSearch() {
if (keyword.val().trim().length > 0) {
getWiki(keyword.val());
} else {
$("#result").html("");
}
}
// 變更語系目標
$("#lang").change(function() {
wikiURL = this.value + "." + wiki;
$("#ranWiki").data("url", "//" + wikiURL + "/wiki/Special:Random");
doSearch();
});
//////////////////////////////////
// 安排物件
//////////////////////////////////
// 在 iframe 完成載入後,再顯示 iframe,並隱藏 progress bar
// 偵測 iframe 載入情況無現成方法,偵浮方案係參考:https://www.nczonline.net/blog/2009/09/15/iframes-onload-and-documentdomain/
function doIframeLoadShow(iframe) {
$(".progress").show();
if (iframe.attachEvent) {
iframe.attachEvent("onload", function() {
iframe.style.display = "block";
$(".progress").hide();
});
} else {
iframe.onload = function() {
iframe.style.display = "block";
$(".progress").hide();
};
}
}
// 設定另開視窗的按鈕
function setOpenMeButton(url) {
$("#openMe").attr("href", url);
$("#openMe").show();
}
// 設定 Tweet 按鈕 (https://dev.twitter.com/web/tweet-button/web-intent)
function setTweetMeButton(text, url) {
//由於特殊符號無法放入URL,故在這裡將其轉為全形符號
text = text.replace(/,/g,",").replace(/\//g,"/").replace(/\?/g,"?").replace(/\:/g,":").replace(/@/g,"@").replace(/&/g,"&").replace(/=/g,"=").replace(/\+/g,"+").replace(/\$/g,"$").replace(/#/g,"#");
//由於wikipedai會將網址中的空格轉為底線,所以這裡也要比照辦理才行。另,由於wikipedia的網址會包含特殊符號,非一般URL的規範,無法放入tweet的url參數中,故一併放入內容中
url = url.replace(/\s/g,"_").replace(/\+/g,"plus");
var tweetURL =
"//twitter.com/share?text=" +
text + ' -- https:' + url + "&url=''";
$("#tweetMe").attr("href", tweetURL);
$("#tweetMe").show();
}
// 輸入區的捲動佈局
function setShyScroll() {
window.addEventListener("scroll", function(e) {
var offsetY = window.scrollY;
var shyPoint = 50;
if (offsetY > shyPoint) {
$("header").addClass("shy");
$("#iconSearch").addClass("shy");
$(".moreInfo").hide();
} else {
$("header").removeClass("shy");
$("#iconSearch").removeClass("shy");
$(".moreInfo").show();
}
});
}
// 輸出 card 的 html 語句
function cardHTML(title, content, link, ID) {
var content =
'<div class="card-content white-text">' +
'<span class="card-title" >' +
title +
"</span>" +
"<p>" +
content +
"</p>" +
"</div>";
var clickable =
'<a href="#showDetail" data-url="' +
link +
'" data-title="' +
title +
'">' +
content +
"</a>";
return (
'<div class="col s12 m12 l6 xl4">' +
'<div id="' +
ID +
'" class="card brown lighten-2 z-depth-3">' +
clickable +
"</div>" +
"</div>"
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.2/js/materialize.min.js"></script>
/* 基本佈局 */
html, body {
height: 100vh;
background-color: #3e2723;
}
.supriseMe, .author {
position: absolute;
bottom: 0;
right: 0;
}
.author {
left: 0;
}
/* 卡片特效 */
.searchmatch {
text-shadow: 2px 1px 1px #3e2723;
}
.card {
background-repeat: no-repeat;
background-position: right center;
background-size: auto 100%;
}
.card-content {
text-shadow: 1px 1px 1px #3e2723;
background: -webkit-linear-gradient(
right,
rgba(161, 136, 127, 1) 60%,
rgba(161, 136, 127, 0) 100%
); /*Safari 5.1-6*/
background: linear-gradient(
to right,
rgba(161, 136, 127, 1) 60%,
rgba(161, 136, 127, 0) 100%
); /*Standard*/
}
/* 輸入區的捲動佈局 */
header .row, input {
margin-bottom: 0 !important;
}
main {
padding-top: 42vh;
}
header {
width: 100%;
height: 40vh;
z-index: 999;
position: fixed;
-webkit-transition: height 0.4s;
transition: height 0.4s;
}
header.shy {
height: 70px;
}
#iconSearch {
font-size: 5rem;
margin-left: -20px;
-webkit-transition: font-size 0.5s, margin-left 0.5s;
transition: font-size 0.5s, margin-left 0.5s;
}
#iconSearch.shy {
font-size: 3rem;
margin-left: 0px;
}
/* Modals 微調*/
.bottom-sheet, .modal-content {
max-height: 75vh !important;
height: 75vh !important;
padding: 0 !important;
}
iframe {
width: 100%;
height: 99%;
border-style: none;
}
.progress {
position: absolute;
top: 0;
}
/* 搜尋框色調微調 */
.input-field input[type=text]:focus + label {
color: #fff;
}
.input-field input[type=text]:focus {
border-bottom: 1px solid #fff;
box-shadow: 0 1px 0 0 #fff;
}
.input-field input[type=text].valid {
border-bottom: 1px solid #fff;
box-shadow: 0 1px 0 0 #fff;
}
.input-field input[type=text].invalid {
border-bottom: 1px solid #fff;
box-shadow: 0 1px 0 0 #fff;
}
.input-field .prefix.active {
color: #fff;
}
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.2/css/materialize.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment