Wednesday, February 29, 2012

Membuat Plugin OpenSeach Untuk Blogger

OpenSearch adalah kumpulan format sederhana untuk berbagi hasil pencarian. OpenSearch digunakan untuk membuat sebuah mesin pencari yang dapat digunakan oleh aplikasi client pencarian (browser). Masih bingung juga dengan pengertian tersebut? Wajarlah, karena jika mengikuti pengertian yang diberikan oleh situs resminya memang agak sulit dimengerti. Pengertian OpenSearch tersebut memang diambil dan diterjemahkan dari situs resminya OpenSearch.org. Maksudnya, pada browser yang kita gunakan biasanya ada baris mesin pencari pada sebelah kanan atas terutama pada Firefox dan IE 7.
Pada baris mesin pencari ini kita bisa memilih dan menentukan mesin pencari apa yang akan kita gunakan. Baris mesin pencari sangat berguna bagi mereka yang ingin melakukan pencarian cepat tanpa harus membuka halaman mesin pencari yang dimaksud, misal kita ingin melakukan pencarian menggunakan Google maka kita tidak perlu membuka terlebih dahulu halaman Google, cukup pilih mesin pencari yang akan digunakan dan ketikan kata kunci pada area yang disediakan. Maka halaman Google akan terbuka dengan hasil pencarian yang sesuai dengan kata kunci yang telah dimasukan. Nah, pada artikel ini Blogger Tune-Up akan membahas cara membuat plugin browser mesin pencari untuk blog kita sendiri.
Membuat Plugin OpenSeach Untuk Blogger

Skenario Plugin OpenSeach

Ketika pengunjung membuka halaman blog maka pada bagian "Daftar Mesin Pencari" akan keluar sebuah baris informasi penambahan mesin pencari, (misal; Tambahkan "Blogger tune-Up"). Dan ketika baris informasi penambahan tersebut di klik maka sebuah mesin pencari akan terpasang. Jika kita berencana mencari-cari informasi dari blog yang menyediakan mesin pencari tersebut maka cukup memilih mesin pencari untuk blog yang kita tuju dan masukan kata kunci. Halaman blog yang dituju akan terbuka bersama dengan artikel yang sesuai dengan kata kunci. Untuk memposisikan mesin pencari dan atau menghapusnya cukup masuk ke menu "Kelola Mesin Pencari..."

Deskripsi Berkas OpenSearch

Berkas Plugin OpenSearch untuk Blogger ditulis menggunakan bahasa XML, jadi bisa ditulis dengan perangkat lunak Notepad atau yang sejenisnya. Berkas file XML yang digunakan sebagai mesin pencari sebenarnya cukup sederhana, cukup mengikuti aturan template dasar di bawah ini. Kita cukup menyesuaikannya berdasarkan kebutuhan dari plugin mesin pencari tertentu yang ingin Anda tulis. Dibawah ini template Plugin OpenSearch yang diambil dari situs Developer Mozilla dan situs OpenSearch.

Template Plugin OpenSearch dari Mozilla Developer Network
Dibawah ini adalah template yang di ambil dari situs Mozilla Developer Network, silahkan kunjungi situs resminya jika ingin memahami lebih lanjut tentang penggunaan dan penerapannya.
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">
<ShortName>engineName</ShortName>
<Description>engineDescription</Description>
<InputEncoding>inputEncoding</InputEncoding>
<Image width="16" height="16" type="image/x-icon">data:image/x-icon;base64,imageData</Image>
<Url type="text/html" method="method" template="searchURL">
<Param name="paramName1" value="paramValue1"/>
...
<Param name="paramNameN" value="paramValueN"/>
</Url>
<Url type="application/x-suggestions+json" template="suggestionURL"/>
<moz:SearchForm>searchFormURL</moz:SearchForm>
</OpenSearchDescription>
Template Plugin OpenSearch dari OpenSearch Draft
Dibawah ini adalah template yang di ambil dari situs OpenSearch Draft sebagai pencetus ide OpenSearch, silahkan kunjungi situs resminya jika ingin memahami lebih lanjut tentang penggunaan dan penerapannya.
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Web Search</ShortName>
<Description>Use Example.com to search the Web.</Description>
<Tags>example web</Tags>
<Contact>admin@example.com</Contact>
<Url type="application/rss+xml" template="http://example.com/?q={searchTerms}&amp;pw={startPage?}&amp;format=rss"/>
</OpenSearchDescription>

Contoh Berkas Plugin OpenSearch Blogger

