1 of 1 found this page helpful

AJAX Suggestion Voter

    This Page has been Viewed: 214 Times
    Login to Vote

    AjaxVoter

     

    Introduction

    This template is a simple AJAX enabled Up/Down or Positive/Negative Voter.  You are able to place many of these on one page to simulate the FeVote functionality.  Since this uses page properties to save the voting results its not recommended for any use if security or 100% integrity maybe a concern.  In order to vote the voter must have write capabilites for the page the results are being stored on.  As written this voter can only be used by users that are logged in, no anon voting is allowed.  It should also be noted that if a user votes twice their vote does not get counted twice instead their vote is changed.  Please direct any questions / comments to this forum thread.

    How the Score is calculated

    There are 2 methods of scoring "sum" and "percent" the default method is "sum".  With the "sum" scoring method each positive vote is given a value of 1 and each negative vote is given a value of -1.  The Score is calculated by the sum of all the positive and negative votes.  For Example:  Billy votes Positive or ThumbsUp, his vote will be given a value of 1.  Jane votes Negative or ThumbsDown, her vote will be given a value of -1.  The score for Billy and Jane's votes will be (1 + -1 = 0), if John comes along and votes ThumbsUp then the score would be +1 (1 + -1 + 1 = 1).  The "percent" scoring method simply displays the percentage of ThumbsUp votes.

    History

    Version Date Author Description
    1.00 12/22/2009  rberinger/NeilW Initial Release 
    1.01 1/11/2010 NeilW CSS fixed for IE7

    Requirements

    • This template requires MindTouch version: 9.02 Lyons or Greater

    How do I install it?

    These instructions will help you install the template'AjaxVoter' on your wiki quickly and correctly.

    Perform the following steps:

    1. Copy the template source code:
      1. If the code in the "HTML Source" area below is not already visible and selected, click the "View" button to display and select it.
      2. Copy it to your clipboard (CTRL-C).
    2. Create a new template on your wiki:
      1. Select the Tools->Templates menu item on your wiki
      2. Click "New Page" button. This will open the editor on a new page with the title "Template:Page Title".
      3. Replace "Page Title" with the name of the template.
    3. Paste in the source code:
      1. In the editor, click the "View->Source" menu item to view the HTML source of the page.
      2. Select all text (CTRL-A).
      3. Paste in the template source code from the clipboard (CTRL-V).
    4. Click "Save" button.

    HTML Source for Template:AjaxVoter

    Additional installation instructions

    • Right Click and Download the 2 image files below and copy to a page in your own MindTouch Installation.
    • Find the following lines in the template you copied above and change the image locations to point to the images you uploaded on your MindTouch Installation.
      • <a href="#" id=('up_val_' ..@id)><img src="http://developer.mindtouch.com/@api/deki/files/5004/=Thumbs_upSQ.png" style="height:24px;width:24px;" /></a>       
        <a href="#" id=('down_val_' ..@id)><img src="http://developer.mindtouch.com/@api/deki/files/5005/=Thumbs_downSQ.png" style="height:24px; width:24px;" /></a>

    How do I use it?

    There are 2 properties created for each AjaxVoter.  One that is controlled by the "name" argument and another that is the voterid argument preceded with "Totals@".  i.e. If you created a voter with the name of TestVoter and a voterid of "TestVoter_id" there will be 2 properties created, "TestVoter" and "Totals@TestVoter_id".  The Totals@voterid property is a map that has 3 values {negative, positive, score}.  The Totals@ property allows you to quickly gather score totals without iterating the voter property list of maps to get the totals.  This would possibly be useful for vote summary pages or reports.   The unique voterID allows you to reset the voter by changing its name but keeping the Totals@ property the same so you do not have to change any summary pages gathering those results.

    To help clarify the intended use of the Totals@ property I will try to layout a scenerio using a documentation page as an example:

    As you can see I have a page rating voter at the top right of this documentation page.  Over time the score will change when people vote this template up or down.  If I notice a negative or low score that should indicate some much needed changes are required.  Once I make those changes I may want to "reset" the counter.  All I need to do is change the name parameter and the score will be reset.  However if I was to view a previous version of this page I will be able to see the score prior to the name change.  At the same time the voterid stays the same and the Totals@ property will get reset along with everything else but any slave functions (such as summary pages or templates) do not need to change because I only changed the name of the voter and not the voterid.  I know I'm almost confused myself but sit and picture it in your mind and all should become clear.

    Arguments

    Name
    Type
    Default
    Description
     name  str? voter  The Property name to save the Voter results under.  This argument is only REQUIRED if more than one voter is present on a page or if the vote result properties are stored on another page that is also housing another voter.
     pageid  num? page.id   The Page ID of the page to store the results of the voting 
     voterid  str?  n/a REQUIRED: This argument is a required argument.  It should be unique between all the voters on a single property destination page.  This allows for the totals@ property to stay with a particular voter even if the voter name changes.  Useful if you want to reset the voter scores by changing its name but keeping versioning intact while at the same time not requiring the change of any summary reports that rely on the Totals@ property for its results. 

     

    Examples

    {{ AjaxVoter{ name:'css_voter', voterid:'cssvoter_id', scoring:'sum' } }} This example uses the sum scoring method, which simply sums the positive (value = 1) and Negative (Value = -1) votes to provide a numeric score.  Hovering the mouse over the score will provide the actual percentage of Positive Vs. Negative votes.
    {{ AjaxVoter{ name:'example_voter', voterid:'examplevoter_id', scoring:'percent'} }} This example uses the "Percentage" scoring method.  The score range will be from 0% - 100% according to the number of positive votes.
    Login to Vote
    This example uses the sum scoring method, which simply sums the positive (value = 1) and Negative (Value = -1) votes to provide a numeric score.  Hovering the mouse over the score will provide the actual percentage of Positive Vs. Negative votes.
    Login to Vote
    This example uses the "Percentage" scoring method.  The score range will be from 0% - 100% according to the number of positive votes.

    Reference(s)

      Please direct any questions / comments to this forum thread..

    Credits/Special Thanks

    Thanks to NeilW for his aesthetics insights and his work for the percentage scoring while developing this template.

    Template/Extension Syntax Highlighted View

    DekiScript Source for Template: AjaxVoter

    <input type="hidden" id=('TheProperty_' ..@id) value=($0 ?? $name ?? 'voter') />
    <input type="hidden" id=('pageApi_' ..@id) value=($1 ?? $pageid ?? wiki.getpage(page.id).api) />
    <input type="hidden" id=('CurUserID_' ..@id) value=(user.id) />
    <input type="hidden" id=('UniqueId_' ..@id) value=($2 ?? $voterid ?? '') />
    
    var scoretype = ($3 ?? $scoring ?? 'sum');
    if(scoretype != 'sum' && scoretype != 'percent') {let scoretype = 'sum'}
    <input type="hidden" id=('Scoring_' ..@id) value=(scoretype) />
    
    if(($3 ?? $voterid ?? '') == '') {
        <p>    
            'Template:AjaxVoter (ERROR):  The $voterid Parameter MUST have a value and should be unique amongst all the AjaxVoters on this page.';
        </p>
    } else {
    
    <table>
    <tr>
    <td style="padding:0px; border:1px solid black; min-width:75px; width:75px">
    <div id=('voter-container_' ..@id) style="padding-bottom:5px">
        <div id=('vote-results-container_' ..@id)>
            <div id=('vote-results-percent_' ..@id) style="font-size:.8em;font-weight:bold;margin:auto;text-align:center"/>
            <div id=('vote-results_' ..@id) style="font-size:1.5em;font-weight:bold;margin:auto;text-align:center"/>
            <div id=('vote-loading_' ..@id) style="margin:auto;width:16px;height:16px;background-repeat: no-repeat; background-image:url('/skins/common/icons/anim-wait-circle.gif');" />
        </div>
        <div id=('results-footer_' ..@id) style="font-weight:bold;font-size:.8em;text-align:center;"></div>
        <div id=('icon-wrap_' ..@id) style="text-align:center;">
            if(!user.anonymous) {
                <a href="#" id=('down_val_' ..@id)><img src="http://developer.mindtouch.com/@api/deki/files/5005/=Thumbs_downSQ.png" style="height:24px;width:24px;" /></a>
                <a href="#" id=('up_val_' ..@id)><img src="http://developer.mindtouch.com/@api/deki/files/5004/=Thumbs_upSQ.png" style="height:24px;width:24px;" /></a>        
            }
            else 'Login to Vote';
        </div>
    </div>
    </td>
    </tr>
    </table>
    }
    <html><head>
    
    //    <link rel="stylesheet" type="text/css" href="" />
    //    <script type="text/javascript" src="" />
    
        <script type="text/javascript">"
    
            $(document).ready(function() {
    var scoring = $('#Scoring_"..@id.."').val();
    if (scoring == 'sum') $('#vote-results-container_"..@id.."').hover(ShowPercent"..@id..", ShowScore"..@id..");
    $('#vote-results-percent_"..@id.."').hide();
    
    $('#up_val_"..@id.."').click(function() {
        var thePageApi = $('#pageApi_"..@id.."').val();
        var thePropertyName = $('#TheProperty_"..@id.."').val();
    
        ReadProperty( thePageApi, thePropertyName, IncreaseVote_"..@id..");
        return false;
    });
    $('#down_val_"..@id.."').click(function() {
        var thePageApi = $('#pageApi_"..@id.."').val();
        var thePropertyName = $('#TheProperty_"..@id.."').val();
    
        ReadProperty( thePageApi, thePropertyName, DecreaseVote_"..@id..");
        return false;
    });
    
    GetVoteCount_"..@id.."();
    
            });
    
    function ShowPercent"..@id.."() {
        $('#vote-results_"..@id.."').hide();
        $('#vote-results-percent_"..@id.."').show();
    }
    function ShowScore"..@id.."() {
        $('#vote-results-percent_"..@id.."').hide();
        $('#vote-results_"..@id.."').show();
    }
    
    var SaveTotals_"..@id.." = function(curVal, status) {
    
        var voteCounts = '';
        var totalsPos = 0;
        var totalsNeg = 0;
        var thePageApi = $('#pageApi_"..@id.."').val();
        var thePropertyName = 'Totals@' + $('#UniqueId_"..@id.."').val();
    
        if(typeOf(curVal) == 'array') {
            for(var i = 0; i < curVal.length; i++) {
                if(curVal[i].vote == 1) {
                    totalsPos = totalsPos + 1;
                } else {
                    totalsNeg = totalsNeg + -1;
                }
            }
            voteCounts = {positive: totalsPos, negative: Math.abs(totalsNeg), score: totalsPos + totalsNeg};
        } else {
            voteCounts = {positive: 0, negative: 0, score: 0};
        }    
    
        AddUpdateProperty(thePageApi, thePropertyName, voteCounts, '', '');
    }
    
    
    var IncreaseVote_"..@id.." = function(curVal, status) {
        $('#icon-wrap_"..@id.."').hide();
        $('#vote-loading_"..@id.."').show();
        $('#vote-results_"..@id.."').hide();
    
        var voteCounts = new Array;
        var newUser = true;
        var thePageApi = $('#pageApi_"..@id.."').val();
        var thePropertyName = $('#TheProperty_"..@id.."').val();
        var CurUserID = $('#CurUserID_"..@id.."').val();
    
        if(typeOf(curVal) == 'array') {
            for(var i = 0; i < curVal.length; i++) {
                if(curVal[i].userid == CurUserID) {
                    curVal[i].vote = 1;
                    newUser = false;
                    break;
                }
            }
        } else {
            curVal = new Array;
        }
    
        if(newUser == true) {
            curVal[curVal.length] = {userid: CurUserID, vote: 1};
        }
    
        AddUpdateProperty(thePageApi, thePropertyName, curVal, GetVoteCount_"..@id..", SaveTotals_"..@id..");
    }
    
    var DecreaseVote_"..@id.." = function(curVal, status) {
        $('#icon-wrap_"..@id.."').hide();
        $('#vote-loading_"..@id.."').show();
        $('#vote-results_"..@id.."').hide();
    
        var voteCounts = new Array;
        var newUser = true;
        var thePageApi = $('#pageApi_"..@id.."').val();
        var thePropertyName = $('#TheProperty_"..@id.."').val();
        var CurUserID = $('#CurUserID_"..@id.."').val();
    
        if(typeOf(curVal) == 'array') {
            for(var i = 0; i < curVal.length; i++) {
                if(curVal[i].userid == CurUserID) {
                    curVal[i].vote = -1;
                    newUser = false;
                    break;
                }
            }
        } else {
            curVal = new Array;
        }
    
        if(newUser == true) {
            curVal[curVal.length] = {userid: CurUserID, vote: -1};
        }
    
        AddUpdateProperty(thePageApi, thePropertyName, curVal, GetVoteCount_"..@id..", SaveTotals_"..@id..");
    }
    
    var GetVoteCount_"..@id.." = function(parm, status) {
        $('#vote-loading_"..@id.."').show();
        $('#vote-results_"..@id.."').hide();
    
        var thePageApi = $('#pageApi_"..@id.."').val();
        var thePropertyName = $('#TheProperty_"..@id.."').val();
        var CurUserID = $('#CurUserID_"..@id.."').val();
    
        ReadProperty( thePageApi, thePropertyName, ShowVoteCount_"..@id..");
    }
    
    var ShowVoteCount_"..@id.." = function(curVal, status) {
        $('#icon-wrap_"..@id.."').show();
        $('#vote-results_"..@id.."').show();
        $('#vote-loading_"..@id.."').hide();
    
    // calculate vote totals
        var totalsPos = 0;
        var runTotal = 0;
        var totalVotes = 0;
        var bgcolor;
        var green = new Array(80,225,80);
        var red = new Array(245,110,100);
        var white = new Array(255,255,255);
        var scoring = $('#Scoring_"..@id.."').val();
        if(typeOf(curVal) == 'array') {
            for(var i = 0; i < curVal.length; i++) {
                runTotal = runTotal + curVal[i].vote
                if(curVal[i].vote > 0) totalsPos ++;
            }
            totalVotes = curVal.length;
        }
    // calculate and output final score and color
        if (scoring == 'sum') {
            if (runTotal == 0)
                bgcolor = 'rgb(' + white.join(',') + ')';
            else if (runTotal > 0) {
                runTotal = '+' + runTotal;
                bgcolor = 'rgb(' + green.join(',') + ')';
            }
            else if (runTotal < 0)
                bgcolor = 'rgb(' + red.join(',') + ')';
            // update percentage overlay
            var percentPos = Math.round((totalsPos / Math.max(totalVotes,1)) * 100);
            $('#vote-results-percent_"..@id.."').html('Pos: ' + percentPos + '%<br/>Neg: ' + (100-percentPos) + '%').css('background-color', bgcolor);
        }
        else {
            var outColor = new Array(192,192,192);
    
            if (totalVotes == 0)
                runTotal = 'NA';
            else {
                var degree = 2*totalsPos/totalVotes - 1;
                var i;
                for (i = 0; i < 3; i++)
                    outColor[i] = Math.round(white[i] + degree*
                        (degree > 0 ? green[i] - white[i] : white[i] - red[i]));
                if (scoring == 'percent') runTotal = Math.round(100*totalsPos/totalVotes) + '%';
                else                      runTotal = (degree > 0 ? '+' : '') + Math.round(100*degree);
            }
    
            bgcolor = 'rgb(' + outColor.join(',') + ')';
        }
        $('#vote-results_"..@id.."').html(runTotal).css('background-color', bgcolor);
        $('#results-footer_"..@id.."').html(totalVotes + ' votes');
    }
    
    function typeOf(obj) {
      if ( typeof(obj) == 'object' ) {
        if (obj.length) {
          return 'array';
        } else {
          return 'object';
        }
      } else {
          return typeof(obj);
      }
    }
    
    // This function is simply used to aid in debugging.
    var AlertMe = function(text, status) {
    
        alert(status + ':' + text);
    
    }
    
    function AddUpdateProperty(pageApi, propName, propValue, callback, totalsCallBack) {
    
        // property name to update
        var propertyname = 'urn:custom.mindtouch.com#' + propName;
        propValueJson = YAHOO.lang.JSON.stringify(propValue);
    
        // send AJAX request to find the property
        $.ajax({
        
            // set the uri for the properties API of the current page
            // add parameter to only list properties that match the requested name
            // add parameters to convert response to JSON and
            url: pageApi + '/properties?dream.out.format=json&names=' + Deki.url.encode(propertyname), 
            
            // set the request HTTP verb
            type: 'GET',
            cache: false,
            
            // check outcome of the request
            complete: function(xhr) {
            
                // check response status code
                if(xhr.status == 200) {
                
                    // evaluate respone JSON data
                    var data = eval('(' + xhr.responseText + ')');
    
                    // read property href and ETag
                    var href = data.property && data.property.contents['@href'];
                    var etag = data.property && data.property['@etag'];
                    
                    // check we the value was found
                    if(href && etag) {
    
                        // send AJAX request to update the property
                        $.ajax({ 
            
                            // set the request HTTP verb
                            url: href + '?dream.in.verb=PUT',
                            type: 'POST', 
            
                            // set the value of the updated property        
                            data: propValueJson, 
                            contentType: 'text/plain',
                            processData: false,
    
                            // add the 'ETag' header which checks if the property was modified since we read it
                            beforeSend: function(xhr) {
    
                                // NOTE (see Update 1 below): remove content-encoding added by Apache mod_deflate
                                etag = etag.replace('-gzip', '').replace('-bzip2', '').replace('-zip', '');
    
                                xhr.setRequestHeader('ETag', etag); 
                                return true; 
                            },
            
                            // check response status code
                            complete: function(xhr) {
    
                                // check the response status code
                                if(xhr.status == 200) {
                                    // alert('Property was successfully updated.');
                                    if(typeof callback == 'function') callback(true, xhr.status); else return true;
                                    if(typeof totalsCallBack == 'function') totalsCallBack(propValue, xhr.status); else return false;
                                } else {
                                    // alert('Unable to update the property. It may no longer exist (try recreating it), it may have been changed, or you don\\'t have permission to update it.');
                                    if(typeof callback == 'function') callback(false, xhr.status); else return false;
                                }
                            }
                        });
                    } else {
    
                        // CREATE PAGE PROPERTY
                        // send AJAX request using jQuery 
                        $.ajax({
                        
                            // set the uri for the properties API of the current page
                            url: pageApi + '/properties',
                            
                            // set the request HTTP verb
                            type: 'POST', 
                            
                            // set the value of the new property
                            data: propValueJson, 
                            contentType: 'text/plain',
                            processData: false,
                            
                            // add the 'Slug' header which sets the property name
                            beforeSend: function(xhr) { 
                                xhr.setRequestHeader('Slug', 'urn:custom.mindtouch.com#' + propName); 
                                return true; 
                            },
                            
                            // check outcome of the request
                            complete: function(xhr) {
                    
                                // check the response status code
                                if(xhr.status == 200) {
                                    // alert('Property was successfully created.');
                                    if(typeof callback == 'function') callback(true, xhr.status); else return true;
                                    if(typeof totalsCallBack == 'function') totalsCallBack(propValue, xhr.status); else return false;
                                } else {
                                    // alert('Unable to create the property. It may already exist (try deleting it) or you don\\'t have permission to create it.');
                                    if(typeof callback == 'function') callback(true, xhr.status); else return true;
                                }
                            }
                        });  // End Ajax (Create Property)
    
                    }
                } else {
                    // alert('Unable to query the property. You don\\'t have permission to read it.');
                    if(typeof callback == 'function') callback(false, xhr.status); else return false;
                }
            }
        });  // End AJAX (Update Property)
    
    }  // End Function AddUpdateProperty
    
    
    function DeleteProperty(pageApi, propName, callback) {
    
        // property name to update
        var propertyname = 'urn:custom.mindtouch.com#' + propName;
    
        // send AJAX request to find the property
        $.ajax({
        
            // set the uri for the properties API of the current page
            // add parameter to only list properties that match the requested name
            // add parameters to convert response to JSON and
            url: pageApi + '/properties?dream.out.format=json&names=' + Deki.url.encode(propertyname), 
            
            // set the request HTTP verb
            type: 'GET',
            cache: false,
            
            // check outcome of the request
            complete: function(xhr) {
            
                // check response status code
                if(xhr.status == 200) {
                
                    // evaluate respone JSON data
                    var data = eval('(' + xhr.responseText + ')');
    
                    // read property href
                    var href = data.property && data.property.contents['@href'];
                    
                    // check we the value was found
                    if(href) {
    
                        // send AJAX request to delete the property
                        $.ajax({ 
            
                            // set the request HTTP verb
                            url: href + '?dream.in.verb=DELETE',
                            type: 'POST', 
    
                            // check response status code
                            complete: function(xhr) {
    
                                // check the response status code
                                if(xhr.status == 200) {
                                    // alert('Property was successfully deleted.');
                                    if(typeof callback == 'function') callback(true, xhr.status); else return true;
                                } else {
                                    // Who cares we were going to trash it anyhow
                                    // alert('Unable to delete the property. It may not exist (try creating it) or you don\\'t have permission to delete it.');
                                    if(typeof callback == 'function') callback(true, xhr.status); else return true;
                                }
                            }
                        });
                    } else {
                        // Who cares we were going to trash it anyhow
                        // alert('Unable to find the property. It may not exist (try creating it).');
                        if(typeof callback=='function') callback(true, xhr.status); else return true;
                    }
                } else {
                    alert('Unable to query the property. You don\\'t have permission to read it.');
                    if(typeof callback=='function') callback(false, xhr.status); else return false;
                }
            }
        });
    
    } // End Function DeleteProperty
    
    
    function ReadProperty(pageApi, propName, callback) {
    
        // property name to read
        var propertyname = 'urn:custom.mindtouch.com#' + propName;
    
        // send AJAX request using jQuery
        $.ajax({
        
            // set the uri for the properties API of the current page
            // add parameter to only list properties that match the requested name
            // add parameters to convert response to JSON and
            url: pageApi + '/properties?dream.out.format=json&names=' + Deki.url.encode(propertyname), 
            
            // set the request HTTP verb
            type: 'GET',
            cache: false,
            async: false,
            
            // check outcome of the request
            complete: function(xhr) {
            
                // check response status code
                if(xhr.status == 200) {
                
                    // evaluate response JSON data
                    var data = eval('(' + xhr.responseText + ')');
    
                    // read property value
                    var value = data.property && data.property.contents['#text'];
                    
                    // check we the value was found
                    if(value) {
    
                        value = YAHOO.lang.JSON.parse(value);
                         // alert('Property was successfully read.\\nIts value is \\'' + value + '\\'');
                        if(typeof callback == 'function') callback(value, xhr.status); else return value;
                    } else {
                        // alert('Unable to find the property. It may not exist (try creating it).');
                        if(typeof callback == 'function') callback(value, xhr.status); else return null;
                    }
                } else {
                    // alert('Unable to query the property. You don\\'t have permission to read it.');
                    if(typeof callback == 'function') callback(value, xhr.status); else return null;
                }
            }
        });
    
    }
            
        "</script>
    
        <style type="text/css">"
            
        "</style>
    
    
    </head></html> 
    

    Disclaimers

    None.

     

     

    Was this page helpful?
    Tag page
    You must login to post a comment.

    Copyright © 2011 MindTouch, Inc. Powered by