Selasa, 11 Juni 2013

Performa Widget Random Post

Saya harus mengatakan bahwa widget random post itu sangat buruk dalam hal performa. Kerja mereka sangat lambat dan membuang-buang tenaga, karena mereka bekerja dengan cara memanggil feed posting berukuran sangat besar! Kemudian mereka hanya akan menampilkannya sebagian saja secara acak:

<script src='/feeds/posts/default?alt=json-in-script&max-results=99999&callback=randomPosts'></script>

Bagi Anda para pemakai mungkin tidak pernah tahu mengenai ini, tetapi sebenarnya setiap pemakai widget random post —termasuk juga — tanpa sadar telah menerima beban begitu besar. Ketika Anda membuka halaman tunggal dimana terdapat widget random post di dalamnya, pada saat yang bersamaan Anda juga sebenarnya sedang membuka semua posting yang telah Anda terbitkan dalam satu waktu. Mengapa? Karena parameter max-results pada feed menunjukkan angka 99999 yang artinya bahwa semua feed posting akan “diusahakan” untuk dipanggil (diusahakan, karena jumlah posting yang kita terbitkan tidak mungkin mencapai angka sebesar itu).

Mengacak Indeks Permulaan

Saya menyadari terdapat sebuah peluang untuk menciptakan widget random post yang lebih ringan dan cepat dimuat. Dibandingkan memuat semua posting dan memilah beberapa posting secara acak untuk ditampilkan, akan lebih baik jika kita mengacak nilai start-index pada feed sehingga posting yang dimuat bisa dimulai dari urutan sembarang:

function randomPosts(json) {
// Ubah JSON menjadi HTML...
}

var startIndex = Math.round(Math.random() * 9999); // Membuat angka acak
document.write('<scr' + 'ipt src="/feeds/posts/summary?alt=json-in-script&start-index=' + startIndex + '&max-results=7&callback=randomPosts"></scr' + 'ipt>');

Ada satu masalah kecil yang mungkin akan mengganggu. Jika angka acak yang tercipta nilainya lebih besar dari total posting blog Anda saat ini, maka widget random post ini tidak akan bekerja. Oleh karena itu kita harus membatasi angka acak yang tercipta, yaitu tidak boleh kurang dari 1 dan tidak boleh lebih dari “total posting dikurangi jumlah posting yang ingin ditampilkan”.

Untuk mendapatkan jumlah posting secara keseluruhan kita bisa mengambilnya melalui objek json.feed.openSearch$totalResults.$t:

// Konfigurasi
var homePage = 'http://nama_blog.blogspot.com',
maxResults = 7;

// Fungsi untuk menggenerasikan angka acak dengan batasan minimal dan maksimal
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}

// Mendapatkan indeks pemulai yang aman untuk memanggil feed utama
function createRandomPostsStartIndex(json) {
// Buat angka acak dengan nilai tidak boleh kurang dari `1` dan tidak boleh lebih dari `total posting - posting yang ingin ditampilkan`
var startIndex = getRandomInt(1, (json.feed.openSearch$totalResults.$t - maxResults));
// Tampilkan pesan log
console.log('Get the post feed start from ' + startIndex + ' until ' + (startIndex + maxResults));
}

document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&max-results=0&callback=createRandomPostsStartIndex"></scr' + 'ipt>');

Muat ulang widget Anda berkali-kali. Seharusnya Anda akan melihat pesan log indeks pemanggilan posting yang berubah-ubah seperti ini:

Showing log message to indicate random number for feed start index purpose
Angka acak yang Saya tandai akan kita manfaatkan untuk memanggil indeks feed secara acak.

Kita akan menggunakan angka acak itu sebagai angka pemulai pada parameter start-index seperti ini:

function createRandomPostsStartIndex(json) {
var startIndex = getRandomInt(1, (json.feed.openSearch$totalResults.$t - maxResults));
document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&orderby=updated&start-index=' + startIndex + '&max-results=' + maxResults + '&callback=randomPosts"></scr' + 'ipt>');
}

Pada bagian akhir URL feed terdapat parameter callback=randomPosts. randomPosts di sini merupakan fungsi utama yang akan kita buat untuk mengubah JSON Blogger menjadi widget:

function randomPosts(json) {
var link, ct = document.getElementById('random-post-container'),
entry = json.feed.entry,
skeleton = "<ul>";
for (var i = 0, len = entry.length; i < len; i++) {
for (var j = 0, jen = entry[i].link.length; j < jen; j++) {
link = (entry[i].link[j].rel == "alternate") ? entry[i].link[j].href : '#';
}
skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
}
ct.innerHTML = skeleton + '</ul>';
}

Sentuhan akhir, acak urutan posting yang ditampilkan menggunakan fungsi ini sehingga daftar posting yang ditampilkan nantinya akan semakin acak urutannya:

// Fungsi untuk mengacak array
function shuffleArray(arr) {
var i = arr.length, j, temp;
if (i === 0) return false;
while (--i) {
j = Math.floor(Math.random() * (i + 1));
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
}

// Widget
function randomPosts(json) {
var link, ct = document.getElementById('random-post-container'),
entry = shuffleArray(json.feed.entry),
skeleton = "<ul>";
for (var i = 0, len = entry.length; i < len; i++) {
for (var j = 0, jen = entry[i].link.length; j < jen; j++) {
link = (entry[i].link[j].rel == "alternate") ? entry[i].link[j].href : '#';
}
skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
}
ct.innerHTML = skeleton + '</ul>';
}

Hasil Akhir

<div id='random-post-container'>Memuat...</div>

<script>
//<![CDATA[
// Feed configuration
var homePage = 'http://nama_blog.blogspot.com',
maxResults = 7,
containerId = 'random-post-container';
// Function to generate random number limited from `min` to `max`
// Used to create a valid and safe random feed `start-index`
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Function to shuffle arrays
// Used to randomize order of the generated JSON feed
function shuffleArray(arr) {
var i = arr.length, j, temp;
if (i === 0) return false;
while (--i) {
j = Math.floor(Math.random() * (i + 1));
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
}
// Get a random start index
function createRandomPostsStartIndex(json) {
var startIndex = getRandomInt(1, (json.feed.openSearch$totalResults.$t - maxResults));
// console.log('Get the post feed start from ' + startIndex + ' until ' + (startIndex + maxResults));
document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&orderby=updated&start-index=' + startIndex + '&max-results=' + maxResults + '&callback=randomPosts"></scr' + 'ipt>');
}
// Widget's main function
function randomPosts(json) {
var link, ct = document.getElementById(containerId),
entry = shuffleArray(json.feed.entry),
skeleton = "<ul>";
for (var i = 0, len = entry.length; i < len; i++) {
for (var j = 0, jen = entry[i].link.length; j < jen; j++) {
link = (entry[i].link[j].rel == "alternate") ? entry[i].link[j].href : '#';
}
skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
}
ct.innerHTML = skeleton + '</ul>';
}
document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&max-results=0&callback=createRandomPostsStartIndex"></scr' + 'ipt>');
//]]>
</script>

Ketika Posting Acak Dikirim Secara Langsung oleh Server

Dalam sebuah forum Saya pernah menemukan seseorang yang mencoba menyarankan Blogger untuk menyertakakan parameter orderby baru berupa random dan popular. Saya pikir ini adalah ide yang bagus. Karena berdasarkan dokumentasi, kalau tidak salah hanya ada dua macam nilai untuk parameter orderby, yaitu published dan updated:

http://nama_blog.blogspot.com/feeds/posts/summary?orderby=published
http://nama_blog.blogspot.com/feeds/posts/summary?orderby=updated

Jika suatu saat Blogger merealisasikan masukan dari pengguna ini, maka kita tidak perlu lagi menerapkan hack semacam ini. Cukup dengan membuat widget recent post biasa, kita bisa mengubah parameter URL feed dari orderby=published menjadi orderby=random untuk menciptakan widget random post. Tapi kasus ini masih dalam tahap “seandainya”.

Label: , , , ,


Selasa, 02 April 2013

Widget Blogger untuk Menampilkan Daftar Posting Terbaru dalam Label-Label yang Spesifik

Screenshot of multiple recent post widget for Blogger.

Judulnya sangat panjang karena Saya sendiri tidak tahu harus memberi nama apa kepada widget ini. Pada intinya widget ini adalah widget recent post biasa, namun memiliki kemampuan untuk menggandakan diri agar bisa menampilkan daftar posting terbaru lebih dari satu buah berdasarkan label yang spesifik:

Pada awalnya Saya membuat widget ini agar bisa menampilkan posting-posting terbaru dari blog tunggal saja, namun mengingat ada juga beberapa blog yang suka menampilkan daftar posting dari blog lain, jadi sekarang widget ini tidak hanya berfungsi untuk menampilkan daftar posting dari satu feed blog, melainkan juga bisa menampilkan daftar posting dari feed blog Blogger yang lain.

Pemasangan Widget

Masuk ke halaman Tata Letak kemudian tambahkan sebuah widget HTML/JavaScript. Salin kode ini dan letakkan di dalam formulirnya:

<link rel="stylesheet" type="text/css" scoped="scoped" href="http://blogger-json-experiment.googlecode.com/svn/resources/multi-feed/multi-feed.min.css"/>
<style type="text/css" scoped="scoped">
/* Custom CSS */
.list-entries {
margin:5px;
width:270px; /* Lebar widget */
float:left;
font-size:11px;
}
</style>

<div id="feed-list-container"></div>
<div style="clear:both;"></div>

<script type="text/javascript">
var multiFeed = {
feedsUri: [
{
name: "Judul Widget 1",
url: "http://nama_blog-1.blogspot.com",
tag: "JQuery"
},
{
name: "Judul Widget 2",
url: "http://nama_blog-2.blogspot.com",
tag: "CSS"
},
{
name: "Judul Widget 1",
url: "http://nama_blog-3.blogspot.com",
tag: "Widget"
}
],
numPost: 4,
showThumbnail: true,
showSummary: true,
summaryLength: 80,
titleLength: "auto",
thumbSize: 72,
containerId: "feed-list-container",
readMore: {
text: "Selengkapnya",
endParam: "?max-results=20"
}
};
</script>
<script type="text/javascript" src="http://blogger-json-experiment.googlecode.com/svn/resources/multi-feed/multi-feed.min.js"></script>

Konfigurasi

OpsiKeterangan
feedsUriBerupa array objek dimana di dalamnya terdapat beberapa data berupa name, url dan tag: name digunakan untuk menentukan judul widget, url digunakan untuk menentukan URL feed, tag digunakan untuk menentukan tag/label posting yang ingin ditampilkan.
numPostDigunakan untuk menentukan jumlah posting yang ingin ditampilkan
showThumbnailGanti nilainya menjadi false untuk menyembunyikan gambar posting
showSummaryGanti nilainya menjadi false untuk menyembunyikan ringkasan posting
summaryLengthDigunakan untuk menentukan jumlah karakter ringkasan posting
titleLengthDigunakan untuk memotong karakter judul posting yang terlalu panjang. Ganti nilainya menjadi angka untuk memotong karakter sebanyak angka tersebut, atau set sebagai "auto" untuk membiarkan judul posting sebagai apa adanya
thumbSizeDigunakan untuk menentukan ukuran thumbnail posting
containerIdDigunakan untuk menentukan ID kontainer yang akan digunakan untuk memuat widget
readMoreTerdiri dari text dan endParam. text digunakan untuk menentukan label tautan “Baca Selengkapnya” yang terletak di bawah masing-masing widget dan endParam digunakan untuk menentukan parameter akhir tautan tersebut (abaikan jika tidak perlu).

Opsi feedsUri merupakan array objek dimana setiap objek akan mewakili masing-masing widget:

// Tiga objek = tiga widget
feedsUri: [{}, {}, {}]

Setiap objek berisi beberapa data yang digunakan untuk menentukan judul widget, sumber URL dan tag/kategori posting spesifik:

feedsUri: [
{
name: "Judul Widget 1",
url: "http://nama_blog-1.blogspot.com",
tag: "Blogger"
},
{
name: "Judul Widget 2",
url: "http://nama_blog-2.blogspot.com",
tag: "WordPress"
},
{
name: "Judul Widget 3",
url: "http://nama_blog-3.blogspot.com",
tag: "SEO"
}
]

Setelah semua konfigurasi sudah diatur dengan benar, klik Simpan Widget. Modifikasi tampilan bisa dilakukan di dalam tag <style> yang Saya tambahkan pada widget di atas.

Kode Sumber

  1. multi-feed.css
  2. multi-feed.min.css
  3. multi-feed.js
  4. multi-feed.min.js

Label: , , , ,


Senin, 12 November 2012

Memperbaharui Pemuatan JSON berdasarkan Event Scroll

Sebuah kumpulan data dalam daftar akan menampilkan indikator sedang memuat dan akan memulai pemuatan data baru saat jarak gulungan telah mencapai titik maksimal
Sebuah kumpulan data dalam daftar akan menampilkan indikator sedang memuat dan akan memulai pemuatan data baru saat jarak gulungan telah mencapai titik maksimal

Metode ini masih sama dengan metode-metode penundaan pemuatan JSON yang biasa Saya lakukan untuk widget-widget Saya yaitu dengan cara menyisipkan script callback secara tidak langsung ke dalam area <head> dengan ID tertentu. Kemudian, jika Saya ingin memperbaharui muatan JSON yang sudah ada, Saya tinggal menyingkirkan script callback yang lama kemudian menggantinya dengan duplikat baru dengan parameter yang sudah diperbaharui.

Bayangkan saja bahwa sudah terdapat sebuah script callback dengan ID foo di dalam area <head> seperti ini:

...
...
<script id="foo" src="../feeds/posts/summary?alt=json-in-script&callback=functionName" type="text/javascript"></script>
</head>

Kemudian Saya akan menyingkirkannya dengan cara menangkap ID elemen tersebut sebagai awalan untuk menyeleksi node induknya:

var a = document.getElementById('foo');
var parent = a.parentNode; // Mendapatkan elemen induk dari `foo`

Kemudian, dari induk tersebut Saya akan menyingkirkan elemen itu sendiri:

var a = document.getElementById('foo');
var parent = a.parentNode; // Mendapatkan elemen induk dari `foo`
parent.removeChild(a); // Singkirkan `foo` dari `parent`

Di sini, kita akan melakukan pekerjaan di atas berdasarkan event onscroll dengan batasan akhir berupa jarak gulungan maksimal kontainer. Kode di bawah ini akan menjalankan fungsi bernama myFunction() berdasarkan event onscroll dengan syarat jarak gulungan telah mencapai titk maksimal:

elem.onscroll = function() {
if ((this.scrollTop + this.offsetHeight) == inner.offsetHeight) {
myFunction();
}
};

Dimana elem adalah objek berupa kontainer.

Memulai Pekerjaan

Markup HTML

Kita mulai dengan pembuatan markup HTML sederhana seperti ini:

<div id="result-container" style="width:400px;height:400px;overflow:auto;">
<ol></ol>
<span class="loading">Memuat...</span>
</div>

Elemen #result-container digunakan sebagai area scroll, elemen ol digunakan sebagai kontainer data yang nantinya akan dihasilkan dari JSON, dan elemen span.loading digunakan sebagai indikator sedang memuat.

Membangun JavaScript

Pertama-tama kita buat script untuk memparse data JSON menjadi elemen HTML. Sederhana, seperti script recent post biasa yang Saya beri nama grabList(), dan hanya akan menghasilkan elemen <li> dengan tautan dan judul posting di dalamnya:

var elem = document.getElementById('result-container'), // Mendapatkan elemen `#result-container`
inner = elem.getElementsByTagName('ol')[0], // Mendapatkan elemen `ol` pertama
loading = elem.getElementsByTagName('span')[0]; // Mendapatkan elemen `span` pertama (dalam hal ini adalah elemen indikator sedang memuat)

// Bangun sebuah script untuk menampilkan daftar posting
function grabList(json) {

var list = json.feed.entry, link, skeleton = "";

// Jalankan loop hanya jika data JSON masih ada/dapat didefinisikan
if (list !== undefined) {
for (var i = 0; i < list.length; i++) {
for (var j = 0; j < list[i].link.length; j++) {
if (list[i].link[j].rel == "alternate") {
link = list[i].link[j].href; // Mendapatkan URL posting
}
}

// Bangun beberapa elemen `<li>` yang berisi tautan dan judul posting...
skeleton += '<li><a href="' + link + '">' + list[i].title.$t + '</a></li>';

}

// ... kemudian sisipkan elemen tersebut ke dalam elemen `<ol>`
inner.innerHTML += skeleton;

// dan sembunyikan indikator sedang memuat.
loading.style.display = "none";

} else {

// Jika data JSON sudah tidak ada (list == undefined), tambahkan kelas baru kepada elemen indikator sedang memuat dengan nilai `the-end`
loading.className += ' the-end';

// kemudian ganti teks indikator sedang memuat dengan kata `Habis`
loading.textContent = 'Habis';

}
}

Setelah itu buat sebuah fungsi untuk memuat script callback secara tidak langsung. Tambahkan dua buah parameter untuk menangani start-index dan max-results:

function updateScript(i, max) {
var head = document.getElementsByTagName('head')[0],
script = document.createElement('script');
script.type = 'text/javascript';
script.id = 'load-on-scroll-end';
script.src = 'http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&start-index=' + i + '&max-results=' + max + '&callback=grabList';
head.appendChild(script);
}

// Jalankan fungsi!
updateScript(1, 25);

Fungsi di atas akan menyisipkan sebuah script callback JSON Blogger dengan nilai parameter start-index berupa 1 dan max-results berupa 25, sehingga sebuah elemen <script> dengan parameter yang sudah diatur akan disipkan ke dalam area <head> secara tidak langsung seperti ini:

...
...
...
<script id="load-on-scroll-end" src="http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&start-index=1&max-results=25&callback=grabList" type="text/javascript"></script>
</head>

Memperbaharui JSON Berdasarkan Jarak Maksimal Gulungan Area

Setelah itu kita tambahkan sebuah kondisional untuk menyingkirkan script callback lama jika script tersebut ada. Tidak perlu membuat fungsi baru, cukup gunakan fungsi tadi agar kita bisa menggunakannya untuk dua hal sekaligus, yaitu menyisipkan script baru dan/atau menyingkirkan script lama (jika ada):

function updateScript(i, max) {
var head = document.getElementsByTagName('head')[0],
script = document.createElement('script');
script.type = 'text/javascript';
script.id = 'load-on-scroll-end';
script.src = 'http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&start-index=' + i + '&max-results=' + max + '&callback=grabList';
if (document.getElementById('load-on-scroll-end')) {
var oldScript = document.getElementById('load-on-scroll-end');
oldScript.parentNode.removeScript(oldScript);
}

head.appendChild(script);
}

updateScript(1, 25); // Jalankan fungsi!

Karena kita juga akan menjalankan fungsi updateScript() berdasarkan event onscroll, maka kita juga harus memasukkan updateScript() ke dalam event yang Saya tuliskan pertama kali:

elem.onscroll = function() {
if ((this.scrollTop + this.offsetHeight) == inner.offsetHeight) {
// Muat ulang JSON Blogger dengan `start-index` yang baru melalui parameter `i`
updateScript(i, 25);
// Kemudian tampilkan indikator sedang memuat
loading.style.display = "block";
}
};

Parameter i haru dinamis, dan harus bisa bertambah setiap kali gulungan kontainer berakhir. Untuk membuatnya menjadi dinamis, kita akan menggunakan variabel awalan dengan nilai 0, kemudian kita tingkatkan nilainya di dalam event onscroll setiap kali jarak gulungan telah mencapai titik maksimal:

var start = 0;
elem.onscroll = function() {
if ((this.scrollTop + this.offsetHeight) == inner.offsetHeight) {

// Tingkatkan nilai `start` dengan 1 (dari `start = 0` menjadi `start = 1`, `start = 2`, dst...)
start++;

// Muat ulang JSON Blogger dengan `start-index` yang telah diperbaharui...
// ... melalui `start` yang nilainya dikalikan dengan 25
updateScript(start*25, 25); // => dari `(1, 25)` menjadi `(25, 25)`, `(50, 25)`, `(75, 25)`, dst...

// Kemudian tampilkan indikator sedang memuat
loading.style.display = "block";

}
};

Produk Final

HTML

<div id="result-container">
<ol></ol>
<span class="loading">Memuat...</span>
</div>

CSS

#result-container {
height:400px;
width:400px;
overflow:auto;
margin:50px auto;
font:normal normal 12px 'Trebuchet MS',Trebuchet,Geneva,Arial,Sans-Serif;
}

#result-container ol {
margin:0 0;
padding:0 0;
background-color:#B5D68C;
}

