Thursday, February 19, 2015

Parser JSON untuk Blogger · Posting dan Halaman Statis

Berikut adalah generalisasi fungsi untuk mengubah data JSON posting dan halaman statis Blogger menjadi markup HTML sesuai dengan keinginan. Di sini Saya membagi fungsinya menjadi dua macam yaitu generatePostsData untuk menangani posting dan halaman dalam bentuk daftar dan generatePostData untuk menangani posting dan halaman dalam bentuk tunggal:

Bentuk Daftar

function generatePostsData(json) {

    // Poor configuration settings, develop them yourself!
    var config = {
        containerID: 'result-container', // Container ID to show the generated data
        avatarSize: 50, // Default avatar size
        noThumbnail: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABIAQMAAABvIyEEAAAABGdBTUEAALGPC/xhBQAAAAZQTFRFOjo6JycnNkxyjQAAADZJREFUKM9j+A8DDFRnNTCAACP9WewPGNgfkMmiwN7GH0CGfCPdWZT5V/7/DwZyWeSHFa1SHQDDGF2E0US40gAAAABJRU5ErkJggg==',
        text: {
            anon: 'Anonymous',
            untagged: 'Untagged',
            monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
        }
    };

    var html = "",
        item = "",
        w = window,
        d = document,
        feed = json.feed,
        container = d.getElementById(config.containerID),
        postTotal = +feed.openSearch$totalResults.$t, // The post/page feeds' total (all)
        postStartIndex = +feed.openSearch$startIndex.$t, // The post/page feeds' start index
        postPerPage = +feed.openSearch$itemsPerPage.$t, // The post/page feeds' max results per page or per feed request
        blogID = /\:blog-?(\d+)(\.|$)/.exec(feed.id.$t) ? /\:blog-?(\d+)(\.|$)/.exec(feed.id.$t)[1] : false, // The blog ID
        blogTitle = feed.title.$t, // The post/page feeds' title
        blogTags = false, // The available post/page tags (all)
        blogSubTitle = feed.subtitle.$t, // The post/page feeds' subtitle
        blogAuthorName = feed.author[0].name ? feed.author[0].name.$t : config.text.anon, // The blog/post author name
        blogAuthorAvatar = feed.author[0].gd$image.src.replace(/\/s\d+(\-c)?\//, '/s' + config.avatarSize + '-c/'), // The blog/post author profile avatar URL
        blogGeneratorName = feed.generator.$t, // The blog generator name (Blogger)
        blogGeneratorURL = feed.generator.uri; // The blog generator URL (http://www.blogger.com)

    // Remove the leading `http://` or `https://` in blog/post author profile avatar URL
    // blogAuthorAvatar = blogAuthorAvatar.replace(/^https?\:/, "");

    // No container found
    if (!container) {
        alert('Container not found.');
        return;
    }

    // Getting the blog tags
    if (feed.category && feed.category.length) {
        blogTags = [];
        for (var h = 0, hen = feed.category.length; h < hen; ++h) {
            blogTags.push(feed.category[h].term);
        }
        // Sort the blog tags alphabetically
        blogTags = blogTags.sort();
    }

    // No posts/pages yet
    if (!feed.entry || feed.entry.length === 0) {
        container.innerHTML = '<p>No posts/pages yet.</p>';
        return;
    }

    // Building the markup ...
    html += '<h1>' + blogTitle + '</h1>';
    html += '<h2>' + blogSubTitle + '</h2>';
    html += '<p><b>Blog ID:</b> ' + blogID + '</p>';
    html += '<p><b>Blog Tags:</b> ' + (blogTags !== false ? blogTags.join(', ') : config.text.untagged) + '</p>';
    html += '<p><b>Blog Author Name:</b> ' + blogAuthorName + '</p>';
    html += '<p><b>Blog Author Avatar URL:</b> ' + blogAuthorAvatar + '</p>';
    html += '<p><b>Total Posts:</b> ' + postTotal + '</p>';
    html += '<p><b>Posts Per Page:</b> ' + postPerPage + '</p>';
    html += '<p><b>Posts Start Index:</b> ' + postStartIndex + '</p>';
    html += '<hr>';
    html += '<ol>';

    var posts = feed.entry;

    for (var i = 0, ien = posts.length; i < ien; ++i) {
    
        var post = posts[i], // A single post/page object
            postID = post.id.$t, // The post/page ID
            postPublish = post.published.$t, // The post/page publishing time in ISO format
            postUpdate = post.updated.$t, // The post/page updating time in ISO format
            postDate = postPublish, // The post/page publishing time in human-readable format
            postURL = false, // The post/page URL
            postTags = false, // The post/page tags
            postCommentTotal = post.thr$total ? +post.thr$total.$t : 0, // The post/page comments total
            postCommentFeedURL = false, // The post/page comments feed URL
            postThumbnail = post.media$thumbnail ? post.media$thumbnail.url : config.noThumbnail, // The post/page thumbnail
            postAuthorName = post.author[0].name ? post.author[0].name.$t : config.text.anon, // The post/page author name
            postAuthorURL = post.author[0].uri ? post.author[0].uri.$t : false, // The post/page author profile URL
            postAuthorAvatar = post.author[0].gd$image.src.replace(/\/s\d+(\-c)?\//, '/s' + config.avatarSize + '-c/'), // The post/page author profile avatar URL
            postTitle = post.title.$t, // The post/page title
            postContent = post.content ? post.content.$t : post.summary.$t.replace(/<br *\/?>|[\s]+/gi, ' ').replace(/<.*?>|[<>]/g, ""), // The post/page content
            postEditURL = false; // The post/page edit URL

        // Generate human-readable post/page date format
        var date = postDate.split('T')[0].split('-');
        postDate = date[2] + ' ' + config.text.monthNames[(+date[1]) - 1] + ' ' + date[0];

        // Remove the leading `http://` or `https://` in post/page thumbnail URL
        // postThumbnail = postThumbnail.replace(/^https?\:/, "");

        // Remove the leading `http://` or `https://` in post/page author profile avatar URL
        // postAuthorAvatar = postAuthorAvatar.replace(/^https?\:/, "");

        for (var j = 0, jen = post.link.length; j < jen; ++j) {
            item = post.link[j];
            if (item.rel == 'self') {
                // Getting the original post/page ID
                postID = item.href.split('/').pop();
                // Getting the post/page edit URL
                postEditURL = item.href.replace(/\/feeds\/(\d+)\/(post|page)s?\/(default|summary)\/(\d+)/, '/$2-edit.g?blogID=$1&$2ID=$4');
            }
            // Getting the post/page URL
            if (item.rel == 'alternate') {
                postURL = item.href;
            }
            // Getting the post/page comment feed URL
            if (item.rel == 'replies' && item.type == 'application/atom+xml') {
                postCommentFeedURL = item.href;
            }
        }

        // Trying to get the external image URL from post/page content
        if (post.content && postThumbnail == config.noThumbnail) {
            var image = /<img +(.*?)src=(['"])([^'"]+?)(['"])(.*?) *\/?>/i.exec(post.content.$t);
            postThumbnail = image && image[3] ? image[3] : config.noThumbnail;
        }

        // Getting the post/page tags
        if (post.category && post.category.length) {
            postTags = [];
            for (var k = 0, ken = post.category.length; k < ken; ++k) {
                postTags.push(post.category[k].term);
            }
            // Sort the post/page tags alphabetically
            postTags = postTags.sort();
        }

        // Building the markup ...
        html += '<li>';
        html += '<p><b>ID:</b> ' + postID + '</p>';
        html += '<p><b>Publish:</b> ' + postPublish + '</p>';
        html += '<p><b>Update:</b> ' + postUpdate + '</p>';
        html += '<p><b>Date:</b> ' + postDate + '</p>';
        html += '<p><b>URL:</b> ' + postURL + '</p>';
        html += '<p><b>Tags:</b> ' + (postTags !== false ? postTags.join(', ') : config.text.untagged) + '</p>';
        html += '<p><b>Comment Total:</b> ' + postCommentTotal + '</p>';
        html += '<p><b>Comment Feed URL:</b> ' + postCommentFeedURL + '</p>';
        html += '<p><b>Thumbnail:</b> ' + postThumbnail + '</p>';
        html += '<p><b>Author:</b> ' + postAuthorName + '</p>';
        html += postAuthorURL !== false ? '<p><b>Author URL:</b> ' + postAuthorURL + '</p>' : "";
        html += '<p><b>Author Avatar URL:</b> ' + postAuthorAvatar + '</p>';
        html += '<p><b>Edit URL:</b> ' + postEditURL + '</p>';
        html += '<p><b>Title:</b> ' + postTitle + '</p>';
        html += '<p><b>Content:</b></p>';
        html += '<div>' + postContent + '</div>';
        html += '</li>';

    }

    // Building the markup ...
    html += '</ol>';

    // Show the generated data to the container ...
    container.innerHTML = html;

}

Penggunaan

Urutannya dimulai dari penulisan HTML untuk menampung data yang akan digenerasikan oleh fungsi di atas, dilanjutkan dengan memasukkan fungsi di atas ke dalam tag <script>, lalu memanggil data JSON dengan menggunakan nilai parameter URL callback berupa generatePostsData, sesuai dengan nama fungsi di atas:

<div id="result-container">Loading&hellip;</div>
<script>
function generatePostsData(json) { … }
</script>
<script src="//nama_blog.blogspot.com/feeds/posts/default?alt=json-in-script&orderby=published&callback=generatePostsData"></script>

Lihat Demo

Bentuk Tunggal

function generatePostData(json) {

    // Poor configuration settings, develop them yourself!
    var config = {
        containerID: 'result-container', // Container ID to show the generated data
        avatarSize: 50, // Default avatar size
        noThumbnail: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABIAQMAAABvIyEEAAAABGdBTUEAALGPC/xhBQAAAAZQTFRFOjo6JycnNkxyjQAAADZJREFUKM9j+A8DDFRnNTCAACP9WewPGNgfkMmiwN7GH0CGfCPdWZT5V/7/DwZyWeSHFa1SHQDDGF2E0US40gAAAABJRU5ErkJggg==',
        text: {
            anon: 'Anonymous',
            untagged: 'Untagged',
            monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
        }
    };

    var html = "",
        item = "",
        w = window,
        d = document,
        container = d.getElementById(config.containerID);

    // No container found
    if (!container) {
        alert('Container not found.');
        return;
    }

    var post = json.entry, // The post/page object
        postID = post.id.$t, // The post/page ID
        postPublish = post.published.$t, // The post/page publishing time in ISO format
        postUpdate = post.updated.$t, // The post/page updating time in ISO format
        postDate = postPublish, // The post/page publishing time in human-readable format
        postURL = false, // The post/page URL
        postTags = false, // The post/page tags
        postCommentTotal = post.thr$total ? +post.thr$total.$t : 0, // The post/page comments total
        postCommentFeedURL = false, // The post/page comments feed URL
        postThumbnail = post.media$thumbnail ? post.media$thumbnail.url : config.noThumbnail, // The post/page thumbnail
        postAuthorName = post.author[0].name ? post.author[0].name.$t : config.text.anon, // The post/page author name
        postAuthorURL = post.author[0].uri ? post.author[0].uri.$t : false, // The post/page author profile URL
        postAuthorAvatar = post.author[0].gd$image.src.replace(/\/s\d+(\-c)?\//, '/s' + config.avatarSize + '-c/'), // The post/page author profile avatar URL
        postTitle = post.title.$t, // The post/page title
        postContent = post.content ? post.content.$t : post.summary.$t.replace(/<br *\/?>|[\s]+/gi, ' ').replace(/<.*?>|[<>]/g, ""), // The post/page content
        postEditURL = false; // The post/page edit URL

    // Generate human-readable post/page date format
    var date = postDate.split('T')[0].split('-');
    postDate = date[2] + ' ' + config.text.monthNames[(+date[1]) - 1] + ' ' + date[0];

    // Remove the leading `http://` or `https://` in post/page thumbnail URL
    // postThumbnail = postThumbnail.replace(/^https?\:/, "");

    // Remove the leading `http://` or `https://` in post/page author profile avatar URL
    // postAuthorAvatar = postAuthorAvatar.replace(/^https?\:/, "");

    for (var j = 0, jen = post.link.length; j < jen; ++j) {
        item = post.link[j];
        if (item.rel == 'self') {
            // Getting the original post/page ID
            postID = item.href.split('/').pop();
            // Getting the post/page edit URL
            postEditURL = item.href.replace(/\/feeds\/(\d+)\/(post|page)s?\/(default|summary)\/(\d+)/, '/$2-edit.g?blogID=$1&$2ID=$4');
        }
        // Getting the post/page URL
        if (item.rel == 'alternate') {
            postURL = item.href;
        }
        // Getting the post/page comment feed URL
        if (item.rel == 'replies' && item.type == 'application/atom+xml') {
            postCommentFeedURL = item.href;
        }
    }

    // Trying to get the external image URL from post/page content
    if (post.content && postThumbnail == config.noThumbnail) {
        var image = /<img +(.*?)src=(['"])([^'"]+?)(['"])(.*?) *\/?>/i.exec(post.content.$t);
        postThumbnail = image && image[3] ? image[3] : config.noThumbnail;
    }

    // Getting the post/page tags
    if (post.category && post.category.length) {
        postTags = [];
        for (var k = 0, ken = post.category.length; k < ken; ++k) {
            postTags.push(post.category[k].term);
        }
        // Sort the post/page tags alphabetically
        postTags = postTags.sort();
    }

    // Building the markup ...
    html += '<li>';
    html += '<p><b>ID:</b> ' + postID + '</p>';
    html += '<p><b>Publish:</b> ' + postPublish + '</p>';
    html += '<p><b>Update:</b> ' + postUpdate + '</p>';
    html += '<p><b>Date:</b> ' + postDate + '</p>';
    html += '<p><b>URL:</b> ' + postURL + '</p>';
    html += '<p><b>Tags:</b> ' + (postTags !== false ? postTags.join(', ') : config.text.untagged) + '</p>';
    html += '<p><b>Comment Total:</b> ' + postCommentTotal + '</p>';
    html += '<p><b>Comment Feed URL:</b> ' + postCommentFeedURL + '</p>';
    html += '<p><b>Thumbnail:</b> ' + postThumbnail + '</p>';
    html += '<p><b>Author:</b> ' + postAuthorName + '</p>';
    html += postAuthorURL !== false ? '<p><b>Author URL:</b> ' + postAuthorURL + '</p>' : "";
    html += '<p><b>Author Avatar URL:</b> ' + postAuthorAvatar + '</p>';
    html += '<p><b>Edit URL:</b> ' + postEditURL + '</p>';
    html += '<p><b>Title:</b> ' + postTitle + '</p>';
    html += '<p><b>Content:</b></p>';
    html += '<div>' + postContent + '</div>';
    html += '</li>';

    // Building the markup ...
    html += '</ol>';

    // Show the generated data to the container ...
    container.innerHTML = html;

}

Penggunaan

Sisipkan ID posting/halaman statis setelah path default atau summary:

<div id="result-container">Loading&hellip;</div>
<script>
function generatePostData(json) { … }
</script>
<script src="//nama_blog.blogspot.com/feeds/posts/default/1962799387619194999?alt=json-in-script&callback=generatePostData"></script>

Lihat Demo

Ekstra

Halaman Statis

Data halaman statis dapat dipanggil dengan menggunakan format URL seperti ini:

http://nama_blog.blogspot.com/feeds/pages/default?alt=json-in-script&orderby=published&callback=generatePostsData
http://nama_blog.blogspot.com/feeds/pages/default?alt=json-in-script&callback=generatePostData

Mode Ringkas

Mode ringkas dapat dipanggil dengan menggunakan format URL seperti ini:

http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&orderby=published&callback=generatePostsData
http://nama_blog.blogspot.com/feeds/pages/summary?alt=json-in-script&orderby=published&callback=generatePostsData
http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&callback=generatePostData
http://nama_blog.blogspot.com/feeds/pages/summary?alt=json-in-script&callback=generatePostData

Labels: , , ,

Wednesday, February 18, 2015

Parser JSON untuk Blogger · Komentar

Berikut adalah generalisasi fungsi untuk mengubah data JSON komentar Blogger menjadi markup HTML sesuai dengan keinginan. Saya sudah mendefinisikan ulang data-data yang penting ke dalam variabel, sehingga Anda bisa memodifikasi skrip ini dengan lebih mudah:

Bentuk Daftar

function generateCommentsData(json) {

    // Poor configuration settings, develop them yourself!
    var config = {
        containerID: 'result-container', // Container ID to show the generated data
        avatarSize: 50, // Default avatar size
        text: {
            anon: 'Anonymous'
        }
    };

    var html = "",
        item = "",
        w = window,
        d = document,
        feed = json.feed,
        container = d.getElementById(config.containerID),
        postCommentTotal = +feed.openSearch$totalResults.$t, // The comment feeds' total (all)
        postCommentStartIndex = +feed.openSearch$startIndex.$t, // The comment feeds' start index
        postCommentPerPage = +feed.openSearch$itemsPerPage.$t, // The comment feeds' max results per page or per feed request
        blogID = /\:blog-?(\d+)(\.|$)/.exec(feed.id.$t) ? /\:blog-?(\d+)(\.|$)/.exec(feed.id.$t)[1] : false, // The blog ID
        postID = /\.post-?(\d+)(\.|$)/.exec(feed.id.$t) ? /\.post-?(\d+)(\.|$)/.exec(feed.id.$t)[1] : false, // The current post ID (if any)
        postURL = false, // The current post URL (if any)
        blogTitle = feed.title.$t, // The comment feeds' title
        blogAuthorName = feed.author[0].name ? feed.author[0].name.$t : config.text.anon, // The blog/post author name
        blogAuthorAvatar = feed.author[0].gd$image.src.replace(/\/s\d+(\-c)?\//, '/s' + config.avatarSize + '-c/'), // The blog/post author profile avatar URL
        blogGeneratorName = feed.generator.$t, // The blog generator name (Blogger)
        blogGeneratorURL = feed.generator.uri; // The blog generator URL (http://www.blogger.com)

    // Remove the leading `http://` or `https://` in blog/post author profile avatar URL
    // blogAuthorAvatar = blogAuthorAvatar.replace(/^https?\:/, "");

    // No container found
    if (!container) {
        alert('Container not found.');
        return;
    }

    // Getting the current post URL (if any)
    if (postID) {
        for (var h = 0, hen = feed.link.length; h < hen; ++h) {
            item = feed.link[h];
            if (item.rel == 'alternate') {
                postURL = item.href;
                break;
            }
        }
    }
 
    // No comments yet
    if (!feed.entry || feed.entry.length === 0) {
        container.innerHTML = '<p>No comments yet.</p>';
        return;
    }

    // Building the markup ...
    html += '<h1>' + blogTitle + '</h1>';
    html += '<p><b>Blog ID:</b> ' + blogID + '</p>';
    html += '<p><b>Post ID:</b> ' + postID + '</p>';
    html += '<p><b>Post URL:</b> ' + postURL + '</p>';
    html += '<p><b>Blog Author Name:</b> ' + blogAuthorName + '</p>';
    html += '<p><b>Blog Author Avatar URL:</b> ' + blogAuthorAvatar + '</p>';
    html += '<p><b>Total Comments:</b> ' + postCommentTotal + '</p>';
    html += '<p><b>Comments Per Page:</b> ' + postCommentPerPage + '</p>';
    html += '<p><b>Comments Start Index:</b> ' + postCommentStartIndex + '</p>';
    html += '<hr>';
    html += '<ol>';

    var comments = feed.entry;

    for (var i = 0, ien = comments.length; i < ien; ++i) {

        var comment = comments[i], // A single comment feed object
            commentID = comment.id.$t, // The comment ID
            commentPublish = comment.published.$t, // The comment publishing time in ISO format
            commentUpdate = comment.updated.$t, // The comment updating time in ISO format
            commentDate = commentPublish, // The comment publishing time in timestamp format you defined in the dashboard
            commentAuthorName = comment.author[0].name ? comment.author[0].name.$t : config.text.anon, // The comment author name
            commentAuthorURL = comment.author[0].uri ? comment.author[0].uri.$t : false, // The comment author profile URL
            commentAuthorAvatar = comment.author[0].gd$image.src.replace(/\/s\d+(\-c)?\//, '/s' + config.avatarSize + '-c/'), // The comment author profile avatar URL
            commentContent = comment.content ? comment.content.$t : comment.summary.$t.replace(/<br *\/?>|[\s]+/gi, ' ').replace(/<.*?>|[<>]/g, ""), // The comment content
            commentParent = false, // The comment parent ID (if any, for child comments)
            commentPermalink = false, // The comment permalink
            commentIsAdmin = commentAuthorName === blogAuthorName || commentAuthorAvatar === blogAuthorAvatar, // Is this comment was created by the blog/post author?
            commentDeleteURL = false; // The comment delete URL

        // Remove the leading `http://` or `https://` in comment author profile avatar URL
        // commentAuthorAvatar = commentAuthorAvatar.replace(/^https?\:/, "");

        for (var j = 0, jen = comment.link.length; j < jen; ++j) {
            item = comment.link[j];
            if (item.rel == 'self') {
                // Getting the original comment ID
                commentID = item.href.split('/').pop();
                // Getting the comment delete URL
                commentDeleteURL = item.href.replace(/\/feeds\/(\d+)\/(\d+)\/comments?\/(default|summary)\/(\d+)/, '/delete-comment.g?blogID=$1&postID=$4');
            }
            // Getting the comment permalink URL
            if (item.rel == 'alternate') {
                commentPermalink = item.href;
            }
            // Getting the comment parent ID (if any)
            if (item.rel == 'related') {
                var parentID = item.href.split('/').pop();
                commentParent = commentID !== parentID ? parentID : false;
            }
        }

        // Getting the comment publishing time in timestamp format you defined in the dashboard
        for (var k = 0, ken = comment.gd$extendedProperty.length; k < ken; ++k) {
            item = comment.gd$extendedProperty[k];
            if (item.name == 'blogger.displayTime') {
                commentDate = item.value;
                break;
            }
        }

        // Building the markup ...
        html += '<li>';
        html += '<p><b>ID:</b> ' + commentID + '</p>';
        html += '<p><b>Publish:</b> ' + commentPublish + '</p>';
        html += '<p><b>Update:</b> ' + commentUpdate + '</p>';
        html += '<p><b>Date:</b> ' + commentDate + '</p>';
        html += '<p><b>Author:</b> ' + commentAuthorName + '</p>';
        html += commentAuthorURL !== false ? '<p><b>URL:</b> ' + commentAuthorURL + '</p>' : "";
        html += '<p><b>Avatar URL:</b> ' + commentAuthorAvatar + '</p>';
        html += commentParent !== false ? '<p><b>Parent ID:</b> ' + commentParent + '</p>' : "";
        html += '<p><b>Permalink:</b> ' + commentPermalink + '</p>';
        html += '<p><b>Status:</b> ' + (commentIsAdmin ? 'admin' : 'guest') + '</p>';
        html += '<p><b>Delete URL:</b> ' + commentDeleteURL + '</p>';
        html += '<p><b>Message:</b></p>';
        html += '<div>' + commentContent + '</div>';
        html += '</li>';

    }

    // Building the markup ...
    html += '</ol>';

    // Show the generated data to the container ...
    container.innerHTML = html;

}

Penggunaan

Urutannya dimulai dari penulisan HTML untuk menampung data yang akan digenerasikan oleh fungsi di atas, dilanjutkan dengan memasukkan fungsi di atas ke dalam tag <script>, lalu memanggil data JSON dengan menggunakan nilai parameter URL callback berupa generateCommentsData, sesuai dengan nama fungsi di atas:

<div id="result-container">Loading&hellip;</div>
<script>
function generateCommentsData(json) { … }
</script>
<script src="//nama_blog.blogspot.com/feeds/comments/default?alt=json-in-script&reverse=false&orderby=published&callback=generateCommentsData"></script>

Lihat Demo

Ekstra

Memanggil Data Komentar untuk Posting Tertentu

Gunakan format URL seperti ini untuk membatasi pemanggilan data komentar hanya untuk komentar-komentar yang terdapat pada posting dengan ID 6427706034645358331:

http://nama_blog.blogspot.com/feeds/6427706034645358331/comments/default?alt=json-in-script&reverse=false&orderby=published&callback=generateCommentsData

Sayangnya Saya tidak berhasil menemukan referensi resmi mengenai JSON Blogger versi 1.0 di Google Developer. Dulu memang pernah ada tapi sekarang sudah tidak ada lagi. Yang ada sekarang lebih banyak ditujukan untuk API Blogger versi 3.0+ yang membutuhkan autentikasi.

Labels: , , ,

Monday, February 2, 2015

JavaScript Spoiler

JavaScript Spoiler
Spoiler

Ini adalah versi mentah dari plugin spoiler pada CMS Mecha. Anda bisa menerapkannya di Blogger atau di web mana saja dengan cara mengikuti langkah-langkah berikut.

Masuklah ke halaman editor HTML Template Anda. Pertama-tama salin kode di bawah ini dan letakkan di atas kode </head>:

<script>document.documentElement.className += ' spoiler-js';</script>

Setelah itu salin kode CSS ini dan letakkan di atas kode ]]></b:skin> atau </style>:

.spoiler {
  background-color:darkslategray;
  color:white;
  padding:.15384615384615385em;
  margin:1em 0;
}

.spoiler-toggle,
.spoiler-toggle:focus,
.spoiler-toggle:hover,
.spoiler-toggle:active {
  display:block;
  margin:0;
  padding:0 1.2em 0 .6em;
  height:2em;
  font:normal normal 100%/2em Helmet,FreeSans,Sans-Serif;
  color:inherit;
  text-decoration:none;
  outline:none;
  position:relative;
  overflow:hidden;
  white-space:nowrap;
  text-overflow:ellipsis;
  cursor:pointer;
}

.spoiler-toggle:before {
  content:"";
  display:block;
  float:right;
  width:0;
  height:0;
  border-width:.3076923076923077em .3076923076923077em 0;
  border-style:solid;
  border-color:white transparent transparent;
  margin:.9230769230769231em -.6em 0 0;
}

.spoiler-content {
  padding:1em;
  background-color:white;
  color:#333;
}

.spoiler-state-expanded .spoiler-toggle {padding-bottom:inherit}

.spoiler-state-expanded .spoiler-toggle:before {
  border-width:0 .3076923076923077em .3076923076923077em;
  border-color:transparent transparent white;
  margin-top:.8461538461538461em;
}

.spoiler-state-expanded .spoiler-content + .spoiler-toggle {
  padding-top:inherit;
  padding-bottom:0;
}

.spoiler-state-disabled .spoiler-toggle {
  cursor:default;
  cursor:not-allowed;
}

.spoiler-state-disabled .spoiler-toggle:before,
.spoiler-js .spoiler-state-collapsed .spoiler-content {display:none}

.spoiler-primary {background-color:steelblue}
.spoiler-success {background-color:mediumseagreen}
.spoiler-info {background-color:skyblue}
.spoiler-warning {background-color:sandybrown}
.spoiler-danger {background-color:salmon}

Terakhir, salin kode JavaScript ini dan letakkan di atas </body>:

<script>
//<![CDATA[

// http://www.dte.web.id

(function(w, d) {
  var panel = d.getElementsByClassName('spoiler');
  if (!panel) return;
  for (var i = 0, len = panel.length; i < len; ++i) {
    if (!panel[i].id) panel[i].id = 'spoiler-' + (i + 1);
  }
  function toggleSpoiler(elem, index) {
    var toggle = d.createElement('a'),
        toggleText = (elem[index].getAttribute('data-toggle-text') || '&nbsp;').split(' | '),
        togglePlacement = elem[index].getAttribute('data-toggle-placement') && elem[index].getAttribute('data-toggle-placement') !== 'bottom' ? elem[index].getAttribute('data-toggle-placement') : 'bottom';
    toggleText.push(toggleText[0]);
    toggle.className = 'spoiler-toggle';
    toggle.href = '#' + elem[index].id;
    toggle.innerHTML = toggleText[/(^| )spoiler-state-collapsed( |$)/.test(elem[index].className) ? 0 : 1];
    toggle.onclick = function() {
      var target = this.parentNode,
          isExpanded = /(^| )spoiler-state-expanded( |$)/.test(target.className),
          isConnected = target.getAttribute('data-connection');
      if (/(^| )spoiler-state-disabled( |$)/.test(target.className)) return false;
      target.className = isExpanded ? target.className.replace(/(^| )spoiler-state-expanded( |$)/, '$1spoiler-state-collapsed$2') : target.className.replace(/(^| )spoiler-state-collapsed( |$)/, '$1spoiler-state-expanded$2');
      this.innerHTML = toggleText[isExpanded ? 0 : 1];
      if (isConnected) {
        for (var i = 0, len = elem.length; i < len; ++i) {
          var isConnectedTo = elem[i].getAttribute('data-connection'),
              toggleTextSibling = (elem[i].getAttribute('data-toggle-text') || '&nbsp;').split(' | '),
              togglePlacementSibling = elem[i].getAttribute('data-toggle-placement') && elem[i].getAttribute('data-toggle-placement') !== 'bottom' ? elem[i].getAttribute('data-toggle-placement') : 'bottom';
          if (isConnectedTo && isConnected === isConnectedTo && target.id !== elem[i].id) {
            elem[i].className = elem[i].className.replace(/(^| )spoiler-state-expanded( |$)/, '$1spoiler-state-collapsed$2');
            elem[i].children[togglePlacementSibling === 'bottom' ? 1 : 0].innerHTML = toggleTextSibling[0];
          }
        }
      }
      return false;
    };
    toggle.onmousedown = false;
    elem[index].insertBefore(toggle, togglePlacement == 'bottom' ? null : elem[index].firstChild);
  }
  for (var i = 0, len = panel.length; i < len; ++i) {
    toggleSpoiler(panel, i);
  }
})(window, document);
//]]>
</script>

Klik tombol Simpan Templat.

Penggunaan

Semua konfigurasi hanya didasarkan pada atribut-atribut di dalam HTML spoiler.

Dasar

Sebuah spoiler sederhana dapat dibuat dengan menyisipkan kode HTML ini ke dalam posting. Pastikan Anda sedang berada pada mode HTML, dan bukannya berada pada mode Tulis:

<div class="spoiler spoiler-state-collapsed" data-toggle-text="Spoiler">
  <div class="spoiler-content">Konten di sini…</div>
</div>

Terbuka secara Default

Ganti kelas spoiler-state-collapsed menjadi spoiler-state-expanded untuk membuat spoiler menjadi terbuka secara default:

<div class="spoiler spoiler-state-expanded" data-toggle-text="Spoiler">
  <div class="spoiler-content">Konten di sini…</div>
</div>

Modifikasi Teks pada Tombol Toggle

Tambahkan atribut data-toggle-text untuk menambahkan teks pada tombol. Pisahkan teks tombol menjadi dua bagian dengan sebuah “ | ” untuk membuat tombol berubah status secara bergantian ketika panel spoiler terbuka dan tertutup:

<div class="spoiler spoiler-state-collapsed" data-toggle-text="Buka Spoiler | Tutup Spoiler">
  <div class="spoiler-content">Konten di sini…</div>
</div>

Mengubah Posisi Tombol

Secara normal, tombol toggle akan ditempatkan setelah panel. Anda bisa mengubah posisinya menjadi sebelum panel dengan cara menambahkan atribut data-toggle-placement dengan nilai sebagai top:

<div class="spoiler spoiler-state-collapsed" data-toggle-placement="top">
  <div class="spoiler-content">Konten di sini…</div>
</div>

Kelas Kustom

Kelas kustom terinspirasi dari Twitter Bootstrap:

<div class="spoiler spoiler-default spoiler-state-collapsed" data-toggle-text="Default">
  <div class="spoiler-content">Konten di sini…</div>
</div>

<div class="spoiler spoiler-primary spoiler-state-collapsed">
  <div class="spoiler-content">Konten di sini…</div>
</div>

<div class="spoiler spoiler-success spoiler-state-collapsed">
  <div class="spoiler-content">Konten di sini…</div>
</div>

<div class="spoiler spoiler-info spoiler-state-collapsed">
  <div class="spoiler-content">Konten di sini…</div>
</div>

<div class="spoiler spoiler-warning spoiler-state-collapsed">
  <div class="spoiler-content">Konten di sini…</div>
</div>

<div class="spoiler spoiler-danger spoiler-state-collapsed">
  <div class="spoiler-content">Konten di sini…</div>
</div>

Efek Akordion

Tambahkan atribut data-connection untuk mengaitkan antara panel yang satu dengan panel yang lainnya yang juga memiliki atribut data-connection dengan nilai yang sama. Nilai bisa berupa apa saja:

<div class="spoiler spoiler-state-expanded" data-toggle-text="Panel 1" data-connection="A">
  <div class="spoiler-content">Konten panel 1.</div>
</div>

<div class="spoiler spoiler-state-collapsed" data-toggle-text="Panel 2" data-connection="A">
  <div class="spoiler-content">Konten panel 2.</div>
</div>

<div class="spoiler spoiler-state-collapsed" data-toggle-text="Panel 3" data-connection="A">
  <div class="spoiler-content">Konten panel 3.</div>
</div>

Nonaktif

Untuk menonaktifkan spoiler, cukup tambahkan kelas spoiler-state-disabled:

<div class="spoiler spoiler-state-collapsed spoiler-state-disabled">
  <div class="spoiler-content">Konten di sini…</div>
</div>

Demo selengkapnya bisa Anda lihat di sini:

Lihat Demo

Pengguaan spoiler ini kelihatannya memang sedikit rumit jika dilihat dari segi penulisan markup HTML. Mungkin beberapa dari kalian lebih menginginkan markup yang sederhana seperti sebuah elemen <div> dengan ID spoiler-1 dan sebuah tombol dengan atribut onclick bernilai toggleSpoiler('spoiler-1'). Tapi ini adalah resiko yang akan selalu muncul pada semua alat, widget dan plugin dengan fitur yang cukup banyak. Terdapat arsitektur dan pola tersendiri di dalamnya yang tidak bisa dilepaskan maupun disederhanakan begitu saja.

Labels: , ,