Dibawah ini adalah template yang Plugin OpenSearch yang Blogger Tune-Up gunakan. Silahkan klik link ini untuk melihat source code Plugin OpenSearch yang Blogger Tune-Up buat untuk browser Firefox.
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">
<ShortName>Blogger Tune-Up</ShortName>
<Description>Pencarian Blogger Tune-Up Web Design Tutorial Sumber Daya dan Inspirasi</Description>
<Url type="text/html" method="get" template="http://modification-blog.blogspot.com/search">
<Param name="q" value="{searchTerms}" />
</Url>
<Image width="16" height="16" type="image/x-icon">http://modification-blog.blogspot.com/favicon.ico</Image>
<Developer>Dede Hendriono</Developer>
<InputEncoding>UTF-8</InputEncoding>
<Url type="application/opensearchdescription+xml" rel="self" template="http://modification-blog.blogspot.com/cari.xml" />
<moz:SearchForm>http://modification-blog.blogspot.com/search</moz:SearchForm>
</OpenSearchDescription>
Gunakan Notepad atau perangkat lunak sejenisnya untuk membuat atau melakukan perubahan data dan jangan lupa simpanlah file ini dengan filetype .xml, jangan lupa beri nama file tersebut (misal; cari.xml).
<ShortName>NAMA_BLOG</ShortName>
Ubah NAMA_BLOG sesuai dengan nama Blog anda
<Description>DESKRIPSI_BLOG</Description>
Ubah DESKRIPSI_BLOG sesuai dengan deskripsi blog anda
<Url type="text/html" method="get" template="http://ALAMAT_BLOG/search">
<Image width="16" height="16" type="image/x-icon">http://ALAMAT_BLOG/favicon.ico</Image>
<moz:SearchForm>http://ALAMAT_BLOG/search</moz:SearchForm>
Ubah ALAMAT_BLOG sesuai dengan alamat blog anda
<Developer>NAMA_ADMIN</Developer>
Ubah NAMA_ADMIN sesuai dengan nama pengelola Blog
<Url type="application/opensearchdescription+xml" rel="self" template="http://URL_HOSTING/nama_file.xml" />
Ubahlah link http://URL_HOSTING/nama_file.xml sesuai dengan alamat tempat anda menyimpan file ini. (berarti 2 kali pengeditan, upload, ambil alamat link, edit lagi dan masukan alamat link pada file ini). Unggahlah file ini pada hosting milik anda.

Integrasi Plugin OpenSearch Blogger

Setelah berkas plugin OpenSearch Blogger sudah di unggah pada hosting anda, ikuti langkah dibawah ini.
Langkah 1
Login ke Blogger
Langkah 2
Masuk ke Rancangan - Edit HTML
Langkah 3
Cari kode dibawah ini:
<head>
atau
</head>
Langkah 4
Masukan (copy paste) kode dibawah ini diantara tag <head>...</head>:
<link href='http://URL_HOSTING/nama_file.xml' rel='search' title='Blogger Tune-Up' type='application/opensearchdescription+xml'/>
Jangan lupa ubah dulu link sesuai dengan link yang diberikan hosting.
Langkah 5
Simpan Template dan Preview Blog. Jika benar maka akan ada sebuah baris penambahan mesin pencari.

Selamat mencoba dan semoga berhasil... Happy Blogging :)

Tuesday, February 28, 2012

Memahami Threaded Comments Blogger

Sejak Blogger meluncurkan Threaded Comments, banyak sekali para pengguna BlogSpot berbondong-bondong beralih ke komentar versi "Balas" ini. Namun tidak sedikit yang berakhir kecewa karena ternyata Threaded Comments tidak bisa bekerja dengan baik, bahkan ada yang sampai akhirnya beralih ke pihak ketiga penyedia layanan komentar karena sulitnya integrasi Threaded Comments pada template Blogger. Apa dan bagaimana sebenarnya Threaded Comments? Kita akan bahas (tuntas) untuk bisa memahami tentang fasilitas Threaded Comments pada Blogger dengan harapan mempermudah para blogger untuk melakukan modifikasi kode pada templatenya.
Fix Problem Reply Threaded Comments
Sebelum membahas tentang kode-kode yang memusingkan, mari kita pahami dulu perbedaan mendasar antar template yang telah dikeluarkan oleh Blogger. Blogger telah mengeluarkan beberapa versi template. Untuk mempermudahnya maka kita definiskan; versi kesatu kita sebut sebagai template Tata Letak yang menggunakan HTML4; dan versi kedua kita sebut sebagai template Perancang yang sudah menggunakan HTML5. Sedangkan versi Classic, versi Dynamic View dan versi Mobile tidak akan dibahas agar materi tidak terlalu melebar.

Dibawah ini akan diulas sekelumit tentang perbedaan mendasar namun penting untuk diketahui, antara template blogger versi Tata Letak (HTML4) dengan template versi Perancang (HTML5).

Header Document

Inilah perbedaan awal template blogger, jika kita menggunakan template versi Tata Letak berarti kita menggunakan HTML4. Jika kita menggunakan template versi Perancang berarti kita menggunakan HTML5. Silahkan perhatikan Header Document dibawah ini:
Versi Tata Letak - HTML4
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html expr:dir='data:blog.languageDirection' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'>
Versi Perancang - HTML5
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html b:version='2' class='v2' expr:dir='data:blog.languageDirection' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'>
Catatan : Mengenai detail tag yang digunakan pada HTML5 akan dibahas pada artikel terpisah

CSS Widget Default

Perbedaan selanjutnya adalah stylesheet default yang disertakan (tanpa penawaran) pada template.
Versi Tata Letak
Stylesheet versi Tata Letak yang disertakan pada template tidak mengandung kode css untuk Threaded Comments.
<link type='text/css' rel='stylesheet' href='http://www.blogger.com/static/v1/widgets/2002994779-widget_css_bundle.css' />
Versi Perancang
Stylesheet versi Perancang yang disertakan pada template sudah terintegrasi kode css untuk Threaded Comments.
<link type='text/css' rel='stylesheet' href='http://www.blogger.com/static/v1/widgets/4132898751-widget_css_2_bundle.css' />

Kode Internal Template Threaded Comments