#result-container li {
margin:0 0;
padding:0 0;
list-style:none;
}

#result-container li:nth-child(even) {background-color:#A2C179}

#result-container li a {
display:block;
padding:5px 10px;
font-weight:bold;
color:#396B18;
text-decoration:none;
}

#result-container li a:hover {
background-color:#396B18;
color:white;
text-decoration:none;
}

#result-container .loading {
display:block;
height:26px;
font:normal bold 11px/26px Arial,Sans-Serif;
color:white;
text-align:center;
background-color:#B75A6F;
border-top:2px solid #222;
}

#result-container .loading.the-end {background-color:#666}

JavaScript

var widget_config = {
home_page: 'http://nama_blog.blogspot.com', // Your blog homepage
container_id: 'result-container', // ID of the result container
script_id: 'load-on-scroll-end-script', // ID of the asynchronous script
max_result: 25, // Max result post at once script loading
end_text: 'Habis' // End text if all posts has been loaded
};

var elem = document.getElementById(widget_config.container_id),
inner = elem.getElementsByTagName('ol')[0],
loading = elem.getElementsByTagName('span')[0],
start = 0, // Dynamic start-index
max = widget_config.max_result;

function grabList(json) {
var list = json.feed.entry, link, skeleton = "";
if (list !== undefined) {
for (var i = 0; i < list.length; i++) {
for (var j = 0; j < list[i].link.length; j++) {
if (list[i].link[j].rel == "alternate") {
link = list[i].link[j].href;
}
}
skeleton += '<li><a href="' + link + '">' + list[i].title.$t + '</a></li>';
}
inner.innerHTML += skeleton; // Insert the list to the container
loading.style.display = "none"; // Hide the loading indicator
} else {
// If the JSON is empty (list == undefined),
// add a new class to the loading indicator called `the-end`
loading.className += ' the-end';
// Replace the loading indicator text into `fully loaded!` for the example
loading.textContent = widget_config.end_text;
}
}

// Make an indirect script loader with two parameters: start-index and max-result post
function updateScript(a, b) {
var head = document.getElementsByTagName('head')[0],
script = document.createElement('script');
script.type = 'text/javascript';
script.id = widget_config.script_id;
script.src = widget_config.home_page + '/feeds/posts/summary?alt=json-in-script&start-index=' + a + '&max-results=' + b + '&callback=grabList';
// If there is an old script in the document...
if (document.getElementById(widget_config.script_id)) {
var oldScript = document.getElementById(widget_config.script_id);
// Remove the old script, and replace with the new one that has an updated start-index value
oldScript.parentNode.removeChild(oldScript);
}
head.appendChild(script);
}

// Start loading the callback script with start-index of 1
updateScript(1, max);

// When the container is being scrolled...
elem.onscroll = function() {
// ... check the scroll distance
if ((this.scrollTop + this.offsetHeight) == inner.offsetHeight) {
// If the distance equal to the height of the inner container...
start++; // Increase the start value by one
// then load the new script with an updated start-index
updateScript(start*max, max);
// and show the loading indicator
loading.style.display = "block";
}
};

Label: , , , , , ,


Minggu, 11 November 2012

Widget Recent Comment dengan Sistem Notifikasi

Pembaharuan 22 April 2013: Mengintegrasikan widget dengan cookies, menambahkan opsi summary dan menyingkirkan opsi tt_id.

Recent Comments Widget for Blogger with Notification System

Widget ini bukan widget recent comment biasa yang biasanya Anda temukan di blog-blog lain, karena widget ini memiliki kemampuan untuk memberitahu kepada administrator bahwa terdapat komentar baru yang telah masuk.

Demonya bisa Anda lihat di sini, namun efeknya hanya akan terlihat apabila terdapat (paling tidak) satu komentar baru yang masuk di blog ini:

Untuk memasang widget ini, pertama-tama tambahkan sebuah elemen halaman HTML/JavaScript. Salin kode ini kemudian letakkan di dalam formulirnya:

<style type="text/css" scoped="scoped">
.cm-outer {
margin:0 auto;
padding:0 0;
font:normal normal 11px Arial,Sans-Serif;
border:1px solid;
border-top:none;
}
.cm-outer li {
margin:0 0;
padding:7px 10px 12px;
list-style:none;
clear:both;
border-top:1px solid;
}
.cm-outer .cm-header {margin:0 0 5px}
.cm-outer .cm-content {overflow:hidden}
.cm-outer img {
display:block;
float:left;
margin:2px 10px 2px 0;
border:4px solid black;
background:#8fa2cb url('http://img1.blogblog.com/img/anon36.png') no-repeat 50% 50%;
overflow:hidden;
}
</style>
<div id="comments-container">Loading&hellip;</div>
<script type="text/javascript">
var cm_config = {
home_page: "http://nama_blog.blogspot.com",
max_result: 7,
t_w: 32,
t_h: 32,
summary: 9999,
new_tab_link: true,
ct_id: "comments-container",
new_cm: " Komentar Baru!",
interval: 30000,
alert: true
};
</script>
<script type="text/javascript" src="http://reader-download.googlecode.com/svn/trunk/recent-comments-script-with-notification.js"></script>

Ganti kode yang Saya beri tanda dengan URL blog Anda kemudian klik Simpan. Hasil maksimal akan terlihat saat terdapat komentar baru yang masuk.

Konfigurasi Widget Lanjutan

Ada beberapa hal yang harus Anda ketahui mengenai konfigurasi lanjutan widget ini:

OpsiKeterangan
home_pageGanti nilainya dengan URL halaman muka blog Anda
max_resultDigunakan untuk menentukan jumlah komentar yang akan ditampilkan pada widget ini
t_wDigunakan untuk menentukan lebar avatar
t_hDigunakan untuk menentukan tinggi avatar
summaryDigunakan untuk menentukan jumlah karakter komentar yang ditampilkan
new_tab_linkJika bernilai true, seluruh tautan yang ada di dalam widget ini akan terbuka di tab/jendela baru saat diklik
tt_idID kontainer total komentar (abaikan jika tidak perlu)
ct_idID kontainer daftar komentar (abaikan jika tidak perlu)
new_cmLabel teks yang akan muncul setelah jumlah komentar (Misalnya: 2 Komentar Baru!)
intervalLihat deskripsi di bawah
alertLihat deskripsi di bawah

Opsi: interval

Digunakan untuk menentukan interval penyegaran feed komentar. Menggunakan satuan milidetik. Nilai 30000 artinya bahwa feed komentar akan disegarkan selama setengah menit sekali untuk memeriksa apakah terdapat komentar baru yang masuk atau tudak. Jika terdapat komentar baru yang masuk, maka widget ini akan menampilkan pesan bahwa komentar baru telah masuk.

Opsi: alert

Ini cuma opsi untuk menentukan gaya pemberitahuan pesan. Jika bernilai false, widget ini akan menampilkan pesan masuknya komentar baru pada title bar peramban seperti gambar 1. Sebaliknya, jika bernilai true, notifikasi akan muncul pada kotak peringatan seperti terlihat pada gambar 2:

Model Notifikasi 1
Model Notifikasi 1: alert: false
Model Notifikasi 2
Model Notifikasi 2: alert: true

Pembaharuan

Sekarang Anda bisa menampilkan teks notifikasi pada elemen HTML tertentu dengan cara mengubah nilai opsi alert menjadi sebuah fungsi seperti ini:

var cm_config = {
...
alert: function(total, label) {
// Lakukan sesuatu dengan `total` dan `label`
}
};

total berfungsi untuk menampilkan total komentar baru, sedangkan label berfungsi untuk menampilkan nilai pada opsi new_cm. Sebagai contoh, buat sebuah elemen HTML seperti ini. Elemen ini akan digunakan sebagai penampil/kontainer teks total komentar:

<div id="show-total"></div>

Setelah itu, sisipkan teks total komentar ke dalam elemen tersebut dengan JavaScript innerHTML melalui fungsi di atas:

var cm_config = {
...
alert: function(total, label) {
document.getElementById('show-total').innerHTML = '<strong>'+total+' '+label+'</strong>';
}
};

Jika terdapat notifikasi baru, maka teks notifikasi tersebut akan muncul di dalam elemen #show-total dan akan menghasilkan markup HTML seperti ini:

<div id="show-total">
<strong>1 Komentar Baru!</strong>
</div>

Sebaiknya tambahkan juga atribut title dengan pesan tertentu dan satu buah fungsi kecil pada elemen di atas seperti ini:

<div id="show-total" title="Abaikan!" onclick="this.innerHTML='';"></div>

Fungsinya untuk menghilangkan teks total komentar apabila pengguna mengeklik elemen tersebut.

Label: , ,


Selasa, 18 September 2012

Membangun Aplikasi Quick Search dengan JSON Blogger

Membangun Aplikasi Quick Search dengan JSON Blogger
Membangun Aplikasi Quick Search dengan JSON Blogger

Selain melalui halaman pencarian biasa, Blogger juga mengizinkan kita untuk mengakses posting-posting acak di dalam feed yang dapat disortir berdasarkan kata kunci. Ini bisa dilakukan melalui parameter q.

Format dasar URL feed untuk memanggil data posting dengan sortir berdasarkan kata kunci adalah seperti ini:

http://nama_blog.blogspot.com/feeds/posts/summary?q=Kata Kunci

Yang jika diakses akan menghasilkan halaman berisi daftar posting tersortir berdasarkan kata kunci dari parameter q seperti ini ⇒ Daftar posting dengan kata kunci 'september'

Dengan memanfaatkan kerangka dasar widget Blogger, event onsubmit pada formulir dan juga penargetan elemen kontainer yang tepat, maka parameter ini memungkinkan kita untuk menciptakan aplikasi quick search. Yaitu sebuah aplikasi yang bisa digunakan untuk mencari artikel/posting dalam sebuah blog tanpa harus berpindah dari halaman asal. Seluruh hasil penelusuran akan tampil pada tempat yang sama!

Pada prinsipnya, hal pertama yang Saya buat adalah sebuah fungsi yang akan menyisipkan elemen <script> secara tidak langsung ke dalam area <head> saat fungsi tersebut dieksekusi. JavaScript yang Saya bangun adalah callback widget Blogger biasa dengan pola sumber yang umum. Saya hanya menambahkan parameter kata kunci pencarian sebagai penyortir posting yang akan dikirim nantinya:

http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&q=Kata Kunci&callback=functionName

Nilai parameter sumber diambil dari sebuah elemen <input> yang Saya letakkan di dalam formulir. Logikanya seperti ini:

Menyisipkan Nilai Elemen Input ke dalam Parameter 'q'
Menyisipkan Nilai Elemen Input ke dalam Parameter q pada URL Feed
function insertScript() {
// Membangun elemen <script> untuk disisipkan ke dalam area <head> ...
// ... dengan parameter kueri yang ditentukan oleh nilai elemen 'feed-q-input'
var query = document.getElementById('feed-q-input').value,
s = document.createElement('script');
s.type = "text/javascript";
s.src = "http://nama_blog.blogspot.com/feeds/posts/summary?q="+query+"&alt=json-in-script&callback=functionName";
document.getElementsByTagName('head')[0].appendChild(s);
}
<form onsubmit="insertScript();return false;"> ... </form>

Kode aslinya lebih panjang, tapi pada intinya inilah dasar yang Saya gunakan untuk membangun aplikasi pencarian cepat. Anda bisa mengunduh JavaScript dan melihat demonya melalui tombol-tombol ini:


Integrasi Widget ke Blogger

Pertama-tama masuk ke halaman editor HTML template Anda, kemudian salin kode CSS ini dan letakkan di atas kode ]]></b:skin> atau </style>:

#search-form-feed {
width:200px; /* lebar kotak penelusuran */
position:relative;
margin:0 0 10px;
padding:0 0;
font:normal normal 11px Arial,Sans-Serif;
color:#333;
}

#feed-q-input {
display:block;
width:100%;
border:2px solid #bbb;
background-color:white;
padding:5px 5px;
font:normal bold 13px Tahoma,Arial,Sans-Serif;
color:#ccc;
margin:0 0;
-webkit-border-radius:4px;
-moz-border-radius:4px;
border-radius:4px;
-webkit-box-shadow:inset 0 1px 5px rgba(0,0,0,.2);
-moz-box-shadow:inset 0 1px 5px rgba(0,0,0,.2);
box-shadow:inset 0 1px 5px rgba(0,0,0,.2);
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}

#feed-q-input:focus {
border-color:#0D6786;
color:#333;
outline:none;
-webkit-box-shadow:0 0 5px #153E95,0 0 7px #153E95;
-moz-box-shadow:0 0 5px #153E95,0 0 7px #153E95;
box-shadow:0 0 5px #153E95,0 0 7px #153E95;
}

#search-result-container {
width:400px;
height:300px;
overflow:auto;
position:absolute;
top:100%;
right:0;
z-index:999;
background-color:#E5EDF7;
border:2px solid white;
padding:10px 10px 0;
margin:10px 0 0;
-webkit-box-shadow:0 1px 2px rgba(0,0,0,.4),0 7px 7px -4px rgba(0,0,0,.4);
-moz-box-shadow:0 1px 2px rgba(0,0,0,.4),0 7px 7px -4px rgba(0,0,0,.4);
box-shadow:0 1px 2px rgba(0,0,0,.4),0 7px 7px -4px rgba(0,0,0,.4);
-webkit-border-radius:5px;
-moz-border-radius:5px;
border-radius:5px;
display:none;
}

#search-result-container mark {
background-color:yellow;
color:black;
}

#search-result-container a {
text-decoration:none;
color:#0D3E71;
font-weight:bold;
font-size:12px;
display:block;
}

#search-result-container a:hover {
color:#052748;
}

#search-result-container h4 {
margin:0 0 10px;
font:normal bold 12px 'Trebuchet MS',Arial,Sans-Serif;
color:#B50001;
}

#search-result-container ol {
background:transparent;
border:none;
margin:0 0 10px;
padding:0 0;
}

#search-result-container li {
margin:0 0 1px;
padding:7px 8px;
list-style:none;
border:1px solid #B7C1CE;
background-color:white;
overflow:hidden;
word-wrap:break-word;
}

#search-result-container li img {
display:block;
float:left;
margin:0 10px 4px 0;
border:1px solid #B7C1CE;
background-color:#F5F5F5;
padding:2px 2px;
}

#search-result-loader {
position:absolute;
top:100%;
left:5px;
z-index:999;
background-color:#0D6786;
color:white;
padding:3px 5px;
margin:-2px 0 0;
font:normal bold 10px Arial,Sans-Serif;
-webkit-border-radius:0 0 3px 3px;
-moz-border-radius:0 0 3px 3px;
border-radius:0 0 3px 3px;
display:none;
}

Setelah itu masuk ke halaman Tata Letak, kemudian tambahkan sebuah elemen halaman HTML/JavaScript. Salin kode ini dan letakkan di dalam formulirnya:

<div id="search-form-feed">
<form action="/search" onsubmit="return updateScript();">
<input name="q" type="text" value="Telusuri..." id="feed-q-input" onkeyup="resetField();" onfocus="this.value='';"/>
</form>
<div id="search-result-container"></div>
<div id="search-result-loader">Loading...</div>
</div>
<script type="text/javascript">
//<![CDATA[
var searchFormConfig = {
url: "http://hompimpaalaihumgambreng.blogspot.com", // URL Blog
numPost: 9999, // Jumlah maksimal temuan
summaryPost: true, // 'true' jika ingin menampilkan deskripsi posting
summaryLength: 400, // Jumlah karakter ringkasan posting
resultTitle: "Hasil penelusuran untuk kata kunci", // Judul hasil pencarian
noResult: "No result", // Deskripsi 'tak ditemukan'
resultThumbnail: true, // 'true' untuk menampilkan thumbnail posting
thumbSize: 40, // Ukuran & resolusi thumbnail
fallbackThumb: "http://reader-download.googlecode.com/svn/trunk/images/no-image-72x72.png" // Fallback thumbnail untuk posting tak bergambar
};
//]]>
</script>
<script type="text/javascript" src="http://reader-download.googlecode.com/svn/trunk/blogger-quick-search.js"></script>