Sebenarnya antara template versi HTML4 dengan versi HTML5 tidak terlalu banyak perubahan dari sisi code (X)HTML-nya untuk fasilitas Threaded Comments. Perhatikan kode Threaded Comments dibawah ini dan bandingkan.

Versi Tata Letak - HTML4
<b:includable id='threaded_comment_js' var='post'>
<b:includable id='comment-form' var='post'>
<b:includable id='threaded_comments' var='post'>
<b:includable id='threaded-comment-form' var='post'>
<b:includable id='threaded_comment_css'>
<b:includable id='comments' var='post'>
Versi Perancang - HTML5
<b:includable id='threaded_comment_js' var='post'>
<b:includable id='comment-form' var='post'>
<b:includable id='threaded_comments' var='post'>
<b:includable id='threaded-comment-form' var='post'>
<b:includable id='comments' var='post'>
Dari perbandingan diatas maka akan ditemukan satu includable saja yang berbeda. Pada versi HTML4 terdapat <b:includable id='threaded_comment_css'> sedangkan pada versi HTML5 tidak ada karena stylesheet-nya sudah terintegrasi pada stylesheet default bawaan template (lihat penjelasan CSS Widget Default). Sedangkan ketika kita buka satu-satu bagian includable tersebut diatas maka ada perbedaan mendasar hanya pada bagian dalam kode dari <b:includable id='threaded_comment_js' var='post'> seperti dijelaskan selanjutnya.

Lokasi Penempatan Threaded Comments

Dimana lokasi penempatan Threaded Comments? Sebelum beralih ke Threaded Comments kita telusuri lebih dulu lokasi penempatan comments sebelum keluar versi Threaded Comments.
Lokasi Kode Sebelum Versi Threaded Comments
<b:if cond='data:blog.pageType == &quot;static_page&quot;'>
<b:include data='post' name='comments'/>
</b:if>

<b:if cond='data:blog.pageType == &quot;item&quot;'>
<b:include data='post' name='comments'/>
</b:if>
Perhatikan kode <b:include data='post' name='comments'/> itu adalah lokasi penempatan komentar dan form komentar. Pada template, kode tersebut terdapat pada 4 (empat) lokasi, jadi bukan hanya 2 (dua).

Lokasi Kode Sesudah Versi Threaded Comments
<b:if cond='data:blog.pageType == &quot;static_page&quot;'>
<b:if cond='data:post.showThreadedComments'>
<b:include data='post' name='threaded_comments'/>
<b:else/>
<b:include data='post' name='comments'/>
</b:if>
</b:if>

<b:if cond='data:blog.pageType == &quot;item&quot;'>
<b:if cond='data:post.showThreadedComments'>
<b:include data='post' name='threaded_comments'/>
<b:else/>
<b:include data='post' name='comments'/>
</b:if>
</b:if>
Perhatikan kode diatas lalu bandingkan dengan versi sebelum Threaded Comments, ada penambahan beberapa kode. Jika kita telusuri pada template, maka akan ditemukan 2 (dua) lokasi penempatan komentar.
Jika kode komentar template anda masih menggunakan versi awal (sebelum threaded comments) maka kode tersebut harus diganti dengan kode versi Threaded Comments. Namun ingat, bahwa kode tersebut terdapat pada 2 (dua) lokasi, gantilah kedua-duanya. Seperti dijelaskan pada artikel sebelumnya.

JavaScript Threaded Comments