Klik Simpan Widget.

Konfigurasi Widget

Semua opsi sangat umum. Saya yakin Anda bisa dengan cepat mempelajarinya:

OpsiKeterangan
urlURL blog Anda
numPostJumlah posting yang akan ditampilkan dalam hasil pencarian. Nilai 9999 maksudnya adalah Anda mencoba untuk menampilkan semua posting yang ditemukan
summaryPostPilihan untuk menampilkan atau menyembunyikan ringkasan posting dalam hasil pencarian
summaryLengthDigunakan untuk menentukan jumlah karakter ringkasan hasil pencarian
resultTitleJudul hasil pencarian
noResultPeringatan yang menunjukkan bahwa posting tidak ditemukan
resultThumbnailPilihan untuk menampilkan atau menyembunyikan thumbnail posting dalam hasil pencarian
thumbSizeDigunakan untuk menentukan ukuran dan resolusi thumbnail hasil pencarian
fallbackThumbGambar cadangan untuk posting tak bergambar.

Label: , , , , ,


Senin, 03 September 2012

Widget Translator Halaman - Versi 2

Widget Translator Halaman Ringan - Versi 2

Widget ini dibuat sebagai pengganti aplikasi Google Translate yang berat. Murni, dibuat dari kode HTML dan JavaScript, sehingga Anda bisa terbebas dari permintaan HTTP yang biasanya datang dari JavaScript bawaan widget Google Translate. Di sini JavaScript berfungsi untuk melakukan pemformatan URL translasi berdasarkan pola query string dari Google Translate yang berbentuk seperti ini agar kita bisa menuju ke halaman Google Translate tanpa harus terlibat dengannya pada saat awal kunjungan:

http://translate.google.com/translate?u=http://www.nama-situs.com&langpair=from|to&hl=en

Kode Widget

<style type="text/css">
#translator-wrapper {
display:block;
width:200px;
border:1px solid #0A340A;
background-color:white;
overflow:hidden;
position:relative;
}
#translator-wrapper select {
border:none;
background:transparent;
font:normal normal 11px Tahoma,Verdana,Arial,Sans-Serif;
width:100%;
color:black;
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
-webkit-appearance:none;
cursor:text;
padding:5px 10px;
}
#translator-wrapper a,
#translator-wrapper a:hover {
display:block;
background-color:#0A340A;
border:none;
color:white;
margin:0 0;
text-decoration:none;
position:absolute;
top:0;
right:0;
bottom:0;
cursor:pointer;
width:30px;
}
#translator-wrapper a:before {
content:"";
display:block;
width:0;
height:0;
border:5px solid transparent;
border-left-color:white;
position:absolute;
top:50%;
left:13px;
margin-top:-5px;
}
#translator-wrapper a:hover {background-color:#0A2C0A}
#translator-wrapper a:active {background-color:#082408}
#translator-wrapper select:focus,
#translator-wrapper a:focus,
#translator-wrapper select:active,
#translator-wrapper a:active {
border:none;
outline:none;
cursor:pointer;
}
</style>
<h3>Translate this page to:</h3>
<div id="translator-wrapper">
<select id="translate-language">
<option value="en" selected="selected">English</option>
<option value="id">Indonesian</option>
<option value="af">Afrikaans</option>
<option value="sq">Albanian</option>
<option value="ar">Arabic</option>
<option value="hy">Armenian</option>
<option value="az">Azerbaijani</option>
<option value="eu">Basque</option>
<option value="be">Belarusian</option>
<option value="bn">Bengali</option>
<option value="bg">Bulgarian</option>
<option value="ca">Catalan</option>
<option value="zh-CN">Chinese</option>
<option value="hr">Croatian</option>
<option value="cs">Czech</option>
<option value="da">Danish</option>
<option value="nl">Dutch</option>
<option value="en">English</option>
<option value="eo">Esperanto</option>
<option value="et">Estonian</option>
<option value="tl">Filipino</option>
<option value="fi">Finnish</option>
<option value="fr">French</option>
<option value="gl">Galician</option>
<option value="ka">Georgian</option>
<option value="de">German</option>
<option value="el">Greek</option>
<option value="gu">Gujarati</option>
<option value="ht">Haitian Creole</option>
<option value="iw">Hebrew</option>
<option value="hi">Hindi</option>
<option value="hu">Hungarian</option>
<option value="is">Icelandic</option>
<option value="id">Indonesian</option>
<option value="ga">Irish</option>
<option value="it">Italian</option>
<option value="ja">Japanese</option>
<option value="kn">Kannada</option>
<option value="ko">Korean</option>
<option value="la">Latin</option>
<option value="lv">Latvian</option>
<option value="lt">Lithuanian</option>
<option value="mk">Macedonian</option>
<option value="ms">Malay</option>
<option value="mt">Maltese</option>
<option value="no">Norwegian</option>
<option value="fa">Persian</option>
<option value="pl">Polish</option>
<option value="pt">Portuguese</option>
<option value="ro">Romanian</option>
<option value="ru">Russian</option>
<option value="sr">Serbian</option>
<option value="sk">Slovak</option>
<option value="sl">Slovenian</option>
<option value="es">Spanish</option>
<option value="sw">Swahili</option>
<option value="sv">Swedish</option>
<option value="ta">Tamil</option>
<option value="te">Telugu</option>
<option value="th">Thai</option>
<option value="tr">Turkish</option>
<option value="uk">Ukrainian</option>
<option value="ur">Urdu</option>
<option value="vi">Vietnamese</option>
<option value="cy">Welsh</option>
<option value="yi">Yiddish</option>
</select><a id="translate-me" href="#" title="Translate!"></a>
</div>
<script type="text/javascript">
(function() {
var mylang = "id", // Your website language
anchor = document.getElementById('translate-me');
anchor.onclick = function() {
window.open('http://translate.google.com/translate?u=' + encodeURIComponent(location.href) + '&langpair=' + mylang + '%7C' + document.getElementById('translate-language').value + '&hl=en');
return false;
};
})();
</script>

Salin saja kode di atas lalu letakkan di dalam sebuah widget HTML/JavaScript. Klik Simpan dan lihat hasilnya:

Label: , ,


Sabtu, 21 Juli 2012

Widget Recent Post dengan Preloader

Widget Recent Post dengan Preloader

Memperkenalkan widget recent post dengan konsep preloader untuk menangani proses pemuatan halaman yang terlalu berat. Secara teknis, widget ini hanya akan memulai pemuatan konten setelah mencapai waktu yang telah ditentukan:

Tambahkan sebuah elemen halaman HTML/JavaScript, kemudian salin kode ini dan letakkan di dalam formulirnya:

<style type="text/css">
#dte_recent-post {
margin:0 0;
padding:0 0;
font:normal 11px Arial,Sans-Serif;
color:#333;
margin:0 auto;
min-height:100px;
background:white url('http://reader-download.googlecode.com/svn/trunk/images/nivo-loading.gif') no-repeat 50% 50%;
}

#dte_recent-post li {
list-style:none;
margin:0 0;
padding:7px 7px;
background-color:white;
border-bottom:1px solid #ddd;
}

#dte_recent-post li a img {
float:left;
margin:0 10px 0 0;
padding:0 0;
border:none;
background:transparent;
outline:none;
}

#dte_recent-post li a.title {
display:block;
font-size:12px;
text-decoration:none;
color:#1155CC;
}

#dte_recent-post li a.title:hover {
text-decoration:underline;
}

#dte_recent-post li span.foot {
clear:both;
display:block;
color:#ccc;
margin-top:7px;
font-size:10px;
}
</style>
<ul id="dte_recent-post"></ul>
<script type="text/javascript">
//<![CDATA[
var rp_homePage = "http://nama_blog.blogspot.com", // Your blog homepage
rp_numPosts = 5, // How many posts?
rp_thumbWidth = 72, // Thumbnail width. Change to 0 if you want to disable the post thumbnail
rp_numChars = 100, // Number of posts summary
rp_sortByLabel = false, // Replace false with your specific label name to display posts by specific label
// Example: rp_sortByLabel = "JQuery", to sort posts by label "JQuery"
rp_noImage = "http://reader-download.googlecode.com/svn/trunk/images/meee-44x44.png", // A 'no-image' image
rp_monthNames = [
"Januari",
"Februari",
"Maret",
"April",
"Mei",
"Juni",
"Juli",
"Agustus",
"September",
"Oktober",
"November",
"Desember"
],
rp_newTabLink = true, // If true, then all the widget links will automatically opens in new window/new tab
rp_loadTimer = "onload"; // "onload" || time in milliseconds (e.g: 3000, 4000, ...)
//]]>
</script>
<script type="text/javascript" src="http://reader-download.googlecode.com/svn/trunk/blogger-recent-post-with-preloader.js"></script>

Konfigurasi Widget

OpsiKeterangan
rp_homePageGanti dengan URL blog Anda
rp_numPostsDigunakan untuk menentukan jumlah posting yang akan ditampilkan
rp_thumbWidthDigunakan untuk menentukan lebar thumbnail. Ubah lebarnya menjadi 0 untuk menghilangkan thumbnail
rp_numCharsDigunakan untuk menentukan jumlah karakter ringkasan posting
rp_sortByLabelGanti nilai false menjadi nama label untuk menampilkan posting berdasarkan label yang spesifik. Contoh: rp_sortByLabel = "JQuery" akan menampilkan semua posting dengan label JQuery
rp_noImageThumbnail cadangan jika posting yang tampil dalam widget tidak memiliki gambar
rp_monthNamesTentukan nama-nama bulan sesuai dengan sistem penanggalan di negara Anda
rp_newTabLinkJika bernilai true, maka semua tautan dalam widget akan terbuka di tab/jendela baru saat diklik
rp_loadTimerDigunakan untuk menentukan seberapa lama widget akan menunda proses pemuatan sebelum akhirnya mulai memuat. Secara normal bernilai "onload". Artinya bahwa widget akan memuat setelah semua elemen halaman telah berhasil termuat. Anda juga bisa mengganti nilainya dengan satuan waktu milidetik untuk menunda waktu pemuatan selama beberapa detik ke depan. Contoh: rp_loadTimer = 3000 akan menunda pemuatan JSON selama 3 detik.

Label: , , ,


Selasa, 10 Juli 2012

Rilis: Galleria V2 - Widget Masonry untuk Blogger

Pembaharuan 26 Mei 2013: Terjadi perubahan besar pada penulisan variabel-variabel di dalam widget ini. Saya terpaksa melakukannya demi pengembangan dan perbaikan widget yang lebih leluasa ke depannya agar tidak mengganggu para pemakai widget ini. Bagi Anda yang sudah memakai widget ini sebelum tanggal , Anda akan mendapatkan teks peringatan bahwa widget Anda harus segera diperbaharui.

Perubahan fitur: (1) Opsi showThumbnails ditiadakan (2) Menambahkan fitur Infinite Scroll (3) Mengganti parameter indeks feed dari window.location.search menjadi window.location.hash dengan bantuan event hashchange, sehingga pengguna tidak perlu memuat ulang halaman secara keseluruhan hanya untuk mengubah window.location.search pada address bar.

Rilis: Galleria V2 - Widget Masonry untuk Blogger

Sebelum ini Saya telah membuat widget Galleria versi pertama, kali ini Saya membuat versi yang ke dua dengan dua macam pilihan layout dan konfigurasi yang lebih lengkap.
Masalah besar yang terjadi pada versi pertama adalah dia menampilkan semua feed begitu saja sehingga proses muat widget menjadi begitu lama. Kita tidak memiliki kemampuan untuk memotong proses pemuatan feed kecuali dengan cara menambahkan fitur navigasi. Inilah kabar baiknya:

Widget ini dilengkapi dengan navigasi halaman. Setiap kali halaman berganti, itu akan mengubah nilai window.location.search pada address bar. Saya menggunakan angka-angka itu sebagai parameter untuk menentukan nilai start-index pada widget ini Widget ini dilengkapi dengan navigasi halaman. Setiap kali tombol navigasi diklik, itu akan mengubah nilai window.location.hash pada address bar. Saya menggunakan angka-angka itu sebagai parameter untuk menentukan nilai start-index pada widget ini:

Integrasi Widget ke Blogger

Pertama-tama klik menu Laman pada sidebar. Kemudian, pada menu Laman Baru pilihlah Laman Kosong:

Blogger Post Editor
Menambahkan halaman statis baru

Setelah itu Anda akan melihat formulir halaman statis seperti ini. Klik mode HTML:

Blogger Post Editor
Pilih mode HTML

Salin kode di bawah ini kemudian letakkan di dalam formulirnya:

<link rel="stylesheet" type="text/css" href="http://hompimpa.googlecode.com/svn/trunk/Blogger-Masonry-Widget/theme/light.min.css"/>
<div id="dte-masonry-container"></div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script src="http://hompimpa.googlecode.com/svn/trunk/Blogger-Masonry-Widget/js/jquery.masonry.min.js" type="text/javascript"></script>
<script src="http://hompimpa.googlecode.com/svn/trunk/Blogger-Masonry-Widget/js/post.brick.min.js" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
$('#dte-masonry-container').bloggerMasonry({

// JSON Configuration
viewMode: "summary", // Widget mode? "summary" || "thumbnail"
homePage: "http://nama_blog.blogspot.com", // Your blog homepage
numPosts: 10, // Number of posts to display per request
numChars: 270, // Length of summary post
squareImage: false, // Set thumbnail mode to square
newTabLink: false, // Auto open links in new window/tab?
columnWidth: 200, // Width of the image (also will resize the brick item width)
subHeaderText: ["Diposting oleh ", "<br>pada "], // `Diposkan oleh FOO pada BAR`
monthNames: [ // Month array
"Januari",
"Februari",
"Maret",
"April",
"Mei",
"Juni",
"Juli",
"Agustus",
"September",
"Oktober",
"November",
"Desember"
],
commentLabel: "&nbsp;", // Label text after total comments
navText: {
prev: "Sebelumnya", // Previous navigation label
next: "Berikutnya", // Next navigation label
disabled: "&times;", // Disabled navigation label
data: ["Halaman ", " dari "] // `Halaman # dari #`
},
postCategory: null, // Change to a label name to sort posts by specific label
fallbackThumb: "http://hompimpa.googlecode.com/svn/trunk/Blogger-Masonry-Widget/img/meee.png", // Fallback thumbnail for posts without images
loadingText: "Loading...", // `Loading...` text for loading indicator
loadedText: "Loaded.", // `Loaded.` text for loading indicator
infiniteScroll: false, // Enable infinite scroll?
bottomTolerance: 30, // Bottom tolerance for the end of page indicator in infinite scroll

// Shortcuts for Masonry Plugin Configuration (And Some Local Configuration)
masonryConfig: {
itemSelector: '.json_post',
fadeSpeed: 400, // Speed of thumbnail fading effect
resizeSpeed: 1000, // Speed of thumbnail resizing effect
isAnimated: false,
animateWithTransition: true, // Animate each brick with CSS transition instead of JQuery `.animate()`?
animationOptions: {
queue: false,
duration: 1000,
easing: null
},
isFitWidth: true,
gutterWidth: 0,
isRTL: false
}

});
//]]>
</script>

Ganti kode yang Saya beri tanda dengan URL blog Anda. Klik Simpan dan Terbitkan.

Kode yang Saya garis bawahi adalah JQuery. Jika template Anda sudah dilengkapi dengan JQuery, singkirkan kode tersebut!


Pengaturan Lanjutan

Pengaturan widget ini dibagi menjadi dua, yaitu pengaturan JSON Blogger dan pengaturan Plugin Masonry:

Pengaturan JSON Blogger

OpsiKeterangan
viewModeDigunakan untuk menentukan tipe tampilan widget. Nilainya ada dua macam: "summary" untuk menampilkan galeri berupa daftar gambar beserta ringkasannya, "thumbnail" untuk menampilkan galeri berupa foto saja
homePageDigunakan untuk menentukan URL blog sumber feed
numPostsDigunakan untuk menentukan jumlah posting yang akan dipanggil setiap satu kali permintaan
numCharsDigunakan untuk menentukan jumlah karakter ringkasan/paragraf jika mode widget berupa "summary"
squareImageJika bernilai true semua gambar dalam widget akan dikonversi menjadi berbentuk kotak persegi. Ini adalah versi kompresi Picasa versi path gambar s72-c, hanya saja di sini Saya buat lebih fleksibel (ukurannya bisa diperbesar/diperkecil) - Tentang metode kompresi gambar Picasa
newTabLinkJika bernilai true, semua tautan dalam widget akan terbuka di tab/jendela baru
columnWidthDigunakan untuk menentukan lebar setiap kolom/item posting. Menentukan lebar pada opsi ini juga akan mengubah resolusi gambar
subHeaderTextDigunakan untuk mengatur teks deskripsi kecil di bawah header pada mode "summary" (Misal: Diposting oleh Taufik di 21 April 2012)
monthNamesIni adalah daftar variabel nama-nama bulan. Ganti namanya sesuka hati sesuai dengan sistem penanggalan di negara Anda
commentLabelAbaikan saja. Jika nilainya diganti, maka label komentar di bawah item posting akan berubah.
Misalnya: 10 Komentar dari Pembaca akan tampil jika opsi commentLabel bernilai "Komentar dari Pembaca".
Jika dikosongkan, label akan menyesuaikan diri berdasarkan format bahasa dalam pengaturan blog Anda
navTextprev digunakan untuk menentukan label navigasi mundur
next digunakan untuk menentukan label navigasi maju
disabled digunakan untuk menentukan label navigasi tak aktif
data digunakan untuk menentukan label deskripsi posisi halaman dan jumlah total halaman
postCategorySecara normal bernilai null. Ganti nilainya dengan nama label yang spesifik untuk menyortir item feed berdasarkan kategori khusus. Misal: postCategory: "JQuery" akan membuat widget ini menampilkan posting-posting yang memiliki label JQuery
fallbackThumbGambar cadangan jika posting yang tampil tidak memiliki gambar
loadingTextDeskripsi indikator sedang memuat saat sedang memuat
loadedTextDeskripsi indikator sedang memuat saat semua item telah selesai dimuat
infiniteScrollJika bernilai true, navigasi halaman tidak berfungsi lagi. Widget akan secara otomatis memuat item-item posting baru di bawah item-item posting yang sudah ada setiap kali gulungan layar telah mencapai jarak yang diinginkan (dalam hal ini, mencapai posisi navigasi widget terdekat terhadap bagian bawah layar)
bottomToleranceDigunakan untuk menentukan toleransi jarak bawah infinite scroll untuk mengurangi kalkulasi posisi navigasi widget terdekat terhadap bagian bawah layar.