Bagian inilah yang memiliki perbedaan sangat menonjol setelah dibuka dan dibandingkan. Namun tidak akan dibahas mendetail (lain waktu jika tidak malas). Dan ini merupakan salah satu solusi yang ditemukan Blogger Tune-Up ketika mendapati masalah Reply Threaded Comment tidak bekerja dengan baik, yaitu menukar JavaScript versi HTML4 dengan JavaScript versi HTML5.
Versi Tata Letak - HTML4
<b:includable id='threaded_comment_js' var='post'>
<script defer='defer' expr:src='data:post.commentSrc' type='text/javascript'/>
<script type='text/javascript'>
(function() {
var items = <data:post.commentJso/>;
var msgs = <data:post.commentMsgs/>;
var postId = &#39;<data:post.id/>&#39;;
var feed = &#39;<data:post.commentFeed/>&#39;;
var authorName = &#39;<data:post.author/>&#39;;
var authorUrl = &#39;<data:post.authorUrl/>&#39;;
var blogId = &#39;<data:top.id/>&#39;;
var baseUri = &#39;<data:post.commentBase/>&#39;;

// <![CDATA[
feed += '?alt=json&v=2&orderby=published&reverse=false&max-results=50';
var cursor = null;
if (items && items.length > 0) {
cursor = parseInt(items[items.length - 1].timestamp) + 1;
}

var bodyFromEntry = function(entry) {
if (entry.gd$extendedProperty) {
for (var k in entry.gd$extendedProperty) {
if (entry.gd$extendedProperty[k].name == 'blogger.contentRemoved') {
return '<span class="deleted-comment">' + entry.content.$t + '</span>';
}
}
}
return entry.content.$t;
}

var parse = function(data) {
cursor = null;
var comments = [];
if (data && data.feed && data.feed.entry) {
for (var i = 0, entry; entry = data.feed.entry[i]; i++) {
var comment = {};
// comment ID, parsed out of the original id format
var id = /blog-(\d+).post-(\d+)/.exec(entry.id.$t);
comment.id = id ? id[2] : null;
comment.body = bodyFromEntry(entry);
comment.timestamp = Date.parse(entry.published.$t) + '';
if (entry.author && entry.author.constructor === Array) {
var auth = entry.author[0];
if (auth) {
comment.author = {
name: (auth.name ? auth.name.$t : undefined),
profileUrl: (auth.uri ? auth.uri.$t : undefined),
avatarUrl: (auth.gd$image ? auth.gd$image.src : undefined)
};
}
}
if (entry.link) {
if (entry.link[2]) {
comment.link = comment.permalink = entry.link[2].href;
}
if (entry.link[3]) {
var pid = /.*comments\/default\/(\d+)\?.*/.exec(entry.link[3].href);
if (pid && pid[1]) {
comment.parentId = pid[1];
}
}
}
comment.deleteclass = 'item-control blog-admin';
if (entry.gd$extendedProperty) {
for (var k in entry.gd$extendedProperty) {
console.log(entry.gd$extendedProperty[k].name + ' - ' + entry.gd$extendedProperty[k].value);
if (entry.gd$extendedProperty[k].name == 'blogger.itemClass') {
comment.deleteclass += ' ' + entry.gd$extendedProperty[k].value;
}
}
}
comments.push(comment);
}
}
return comments;
};

var paginator = function(callback) {
if (hasMore()) {
var url = feed;
if (cursor) {
url += '&published-min=' + new Date(cursor).toISOString();
}
window.bloggercomments = function(data) {
var parsed = parse(data);
cursor = parsed.length < 50 ? null
: parseInt(parsed[parsed.length - 1].timestamp) + 1
callback(parsed);
window.bloggercomments = null;
}
url += '&callback=bloggercomments';
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
};
var hasMore = function() {
return !!cursor;
};
var getMeta = function(key, comment) {
if ('iswriter' == key) {
var matches = !!comment.author
&& comment.author.name == authorName
&& comment.author.profileUrl == authorUrl;
return matches ? 'true' : '';
} else if ('deletelink' == key) {
return baseUri + '/delete-comment.g?blogID=' + blogId + '&postID=' + comment.id;
} else if ('deleteclass' == key) {
return comment.deleteclass;
}
return '';
};

var replybox = null;
var replyUrlParts = null;
var replyParent = undefined;

var onReply = function(commentId, domId) {
if (replybox == null) {
// lazily cache replybox, and adjust to suit this style:
replybox = document.getElementById('comment-editor');
if (replybox != null) {
replybox.height = '250px';
replybox.style.display = 'block';
replyUrlParts = replybox.src.split('#');
}
}
if (replybox && (commentId !== replyParent)) {
document.getElementById(domId).insertBefore(replybox, null);
replybox.src = replyUrlParts[0]
+ (commentId ? '&parentID=' + commentId : '')
+ '#' + replyUrlParts[1];
replyParent = commentId;
}
};

var tok = 'comment-form_';
var hash = window.location.hash || '';
var startThread = hash.indexOf(tok) == 1 ? hash.substring(tok.length + 1) : undefined;

// Configure commenting API:
var configJso = {
'maxDepth': 2
};
var provider = {
'id': postId,
'data': items,
'loadNext': paginator,
'hasMore': hasMore,
'getMeta': getMeta,
'onReply': onReply,
'rendered': true,
'initReplyThread': startThread,
'config': configJso,
'messages': msgs
};

var render = function() {
if (window.goog && window.goog.comments) {
var holder = document.getElementById('comment-holder');
window.goog.comments.render(holder, provider);
}
};

// render now, or queue to render when library loads:
if (window.goog && window.goog.comments) {
render();
} else {
window.goog = window.goog || {};
window.goog.comments = window.goog.comments || {};
window.goog.comments.loadQueue = window.goog.comments.loadQueue || [];
window.goog.comments.loadQueue.push(render);
}
})();
// ]]>
</script>
</b:includable>
Versi Perancang - HTML5
<b:includable id='threaded_comment_js' var='post'>
<script async='async' expr:src='data:post.commentSrc' type='text/javascript'/>
<script type='text/javascript'>
(function() {
var items = <data:post.commentJso/>;
var msgs = <data:post.commentMsgs/>;
var config = <data:post.commentConfig/>;

// <![CDATA[
var cursor = null;
if (items && items.length > 0) {
cursor = parseInt(items[items.length - 1].timestamp) + 1;
}

var bodyFromEntry = function(entry) {
if (entry.gd$extendedProperty) {
for (var k in entry.gd$extendedProperty) {
if (entry.gd$extendedProperty[k].name == 'blogger.contentRemoved') {
return '<span class="deleted-comment">' + entry.content.$t + '</span>';
}
}
}
return entry.content.$t;
}

var parse = function(data) {
cursor = null;
var comments = [];
if (data && data.feed && data.feed.entry) {
for (var i = 0, entry; entry = data.feed.entry[i]; i++) {
var comment = {};
// comment ID, parsed out of the original id format
var id = /blog-(\d+).post-(\d+)/.exec(entry.id.$t);
comment.id = id ? id[2] : null;
comment.body = bodyFromEntry(entry);
comment.timestamp = Date.parse(entry.published.$t) + '';
if (entry.author && entry.author.constructor === Array) {
var auth = entry.author[0];
if (auth) {
comment.author = {
name: (auth.name ? auth.name.$t : undefined),
profileUrl: (auth.uri ? auth.uri.$t : undefined),
avatarUrl: (auth.gd$image ? auth.gd$image.src : undefined)
};
}
}
if (entry.link) {
if (entry.link[2]) {
comment.link = comment.permalink = entry.link[2].href;
}
if (entry.link[3]) {
var pid = /.*comments\/default\/(\d+)\?.*/.exec(entry.link[3].href);
if (pid && pid[1]) {
comment.parentId = pid[1];
}
}
}
comment.deleteclass = 'item-control blog-admin';
if (entry.gd$extendedProperty) {
for (var k in entry.gd$extendedProperty) {
if (entry.gd$extendedProperty[k].name == 'blogger.itemClass') {
comment.deleteclass += ' ' + entry.gd$extendedProperty[k].value;
}
}
}
comments.push(comment);
}
}
return comments;
};

var paginator = function(callback) {
if (hasMore()) {
var url = config.feed + '?alt=json&v=2&orderby=published&reverse=false&max-results=50';
if (cursor) {
url += '&published-min=' + new Date(cursor).toISOString();
}
window.bloggercomments = function(data) {
var parsed = parse(data);
cursor = parsed.length < 50 ? null
: parseInt(parsed[parsed.length - 1].timestamp) + 1
callback(parsed);
window.bloggercomments = null;
}
url += '&callback=bloggercomments';
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
};
var hasMore = function() {
return !!cursor;
};
var getMeta = function(key, comment) {
if ('iswriter' == key) {
var matches = !!comment.author
&& comment.author.name == config.authorName
&& comment.author.profileUrl == config.authorUrl;
return matches ? 'true' : '';
} else if ('deletelink' == key) {
return config.baseUri + '/delete-comment.g?blogID='
+ config.blogId + '&postID=' + comment.id;
} else if ('deleteclass' == key) {
return comment.deleteclass;
}
return '';
};

var replybox = null;
var replyUrlParts = null;
var replyParent = undefined;

var onReply = function(commentId, domId) {
if (replybox == null) {
// lazily cache replybox, and adjust to suit this style:
replybox = document.getElementById('comment-editor');
if (replybox != null) {
replybox.height = '250px';
replybox.style.display = 'block';
replyUrlParts = replybox.src.split('#');
}
}
if (replybox && (commentId !== replyParent)) {
document.getElementById(domId).insertBefore(replybox, null);
replybox.src = replyUrlParts[0]
+ (commentId ? '&parentID=' + commentId : '')
+ '#' + replyUrlParts[1];
replyParent = commentId;
}
};

var hash = (window.location.hash || '#').substring(1);
var startThread, targetComment;
if (/^comment-form_/.test(hash)) {
startThread = hash.substring('comment-form_'.length);
} else if (/^c[0-9]+$/.test(hash)) {
targetComment = hash.substring(1);
}

// Configure commenting API:
var configJso = {
'maxDepth': config.maxThreadDepth
};
var provider = {
'id': config.postId,
'data': items,
'loadNext': paginator,
'hasMore': hasMore,
'getMeta': getMeta,
'onReply': onReply,
'rendered': true,
'initComment': targetComment,
'initReplyThread': startThread,
'config': configJso,
'messages': msgs
};

var render = function() {
if (window.goog && window.goog.comments) {
var holder = document.getElementById('comment-holder');
window.goog.comments.render(holder, provider);
}
};

// render now, or queue to render when library loads:
if (window.goog && window.goog.comments) {
render();
} else {
window.goog = window.goog || {};
window.goog.comments = window.goog.comments || {};
window.goog.comments.loadQueue = window.goog.comments.loadQueue || [];
window.goog.comments.loadQueue.push(render);
}
})();
// ]]>
</script>
</b:includable>
Demikianlah saya coba memahami apa yang ingin disampaikan oleh kode-kode (X)HTML Blogger. Mohon koreksinya jika ada kesalahan pemahaman. Selamat berpusing ria dan Happy Blogging :)

Sunday, February 26, 2012

Fix Problem Reply Threaded Comments

Beberapa hari yang lalu Blogger Tune-Up mencoba memasang Threaded Comments pada blog ini, pada mulanya semua berjalan lancar-lancar saja. Namun setelah beberapa hari pemasangan, tombol Reply/Balas tidak bereaksi apa-apa ketika di klik. Setelah menunggu satu hari dengan berharap semua membaik, namun tombol reply/balas untuk fasilitas komentar tetap tidak bekerja. Saya mengira hal ini terjadi karena pihak pengembang Blogger sedang memperbaiki fasilitas ini, namun ternyata blogger lain pun mengalami hal sama. Sempat menyampaikan pesan melalui twitter, melalui bantuan Blogger tapi tetap tidak ada solusi, maka Blogger Tune-Up mencoba mencari solusinya sendiri. Setelah melakukan pencarian diberbagai blog, terutama blog resmi dari pihak Google, maka dibawah ini ada 2 (dua) solusi yang ditemukan untuk memperbaiki masalah (Fix Problem Reply Threaded Comments) tombol reply/balas threaded comments dan 1 (satu) solusi yang ditemukan Blogger Tune-Up.
Fix Problem Reply Threaded Comments

Perbaikan Masalah Threaded Comments

Solusi I (Dari Blogger Buzz)
Solusi ini dijelaskan secara langsung melalui blog resmi Blogger Buzz
[Untuk Antar Muka Blogger Lawas]
Langkah 1
Login ke Blogger
Langkah 2
Masuk ke Setelan - Komentar
Langkah 3
Cari "Penempatan Formulir Komentar" kemudian pilih "Disemat di bawah entri"
Langkah 4
Klik tombol "Simpan Setelan"
Langkah 5
Masuk ke Setelan - Feed Situs
Langkah 6
Cari "Izinkan Feed Blog" dan pilih "Penuh"
Langkah 7
Klik tombol "Simpan Setelan"

[Untuk Antar Muka Blogger Baru]
Langkah 1
Login ke Blogger
Langkah 2
Masuk ke Setelan - Pos dan komentar
Langkah 3
Cari Komentar - Lokasi Komentar dan pilih "Tersemat"
Langkah 4
Klik tombol "Simpan setelan"
Langkah 5
Cari Lainnya - Umpan situs dan pada Bolehkan Umpan Blog pilih "Penuh"
Langkah 6
Klik tombol "Simpan setelan"

Solusi II (Dari Blogger Developers Network)
Solusi ini dijelaskan secara langsung melalui blog resmi Blogger Developers Network
Langkah 1
Login ke Blogger
Langkah 2
Masuk ke Template - Edit HTML dan aktifkan "Expand Template Widget"
(Backup dulu template untuk menjaga dari kemungkinan kesalahan pengeditan)
Langkah 3
Cari kode dibawah ini (kode ini lebih dari satu):
<b:if cond='data:blog.pageType == &quot;static_page&quot;'>
<b:include data='post' name='comments'/>
</b:if>
<b:if cond='data:blog.pageType == &quot;item&quot;'>
<b:include data='post' name='comments'/>
</b:if>
Langkah 4
Ganti (replace) kode diatas dengan kode dibawah ini:
<b:if cond='data:blog.pageType == &quot;static_page&quot;'>
<b:if cond='data:post.showThreadedComments'>
<b:include data='post' name='threaded_comments'/>
<b:else/>
<b:include data='post' name='comments'/>
</b:if>
</b:if>
<b:if cond='data:blog.pageType == &quot;item&quot;'>
<b:if cond='data:post.showThreadedComments'>
<b:include data='post' name='threaded_comments'/>
<b:else/>
<b:include data='post' name='comments'/>
</b:if>
</b:if>
Langkah 5
Simpan Template

Solusi III (Ditemukan Blogger Tune-Up)
Ini solusi terakhir Blogger Tune-Up setelah melakukan 2 solusi diatas tapi tetap saja Reply Threaded Comments tidak bisa bekerja, yaitu dengan mengganti script JSON Threaded Comments baru dengan script JSON Threaded Comments lama.
Langkah 1
Login ke Blogger
Langkah 2
Masuk ke Template - Edit HTML dan aktifkan "Expand Template Widget"
(Backup dulu template untuk menjaga dari kemungkinan kesalahan pengeditan)
Langkah 3
Cari kode dibawah ini:
<script type='text/javascript'>
(function() {
var items = <data:post.commentJso/>;
var msgs = <data:post.commentMsgs/>;
var postId = &#39;<data:post.id/>&#39;;
var feed = &#39;<data:post.commentFeed/>&#39;;
var authorName = &#39;<data:post.author/>&#39;;
var authorUrl = &#39;<data:post.authorUrl/>&#39;;
var blogId = &#39;<data:top.id/>&#39;;
var baseUri = &#39;<data:post.commentBase/>&#39;;

// <![CDATA[
feed += '?alt=json&v=2&orderby=published&reverse=false&max-results=50';
var cursor = null;
if (items && items.length > 0) {
cursor = parseInt(items[items.length - 1].timestamp) + 1;
}

var bodyFromEntry = function(entry) {
if (entry.gd$extendedProperty) {
for (var k in entry.gd$extendedProperty) {
if (entry.gd$extendedProperty[k].name == 'blogger.contentRemoved') {
return '<span class="deleted-comment">' + entry.content.$t + '</span>';
}
}
}
return entry.content.$t;
}

var parse = function(data) {
cursor = null;
var comments = [];
if (data && data.feed && data.feed.entry) {
for (var i = 0, entry; entry = data.feed.entry[i]; i++) {
var comment = {};
// comment ID, parsed out of the original id format
var id = /blog-(\d+).post-(\d+)/.exec(entry.id.$t);
comment.id = id ? id[2] : null;
comment.body = bodyFromEntry(entry);
comment.timestamp = Date.parse(entry.published.$t) + '';
if (entry.author && entry.author.constructor === Array) {
var auth = entry.author[0];
if (auth) {
comment.author = {
name: (auth.name ? auth.name.$t : undefined),
profileUrl: (auth.uri ? auth.uri.$t : undefined),
avatarUrl: (auth.gd$image ? auth.gd$image.src : undefined)
};
}
}
if (entry.link) {
if (entry.link[2]) {
comment.link = comment.permalink = entry.link[2].href;
}
if (entry.link[3]) {
var pid = /.*comments\/default\/(\d+)\?.*/.exec(entry.link[3].href);
if (pid && pid[1]) {
comment.parentId = pid[1];
}
}
}
comment.deleteclass = 'item-control blog-admin';
if (entry.gd$extendedProperty) {
for (var k in entry.gd$extendedProperty) {
console.log(entry.gd$extendedProperty[k].name + ' - ' + entry.gd$extendedProperty[k].value);
if (entry.gd$extendedProperty[k].name == 'blogger.itemClass') {
comment.deleteclass += ' ' + entry.gd$extendedProperty[k].value;
}
}
}
comments.push(comment);
}
}
return comments;
};

var paginator = function(callback) {
if (hasMore()) {
var url = feed;
if (cursor) {
url += '&published-min=' + new Date(cursor).toISOString();
}
window.bloggercomments = function(data) {
var parsed = parse(data);
cursor = parsed.length < 50 ? null
: parseInt(parsed[parsed.length - 1].timestamp) + 1
callback(parsed);
window.bloggercomments = null;
}
url += '&callback=bloggercomments';
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
};
var hasMore = function() {
return !!cursor;
};
var getMeta = function(key, comment) {
if ('iswriter' == key) {
var matches = !!comment.author
&& comment.author.name == authorName
&& comment.author.profileUrl == authorUrl;
return matches ? 'true' : '';
} else if ('deletelink' == key) {
return baseUri + '/delete-comment.g?blogID=' + blogId + '&postID=' + comment.id;
} else if ('deleteclass' == key) {
return comment.deleteclass;
}
return '';
};

var replybox = null;
var replyUrlParts = null;
var replyParent = undefined;

var onReply = function(commentId, domId) {
if (replybox == null) {
// lazily cache replybox, and adjust to suit this style:
replybox = document.getElementById('comment-editor');
if (replybox != null) {
replybox.height = '250px';
replybox.style.display = 'block';
replyUrlParts = replybox.src.split('#');
}
}
if (replybox && (commentId !== replyParent)) {
document.getElementById(domId).insertBefore(replybox, null);
replybox.src = replyUrlParts[0]
+ (commentId ? '&parentID=' + commentId : '')
+ '#' + replyUrlParts[1];
replyParent = commentId;
}
};

var tok = 'comment-form_';
var hash = window.location.hash || '';
var startThread = hash.indexOf(tok) == 1 ? hash.substring(tok.length + 1) : undefined;

// Configure commenting API:
var configJso = {
'maxDepth': 2
};
var provider = {
'id': postId,
'data': items,
'loadNext': paginator,
'hasMore': hasMore,
'getMeta': getMeta,
'onReply': onReply,
'rendered': true,
'initReplyThread': startThread,
'config': configJso,
'messages': msgs
};

var render = function() {
if (window.goog && window.goog.comments) {
var holder = document.getElementById('comment-holder');
window.goog.comments.render(holder, provider);
}
};

// render now, or queue to render when library loads:
if (window.goog && window.goog.comments) {
render();
} else {
window.goog = window.goog || {};
window.goog.comments = window.goog.comments || {};
window.goog.comments.loadQueue = window.goog.comments.loadQueue || [];
window.goog.comments.loadQueue.push(render);
}
})();
// ]]>
</script>
Langkah 4
Ganti (replace) kode diatas dengan kode dibawah ini:
<script type='text/javascript'>
(function() {
var items = <data:post.commentJso/>;
var msgs = <data:post.commentMsgs/>;
var config = <data:post.commentConfig/>;

// <![CDATA[
var cursor = null;
if (items && items.length > 0) {
cursor = parseInt(items[items.length - 1].timestamp) + 1;
}

var bodyFromEntry = function(entry) {
if (entry.gd$extendedProperty) {
for (var k in entry.gd$extendedProperty) {
if (entry.gd$extendedProperty[k].name == 'blogger.contentRemoved') {
return '<span class="deleted-comment">' + entry.content.$t + '</span>';
}
}
}
return entry.content.$t;
}

var parse = function(data) {
cursor = null;
var comments = [];
if (data && data.feed && data.feed.entry) {
for (var i = 0, entry; entry = data.feed.entry[i]; i++) {
var comment = {};
// comment ID, parsed out of the original id format
var id = /blog-(\d+).post-(\d+)/.exec(entry.id.$t);
comment.id = id ? id[2] : null;
comment.body = bodyFromEntry(entry);
comment.timestamp = Date.parse(entry.published.$t) + '';
if (entry.author && entry.author.constructor === Array) {
var auth = entry.author[0];
if (auth) {
comment.author = {
name: (auth.name ? auth.name.$t : undefined),
profileUrl: (auth.uri ? auth.uri.$t : undefined),
avatarUrl: (auth.gd$image ? auth.gd$image.src : undefined)
};
}
}
if (entry.link) {
if (entry.link[2]) {
comment.link = comment.permalink = entry.link[2].href;
}
if (entry.link[3]) {
var pid = /.*comments\/default\/(\d+)\?.*/.exec(entry.link[3].href);
if (pid && pid[1]) {
comment.parentId = pid[1];
}
}
}
comment.deleteclass = 'item-control blog-admin';
if (entry.gd$extendedProperty) {
for (var k in entry.gd$extendedProperty) {
if (entry.gd$extendedProperty[k].name == 'blogger.itemClass') {
comment.deleteclass += ' ' + entry.gd$extendedProperty[k].value;
}
}
}
comments.push(comment);
}
}
return comments;
};

var paginator = function(callback) {
if (hasMore()) {
var url = config.feed + '?alt=json&v=2&orderby=published&reverse=false&max-results=50';
if (cursor) {
url += '&published-min=' + new Date(cursor).toISOString();
}
window.bloggercomments = function(data) {
var parsed = parse(data);
cursor = parsed.length < 50 ? null
: parseInt(parsed[parsed.length - 1].timestamp) + 1
callback(parsed);
window.bloggercomments = null;
}
url += '&callback=bloggercomments';
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
};
var hasMore = function() {
return !!cursor;
};
var getMeta = function(key, comment) {
if ('iswriter' == key) {
var matches = !!comment.author
&& comment.author.name == config.authorName
&& comment.author.profileUrl == config.authorUrl;
return matches ? 'true' : '';
} else if ('deletelink' == key) {
return config.baseUri + '/delete-comment.g?blogID='
+ config.blogId + '&postID=' + comment.id;
} else if ('deleteclass' == key) {
return comment.deleteclass;
}
return '';
};

var replybox = null;
var replyUrlParts = null;
var replyParent = undefined;

var onReply = function(commentId, domId) {
if (replybox == null) {
// lazily cache replybox, and adjust to suit this style:
replybox = document.getElementById('comment-editor');
if (replybox != null) {
replybox.height = '250px';
replybox.style.display = 'block';
replyUrlParts = replybox.src.split('#');
}
}
if (replybox && (commentId !== replyParent)) {
document.getElementById(domId).insertBefore(replybox, null);
replybox.src = replyUrlParts[0]
+ (commentId ? '&parentID=' + commentId : '')
+ '#' + replyUrlParts[1];
replyParent = commentId;
}
};

var hash = (window.location.hash || '#').substring(1);
var startThread, targetComment;
if (/^comment-form_/.test(hash)) {
startThread = hash.substring('comment-form_'.length);
} else if (/^c[0-9]+$/.test(hash)) {
targetComment = hash.substring(1);
}

// Configure commenting API:
var configJso = {
'maxDepth': config.maxThreadDepth
};
var provider = {
'id': config.postId,
'data': items,
'loadNext': paginator,
'hasMore': hasMore,
'getMeta': getMeta,
'onReply': onReply,
'rendered': true,
'initComment': targetComment,
'initReplyThread': startThread,
'config': configJso,
'messages': msgs
};

var render = function() {
if (window.goog && window.goog.comments) {
var holder = document.getElementById('comment-holder');
window.goog.comments.render(holder, provider);
}
};

// render now, or queue to render when library loads:
if (window.goog && window.goog.comments) {
render();
} else {
window.goog = window.goog || {};
window.goog.comments = window.goog.comments || {};
window.goog.comments.loadQueue = window.goog.comments.loadQueue || [];
window.goog.comments.loadQueue.push(render);
}
})();
// ]]>
</script>
Langkah 5
Simpan Template

Demikian tahapan demi tahapan untuk mengaktifkan kembali (Reply) Threaded Comments yang tidak bisa bekerja dengan baik. Mudah-mudahan bisa memberikan solusi pada blog anda yang mengalami masalah sama. Selamat mencoba dan semoga berhasil. Happy Blogging :)

Saturday, February 25, 2012

Kumpulan Link Untuk Belajar CSS

Kumpulan Link Untuk Belajar CSS ~ Berikut ini adalah link untuk kita dapat belajar CSS dan juga untuk melihat demonya. Silahkan anda copy di address browser anda untuk melihat Web nya.

http://cssbeauty.com/
http://cssremix.com/
http://bestwebgallery.com/
http://www.cssmania.com/
http://www.cssdrive.com/
http://www.unmatchedstyle.com/
http://cssbased/
http://cssbloom.com/
http://www.cssclip.com/
http://www.csscollection.com/
http://css-demo.com/
http://cssdrive.com/
http://www.csselite.com/
http://www.cssflavor.com/
http://www.cssgalaxy.com/
http://cssglobe.com/
http://www.cssimport.com/
http://www.cssimpress.com/
http://cssmess.com/
http://www.cssprincess.com/
http://www.cssreboot.com/
http://www.cssshowcase.com/
http://csssmoothoperator.com/
http://cssspot.com/
http://www.designmeltdown.com/
http://www.lightondark.com/
http://www.cssliquid.com/
http://www.mostinspired.com/
http://www.submitcss.com/
http://www.dailyslurp.com/
Selamat belajar Guys.

Ref. alkode.com

Friday, February 3, 2012

Blogger Tune-Up Refresh

Alhamdulillah. Setelah beberapa hari melakukan pertimbangan yang cukup berat, akhirnya Blogger Tune-Up mengambil keputusan untuk mengganti wajah. Biasanya desain yang saya buat selalu dominan warna gelap, namun kali ini saya ingin sesuatu yang yang berbeda, lebih cerah, redup, rapi namun tetap kalem. Maka seperti inilah tampilan akhir Blogger Tune-Up.
Namun demikian, desain Blogger Tune-Up ini masih jauh dari sempurna. Dan saya mengambil resiko untuk memperbaiki artikel demi artikel yang telah ditulis, hal ini karena desain yang dibuat total merupakan kode baru, tanpa mengikuti kode lama. Walau demikian hal itu tidak bisa ditawar. Maka jika sahabat blogger menemukan artikel yang tampil berantakan, saya mohon ma'af sekali karena keterbatasan koneksi dan waktu, tapi tetap semua itu akan segera diperbaiki. Dan saya mengucapkan "Selamat Datang di Blogger Tune-Up Refresh"

Banjarsari, 04 Februari 2012 [00.32 WIB]

Hendriono