Konfigurasi Plugin Masonry

OpsiKeterangan
fadeSpeedDigunakan untuk menentukan kecepatan fade pada gambar
resizeSpeedDigunakan untuk menentukan kecepatan perubahan ukuran kontainer gambar pada mode widget "thumbnail"
isAnimatedJalan penghubung untuk opsi isAnimated pada plugin Masonry. Jika bernilai true efek animasi akan diterapkan dengan bantuan JQuery .animate(). Saat jendela peramban diperkecil/diperbesar, setiap item akan berubah posisi dengan efek animasi
animateWithTransitionAlternatif lain untuk menganimasikan widget. Set opsi isAnimated menjadi false, kemudian set opsi animateWithTransition menjadi true untuk menganimasikan masonry brick dengan CSS Transisi. Ini berhubungan dengan performa plugin. Menugaskan plugin untuk melakukan perhitungan posisi sekaligus menganimasikannya terkadang menjadi sesuatu yang berat bagi peramban. Jadi kita bisa memanfaatkan CSS Transisi —yang merupakan fitur bawaan pada peramban-peramban moderen— untuk menganimasikannya agar kerja peramban tidak terlalu berat dalam menangani JavaScript. Tentang metode animasi dengan CSS Transisi pada plugin Masonry bisa dibaca di sini
animationOptionsJalan penghubung untuk opsi animationOptions pada plugin Masonry. Digunakan untuk mengonfigurasikan efek animasi JavaScript (isAnimated:true)
isFitWidthJalan penghubung untuk opsi isFitWidth pada plugin Masonry. Jika bernilai true, kontainer akan menyusut sesuai dengan kalkulasi jumlah semua item dalam satu baris, sehingga widget Masonry bisa berada tepat di tengah-tengah halaman - Selengkapnya tentang isFitWidth
gutterWidthJalan penghubung untuk opsi gutterWidth pada plugin Masonry. Digunakan untuk menentukan gutter/kerenggangan antaritem pada sisi dalam. Tidak terlalu penting - Selengkapnya tentang gutterWidth
isRTLJalan penghubung untuk opsi isRTL pada plugin Masonry. Jika bernilai true, plugin akan menganimasikan item dari kanan ke kiri (RTL = Right To Left) - Selengkapnya tentang isRTL

Lebih Banyak Demo:

Pengembangan Widget

Proyek ini Saya hosting di Google Code: http://code.google.com/p/hompimpa/source/browse/trunk/Blogger-Masonry-Widget/ Jika Anda berminat untuk mengembangkan widget ini, Saya sudah menyimpan semuanya di sana.

Creative Commons License JQuery Masonry Widget for Blogger JSON by Taufik Nurrohman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License

Label: , , , ,


Minggu, 27 Mei 2012

Style Switcher Blog dengan Cookies

Style Switcher Blog
Style Switcher Blog

Style Switcher adalah aplikasi/aksesoris kecil yang biasa ada dalam sebuah situs untuk mengizinkan pengunjung mengganti tampilan dari luar sesuka hati. Dengan ini diharapkan pengunjung tidak akan bosan dengan artikel yang dia baca karena dia bisa memutuskan desain situs yang sedang dia kunjungi melalui Style Switcher.
Versi asli yang lebih besar sebenarnya cenderung mengambil konsep penggantian file CSS secara keseluruhan:

Style Switcher pada Yahoo! Mail
Style Switcher pada Yahoo! Mail

Di sini Saya hanya akan mengambil beberapa perubahan kecil saja seperti warna latar, warna huruf, jenis huruf dan ukuran huruf. Versi ini agak berbeda dengan yang biasa Anda temui, karena versi ini sudah Saya lengkapi dengan JavaScript Cookies. Kelebihannya adalah, saat pengunjung blog Anda mengubah tampilan template Anda dari luar, meskipun dia sudah berpindah-pindah halaman, perubahan yang dia lakukan akan tetap bertahan:

Untuk membuatnya, pertama-tama masuklah ke halaman editor HTML template Anda:

Mengedit HTML Template
Mengedit HTML Template

Temukan kode ini:

</body>

Salin script di bawah ini dan letakkan di atasnya:

<script type='text/javascript' src='http://reader-download.googlecode.com/svn/trunk/cookie.js'></script>
<script type='text/javascript'>
/**
* Style Switcher with JavaScript Cookie by Taufik Nurrohman
* URL: https://plus.google.com/108949996304093815163/posts
*/

//<![CDATA[
// =========================================================================
// == Your personal function
// =========================================================================
var expiredStyle = 7000,
dbs = document.body.style;

// background switcher
function bgSwitch(v) {
dbs.background = v;
createCookie('bgstyle', v, expiredStyle);
}

// font switcher
function fontSwitch(v) {
dbs.fontFamily = v;
createCookie('fontstyle', v, expiredStyle);
}

// font sizer
function changeFontSize(v) {
dbs.fontSize = v + 'px';
createCookie('fontsize', v, expiredStyle);
}

// color switcher
function fontColor(v) {
dbs.color = v;
createCookie('fontcolor', v, expiredStyle);
}

// =========================================================================
// == If cookies successfully created and successfully read... do something!
// =========================================================================
if (readCookie('bgstyle')) {
dbs.background = readCookie('bgstyle');
}
if (readCookie('fontstyle')) {
dbs.fontFamily = readCookie('fontstyle');
}
if (readCookie('fontsize')) {
dbs.fontSize = readCookie('fontsize') + 'px';
document.getElementById('fontSizer').value = readCookie('fontsize');
}
if (readCookie('fontcolor')) {
dbs.color = readCookie('fontcolor');
document.getElementById('fontColorer').value = readCookie('fontcolor');
}

// =========================================================================
// == Reset button
// =========================================================================
function resetStyle() {
eraseCookie('bgstyle');
eraseCookie('fontstyle');
eraseCookie('fontsize');
eraseCookie('fontcolor');
window.location.reload(1);
}
//]]>
</script>

Klik Simpan Template. Sekarang masuk ke halaman Tata Letak:

Masuk ke Halaman Tata Letak
Masuk ke halaman Tata Letak

Tambahkan sebuah elemen halaman HTML/JavaScript, kemudian salin kode ini dan letakkan di dalam formulirnya:

<style type="text/css">
#styleSwitcher {
border:none;
margin:0 0;
padding:0 0;
width:98%;
text-align:left;
font:normal 11px Tahoma,Arial,Sans-Serif;
border-collapse:collapse;
}

#styleSwitcher th,
#styleSwitcher td {
vertical-align:middle;
border:none !important;
padding:2px 10px;
}

#styleSwitcher th.title {
background-color:#ccc;
padding:5px 10px;
margin:0 0 10px;
text-transform:uppercase;
font-size:12px;
font-family:Arial,Sans-Serif;
}

#styleSwitcher select,
#styleSwitcher input[type="text"] {
width:130px;
border:2px solid #bbb;
background-color:#333;
padding:2px;
font:bold 11px Tahoma,Verdana,Arial,Sans-Serif;
color:#999;
display:block;
margin:0 0 0;
height:24px;
}

#styleSwitcher select option {
color:white;
padding:5px 10px;
cursor:pointer;
}

#styleSwitcher button {
font:normal 11px Tahoma,Arial,Sans-Serif;
padding:3px 5px;
cursor:pointer;
}

#styleSwitcher #bgColorer {
overflow:hidden;
margin:10px 0 10px;
}

#styleSwitcher #bgColorer span {
display:block;
float:left;
width:20px;
height:20px;
border:1px solid black;
margin:0 5px 0 0;
cursor:pointer;
}

#styleSwitcher input[type="text"] {
width:118px !important;
padding:4px !important;
height:auto !important;
}
</style>
<table border="0" id="styleSwitcher">
<tr><th class="title" colspan="2">Ganti Tampilan Sesuka Hati</th></tr>
<tr>
<td colspan="2">
<div id="bgColorer">
<span style="background-color:#523690;" onclick="bgSwitch(this.style.backgroundColor);"></span>
<span style="background-color:#248bcb;" onclick="bgSwitch(this.style.backgroundColor);"></span>
<span style="background-color:#fed100;" onclick="bgSwitch(this.style.backgroundColor);"></span>
<span style="background-color:#c91212;" onclick="bgSwitch(this.style.backgroundColor);"></span>
<span style="background-color:#3a9838;" onclick="bgSwitch(this.style.backgroundColor);"></span>
<span style="background-color:#36404a;" onclick="bgSwitch(this.style.backgroundColor);"></span>
<span style="background-color:#ffffff;" onclick="bgSwitch(this.style.backgroundColor);"></span>
</div>
</td>
</tr>
<tr><th>Tipe Font</th>
<td>
<select onchange="fontSwitch(this.value);">
<option selected="true">--</option>
<option value="'Book Antiqua',Serif">Book Antiqua</option>
<option value="'Times New Roman',Serif">Times New Roman</option>
<option value="Georgia,Serif">Georgia</option>
<option value="Arial,Sans-Serif">Arial</option>
<option value="Tahoma,Verdana,Arial,Sans-Serif">Tahoma</option>
<option value="'Trebuchet MS',Arial,Sans-Serif">Trebuchet</option>
<option value="Verdana,Arial,Sans-Serif">Verdana</option>
<option value="'Century Gothic',Tahoma,Verdana,Arial,Sans-Serif">Century Gothic</option>
<option value="'Comic Sans MS',Serif">Comic Sans</option>
</select>
</td>
</tr>
<tr><th>Warna Font</th>
<td><input type="text" id="fontColorer" value="#000000" onkeyup="fontColor(this.value);"/></td>
</tr>
<tr><th>Ukuran Huruf</th>
<td><input type="text" id="fontSizer" value="12" maxlength="3" onkeyup="changeFontSize(this.value);"/></td>
</tr>
<tr><th>Reset?</th>
<td><button onclick="resetStyle();">Reset Semua Tampilan</button></td>
</tr>
</table>

Klik Simpan dan lihat hasilnya.

Kode yang Saya beri tanda adalah masa kadaluarsa kuki. Saya membuat masa kadaluarsa selama 7000 hari untuk membuat tampilannya bertahan selama mungkin. Tapi jika Anda ingin menentukan sendiri masa kadaluarsa perubahan tema yang pengunjung Anda lakukan, (misalnya tampilan yang telah berubah akan kembali menjadi seperti semula dalam waktu 3 hari), cukup ganti nilainya dari 7000 menjadi 3 (untuk 3 hari).

Catatan: Aksesoris ini cocok diterapkan pada tema yang sederhana, cenderung berkotak-kotak dan tidak memiliki detail elemen yang rumit. Tidak cocok untuk .
Pertanyaan mengenai pengembangan dan penambahan fitur bisa dilanjutkan di bawah, tapi pastikan Anda sudah membaca cara kerja JavaScript Cookies sebelum berdiskusi » (pelajari di sini)

Label: , , , , ,


Minggu, 20 Mei 2012

Blogger JSON - Top Commentators

<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<title>Top Commentators</title>
<style type="text/css">
.top-commenter-line {
margin:3px 0px;
}
.top-commenter-avatar {
display:inline-block;
vertical-align:middle;
}
</style>
</head>
<body>

<script type="text/javascript">
// Top Commentators gadget with avatars, by MS-potilas 2012.
// Gets a list of top commentators from all comments, or specified number of days in the past.
// See http://yabtb.blogspot.com/2012/05/top-commenters-gadget-with-avatars.html

// CONFIG:
var homepage = 'http://latitudu.blogspot.com', // Homepage
maxTopCommenters = 17, // How much?
minComments = 1, // How many comments must top commentator have at least
numDays = 0, // From how many days (ex. 30), or 0 from "all the time"
excludeMe = true, // true: exclude my own comments
excludeUsers = ["Anonymous", "Another Name"], // Exclude these usernames
maxUserNameLength = 42, // 0: don't cut, >4: cut usernames
txtTopLine = '<b>[#].</b> [image] [user] ([count])', // List number
txtNoTopCommenters = 'No top commentators at this time.',
txtAnonymous = '', // Empty, or Anonymous user name localized if you want to localize
sizeAvatar = 16, // Avatar size
cropAvatar = true,
urlNoAvatar = 'https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4wSqYWsEVl2VeLeK5R-Cccef1x7g0gqe8hWXS73ZygEc9JuomI81buxbXY4xlUmjo_Eqak8nvn3QFlWhsl5pJvL9vxAi0gGDkC56D3s5eFge6hKPvIAW5WsPybVuCFx3OhH1YcDZRTrI/' + sizeAvatar + '/avatar_blue_m_96.png', // http://www.blogger.com/img/avatar_blue_m_96.png resizeable
urlAnoAvatar = 'http://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm&s=' + sizeAvatar,
urlMyProfile = '', // Set if you have no profile gadget on page
urlMyAvatar = ''; // Can be empty (then it is fetched) or url to image
// END CONFIG
// for old IEs & IE modes:
if (!Array.indexOf) {
Array.prototype.indexOf = function(obj) {
for (var i = 0; i < this.length; i++) if (this[i] == obj) return i;
return -1;
};
}

function replaceTopCmtVars(text, item, position) {
if (!item || !item.author) return text;
var author = item.author;

var authorUri = "";
if (author.uri && author.uri.$t !== "") authorUri = author.uri.$t;

var avaimg = urlAnoAvatar;
var bloggerprofile = "http://www.blogger.com/profile/";
if (author.gd$image && author.gd$image.src && authorUri.substr(0, bloggerprofile.length) == bloggerprofile) {
avaimg = author.gd$image.src;
} else {
var parseurl = document.createElement('a');
if (authorUri !== "") {
parseurl.href = authorUri;
avaimg = 'http://www.google.com/s2/favicons?domain=' + parseurl.hostname;
}
}
if (urlMyProfile !== "" && authorUri == urlMyProfile && urlMyAvatar !== "") avaimg = urlMyAvatar;
if (avaimg == "http://img2.blogblog.com/img/b16-rounded.gif" && urlNoAvatar !== "") avaimg = urlNoAvatar;
var newsize = "s" + sizeAvatar;
avaimg = avaimg.replace(/\/s\d\d+-c\//, "/" + newsize + "-c/");
if (cropAvatar) newsize += "-c";
avaimg = avaimg.replace(/\/s\d\d+(-c){0,1}\//, "/" + newsize + "/");

var authorName = author.name.$t;
if (authorName == 'Anonymous' && txtAnonymous !== "" && avaimg == urlAnoAvatar) authorName = txtAnonymous;
var imgcode = '<img class="top-commenter-avatar" height="' + sizeAvatar + '" width="' + sizeAvatar + '" title="' + authorName + '" src="' + avaimg + '" />';
if (authorUri !== "") imgcode = '<a href="' + authorUri + '">' + imgcode + '</a>';

if (maxUserNameLength > 3 && authorName.length > maxUserNameLength) authorName = authorName.substr(0, maxUserNameLength - 3) + "...";
var authorcode = authorName;
if (authorUri !== "") authorcode = '<a class="profile-name-link" href="' + authorUri + '">' + authorcode + '</a>';

text = text.replace('[user]', authorcode);
text = text.replace('[image]', imgcode);
text = text.replace('[#]', position);
text = text.replace('[count]', item.count);
return text;
}

var topcommenters = {};
var ndxbase = 1;

function showTopCommenters(json) {
var one_day = 1000 * 60 * 60 * 24;
var today = new Date();

if (urlMyProfile === "") {
var elements = document.getElementsByTagName("*");
var expr = /(^| )profile-link( |$)/;
for (var i = 0; i < elements.length; i++) {
if (expr.test(elements[i].className)) urlMyProfile = elements[i].href;
break;
}
}

for (var j = 0; j < json.feed.entry.length; j++) {
var entry = json.feed.entry[j];
if (numDays > 0) {
var datePart = entry.published.$t.match(/\d+/g); // Assume ISO 8601
var cmtDate = new Date(datePart[0], datePart[1] - 1, datePart[2], datePart[3], datePart[4], datePart[5]);

// Calculate difference btw the two dates, and convert to days
var days = Math.ceil((today.getTime() - cmtDate.getTime()) / (one_day));
if (days > numDays) break;
}
var authorUri = "";
if (entry.author[0].uri && entry.author[0].uri.$t !== "") authorUri = entry.author[0].uri.$t;

if (excludeMe && authorUri !== "" && authorUri == urlMyProfile) continue;
var authorName = entry.author[0].name.$t;
if (excludeUsers.indexOf(authorName) != -1) continue;

var hash = entry.author[0].name.$t + "-" + authorUri;
if (topcommenters[hash]) topcommenters[hash].count++;
else {
var commenter = {};
commenter.author = entry.author[0];
commenter.count = 1;
topcommenters[hash] = commenter;
}
}
if (json.feed.entry.length == 200) {
ndxbase += 200;
document.write('<scr' + 'ipt type="text/javascript" src="' + homepage + '/feeds/comments/default?redirect=false&max-results=200&start-index=' + ndxbase + '&alt=json-in-script&callback=showTopCommenters"></' + 'script>');
return;
}

// Convert object to array of tuples
var tuplear = [];
for (var key in topcommenters) tuplear.push([key, topcommenters[key]]);

tuplear.sort(function(a, b) {
if (b[1].count - a[1].count) return b[1].count - a[1].count;
return (a[1].author.name.$t.toLowerCase() < b[1].author.name.$t.toLowerCase()) ? -1 : 1;
});

// List top commenters:
var realcount = 0;
for (var k = 0; k < maxTopCommenters && k < tuplear.length; k++) {
var item = tuplear[k][1];
if (item.count < minComments) break;
document.write('<di' + 'v class="top-commenter-line">');
document.write(replaceTopCmtVars(txtTopLine, item, realcount + 1));
document.write('</d' + 'iv>');
realcount++;
}
if (!realcount) document.write(txtNoTopCommenters);
}
document.write('<scr' + 'ipt type="text/javascript" src="' + homepage + '/feeds/comments/default?redirect=false&max-results=200&alt=json-in-script&callback=showTopCommenters"></' + 'script>');
</script>

</body>
</html>

Label: , , , ,


This page is powered by Blogger. Isn't yours?