﻿/*!
*
* Webshop basket module handling
* 
*/

var time = 250;
var distance = 10;
var beingShown = false;
var shown = false;
var popup;
var addItem;
var addItemDis;
var shopItems;
var basketPage;
var totalProducts;
var totalPrice;

$(document).ready(function () {
    UpdateBasketWithItems();
});

function addEventHandlersForBasket() {
    $('.remove-item a').click(function (e) {
        RemoveItemFromBasket($(this).attr("pid"));
    });
}
function UpdateBasketWithItems() {
    basketPage = $("#basket-page-items");
    shopItems = $("#shopping-items");
    totalProducts = $("#total-products");
    totalPrice = $("#total-price");
    $.ajax({
        type: "POST",
        contentType: "application/json",
        dataType: "json",
        url: "/Services/ScriptServices.asmx/GetBasketItems",
        success: function (json) {
            var data = $.parseJSON(json.d);
            if (data != null) {
                if(data.ShopProducts.length > 0)
                    $('#basket-top').show();
                shopItems.text(data.ShopProducts.length);
                
                if (location.href.indexOf("Basket.aspx") != -1) {
                    var items = [];
                    if (data.ShopProducts.length != 0) {
                        $.each(data.ShopProducts, function (index) {
                            items.push('<div class="basket-content clear" id="product' + data.ShopProducts[index].Id + '">');
                            items.push('    <div class="product">');
                            items.push('        <div class="image-box-small">');
                            items.push('            <div class="image-inner">');
                            items.push('                <img src="../Handlers/ImageCacheThumb.ashx?value=80&contentid=' + data.ShopProducts[index].Assignment.AssignmentContent.ImageId + '" runat="server" />');
                            items.push('            </div>');
                            items.push('            <div class="overlay"></div>');
                            items.push('        </div>');
                            items.push('    </div>');
                            items.push('    <div class="description"><h3 class="font-100 nomargin">' + data.ShopProducts[index].Title + '</h3><p>' + data.ShopProducts[index].ShortDescription + '</p></div>');
                            items.push('    <div class="price">' + data.ShopProducts[index].Price + ' DKK</div>');
                            items.push('    <div class="remove-item">');
                            items.push('        <a pid="' + data.ShopProducts[index].Id + '" title="Remove item from basket">x</a>');
                            items.push('    </div>');
                            items.push('</div>');
                        });
                        var html = items.join('');
                        basketPage.html(html);
                        
                        totalProducts.html(data.TotalPrice + " DKK");
                        totalPrice.html(data.TotalPrice + " DKK");
                    }
                    else {
                        $("#empty-text").show();
                        totalProducts.html("0 DKK");
                        totalPrice.html("0 DKK");

                    }
                    //                    totalProducts.html(data.TotalPrice + " DKK");
                    //                    totalPrice.html(data.TotalPrice + " DKK");
                    addEventHandlersForBasket();
                }
            }
            else {
                $('#basket-top').hide();
                totalProducts.html("0 DKK");
                totalPrice.html("0 DKK");
                shopItems.text("0 ");
                $("#empty-text").show();
            }
        },
        error: function (jqXHR, textStatus, errorThrown) {
        }
    });
}
function RemoveItemFromBasket(productId) {
    $.ajax({
        type: "POST",
        contentType: "application/json",
        dataType: "json",
        url: "/Services/ScriptServices.asmx/RemoveItemFromBasket",
        data: '{ shopProductId: ' + JSON.stringify(productId) + '}',
        success: function (json) {
            var data = $.parseJSON(json.d);
            if (data.ShopProducts.length != null && data.ShopProducts.length != 0) {
                shopItems.text(data.ShopProducts.length);
                totalProducts.html(data.TotalPrice + " DKK");
                totalPrice.html(data.TotalPrice + " DKK");
            }
            else {
                shopItems.text("0");
                totalProducts.html(data.TotalPrice + " DKK");
                totalPrice.html(data.TotalPrice + " DKK");
                $("#empty-text").show();
            }
            $("#product" + productId).fadeOut(500, function () { $("#product" + productId).remove(); });
        },
        error: function (jqXHR, textStatus, errorThrown) {
            //alert(JSON.stringify(jqXHR));
        }
    });
}
function AddItemToBasket(productId) {
    addItemDis = $("#btnAddItemDisabled").show();
    addItem = $("#btnAddItemToBasket").hide();
    popup = $("#item-added").css('opacity', 0);
    var boxMiddle = $(".box-middle");

    if (beingShown || shown) {
        return;
    }
    else {
        beingShown = true;
        popup.css({
            top: 20,
            display: 'block'
        })
    };

    $.ajax({
        type: "POST",
        contentType: "application/json",
        dataType: "json",
        url: "/Services/ScriptServices.asmx/AddItemAndUpdateBasket",
        data: '{ shopProductId: ' + JSON.stringify(productId) + '}',
        success: function (json) {
            var data = $.parseJSON(json.d);
            if (data != null) {
                $('#basket-top').show();
                shopItems.text(data.ShopProducts.length);
                boxMiddle.text("Item added to basket");
            }
            else
                boxMiddle.text("Item already exists");

            popup.animate({
                top: '+=' + distance + 'px',
                opacity: 1
            }, time, 'swing', function () {
                beingShown = false;
                shown = true;
            });
            setTimeout('Hide();', 3000);
        },
        error: function (jqXHR, textStatus, errorThrown) {
        }
    });
}
function Hide() {
    addItemDis.hide();
    addItem.show();
    beingShown = false;
    shown = true;
    popup.animate({
        top: '-=' + distance + 'px',
        opacity: 0
    }, time, 'swing', function () {
        shown = false;
        popup.css('display', 'none');
    });
}


/*!
*
* Image feed controller
* 
*/

var timerOn = 0;
var timer;
var feedIndex = 35;
var feedsToLoad = 1;
var imgsToLoad = 5;
var loadMore = false;
var path1 = "http://syncrowd.com/Handlers/ImageCacheThumb.ashx?value=165&contentid=";
//var path3 = "http://syncrowd.com/Handlers/ImageCache.ashx?value=300&contentid=";
var path3 = "http://syncrowd.com/Handlers/AsyncImageHandler.ashx?value=300&contentid=";
//var path3 = "http://localhost:6806/Handlers/testHandler.ashx?value=300&contentid=";
//var path3 = "http://localhost:6806/Handlers/AsyncImageHandler.ashx?value=300&contentid=";
//var path2 = "http://syncrowd.com/image/?value=120&contentid=";
var path2 = "http://syncrowd.com/Handlers/AsyncImageHandler.ashx?value=120&contentid=";
//var path2 = "http://localhost:6806/Handlers/testHandler.ashx?value=120&contentid=";

function loadImage(img, id) {
    img.onload = null;
    img.src = path1 + id;
}
function loadImageThumb(img, id) {
    img.onload = null;
    img.src = path2 + id;
}
function loadImageLarge(img, id) {
    img.onload = null;
    img.src = path3 + id;
}
//function loadImageThumb(img, id) {
//    img.onload = null;
//    img.src = path2 + id;
//}
function startFeedTimeout() {
    var items = [];
    $.ajax({
        url: "http://services.syncrowd.com/SyncrowdScriptMethods.asmx/GetLiveFeed",
        dataType: "jsonp",
        success: function (json) {
            var data = $.parseJSON(json.d);
            var count = 0;
            var items = [];
            if (data.length > 30)
                loadMore = true;
            for (i = 0; i < data.length; i++) {
                if (count == 0) {
                    items.push("<div class='ajaxBatch' style='margin-top:0px;display:block'>");
                }
                items.push('<span class="left"><div class="feed-img"><a style="display:block"><img style="display:block" onload="loadImage(this,' + data[i].Id + ');" src="/Files/Graphics/Loaders/img-pre.gif" /></a></div></span>');
                if (count == 4) {
                    items.push("</div>");
                    count = -1;
                }
                count++;
            }
            var html = items.join('');
            $('#UpdatesContainer').append(html);
        },
        error: function (jqXHR, textStatus, errorThrown) {
            //            alert(JSON.stringify(jqXHR));
            //            alert(textStatus);
            //            alert(errorThrown);
        }
    });
    if (!timerOn) {
        timerOn = 1;
        loadFeedTimeout();
    }
}
function loadFeedTimeout() {
    $("#UpdatesContainer .ajaxBatch:last").css("marginTop", "-99px").prependTo("#UpdatesContainer").animate({ marginTop: "0" }, 800, "linear");
    timer = setTimeout("loadFeedTimeout()", 4000);
    if (feedsToLoad > 0) {
        $.ajax({
            url: "http://services.syncrowd.com/SyncrowdScriptMethods.asmx/GetSingleFeed",
            dataType: "jsonp",
            data: { feedIndex: JSON.stringify(feedIndex), count: JSON.stringify(imgsToLoad) },
            success: function (json) {
                var data = $.parseJSON(json.d);
                var count = 0;
                var items = [];
                for (i = 0; i < data.length; i++) {
                    if (count == 0) {
                        items.push("<div class='ajaxBatch'>");
                    }
                    items.push('<span class="left"><div class="feed-img"><a><img onload="loadImage(this, ' + data[i].Id + ');" src="/Files/Graphics/Loaders/img-pre.gif" /></a></div></span>');
                    if (count == 4) {
                        items.push("</div>");
                        count = -1;
                    }
                    count++;
                }
                var html = items.join('');
                $('#UpdatesContainer').append(html);
            },
            error: function (jqXHR, textStatus, errorThrown) {
            }
        });
        feedIndex += 5;
        feedsToLoad = feedsToLoad - 1;
    }
}
function startTimeout(assignId) {
    var items = [];
    $.ajax({
        url: "http://services.syncrowd.com/SyncrowdScriptMethods.asmx/GetAssignmentFeed",
        dataType: "jsonp",
        data: { assignmentId: JSON.stringify(assignId) },
        success: function (json) {
            var data = $.parseJSON(json.d);
            var count = 0;
            var items = [];
            if (data.length > 30)
                loadMore = true;
            for (i = 0; i < data.length; i++) {
                if (count == 0) {
                    items.push("<div class='ajaxBatch'>");
                }
                items.push('<span class="left"><div class="feed-img"><a><img onload="loadImage(this,' + data[i].Id + ');" src="/Files/Graphics/Loaders/img-pre.gif" /></a></div></span>');
                if (count == 4) {
                    items.push("</div>");
                    count = -1;
                }
                count++;
            }
            var html = items.join('');
            $('#UpdatesContainer').append(html);

            if (!timerOn && loadMore) {
                timerOn = 1;
                loadTimeout(assignId);
            }
        },
        error: function (jqXHR, textStatus, errorThrown) {
        }
    });
}
function loadTimeout(assignId) {
    $("#UpdatesContainer .ajaxBatch:last").css("marginTop", "-99px").prependTo("#UpdatesContainer").animate({ marginTop: "0" }, 800, "linear");
    timer = setTimeout("loadTimeout(" + assignId + ")", 4000);
    $.ajax({
        url: "http://services.syncrowd.com/SyncrowdScriptMethods.asmx/GetSingleAssignmentFeed",
        dataType: "jsonp",
        data: { assignmentId: JSON.stringify(assignId), feedIndex: JSON.stringify(feedIndex), count: JSON.stringify(imgsToLoad) },
        success: function (json) {
            var data = $.parseJSON(json.d);
            var count = 0;
            var items = [];
            for (i = 0; i < data.length; i++) {
                if (count == 0) {
                    items.push("<div class='ajaxBatch'>");
                }
                items.push('<span class="left"><div class="feed-img"><a><img onload="loadImage(this, ' + data[i].Id + ');" src="/Files/Graphics/Loaders/img-pre.gif" /></a></div></span>');
                if (count == 4) {
                    items.push("</div>");
                    count = -1;
                }
                count++;
            }
            var html = items.join('');
            $('#UpdatesContainer').append(html);
        },
        error: function (jqXHR, textStatus, errorThrown) {
        }
    });
    feedIndex += 5;
}


/*!
*
* Filename changer for fake fileuploader box
* 
*/

function ChangeFilename(uploadId, controlId) {
    var filename = document.getElementById(uploadId).value.replace(/^.*\\/, '');
    var ctrl = document.getElementById(controlId);
    ctrl.value = filename;
}

/*!
*
* File PROCESSOR
* 
*/

var result;
function CheckStatus() {
    if (result == undefined || result == "") {
        setTimeout("CallServer()", 500);
    }
}
//function CallServer(arg, context) {
//    WebForm_DoCallback('ctl00', arg, ReceiveServerData, context, null, true);
//}
function ReceiveServerData(retValue) {
    var resultArr = retValue.split(";");
    result = resultArr[0];
    if (result != "SUCCESS") {
        $("#processPanel").slideDown("slow");
        document.getElementById("file-progress").innerHTML = resultArr[2] + "%";
        setTimeout("CallServer()", 500);
    }
    else {
        result = "";
        $("#processPanel").slideUp("slow");
        var val = resultArr[1];
        if (val != "") {
            if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { //test for MSIE x.x;
                window.open("/Clients/Syncrowd-Research?file=" + val, "");
            }
            else
                document.getElementById("btnInvokeDl").click();
        }
    }
}
function CancelPdfProcess() {
    $.ajax({
        type: "POST",
        url: "/Services/ScriptServices.asmx/SetPdfAbortFlag",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (json) {
        },
        error: function (jqXHR, textStatus, errorThrown) {
            alert(JSON.stringify(jqXHR));
            alert(textStatus);
            alert(errorThrown);
        }
    });
}

/*!
*
* Survey selection handling
* 
*/

//function ShowImage(sId) {
//    var button = document.getElementById("select" + sId);
//    button.style.visibility = 'visible';
//}
//function HideImage(sId, hdfId) {
//    var surveys = document.getElementById(hdfId);
//    if (surveys.value.indexOf(sId) != -1) {
//        var button = document.getElementById("select" + sId);
//        var sImg = document.getElementById("img" + sId);
//        sImg.src = "../Files/Graphics/Dashboard/tick.png";
//        button.style.visibility = 'visible';
//    }
//    else {
//        var button = document.getElementById("select" + sId);
//        button.style.visibility = 'hidden';
//    }
//}
var currPanel = "Completed";
function GetAssignmentCallback(assignment) {
    var arr = assignment.split('*');
    $("#surveycontainer" + currPanel).append('<div id="' + arr[0] + '" style="margin-top:10px">-' + arr[1] + '</div>');
}
function SelectSurvey(sId, hdfId, panel) {
    currPanel = panel;
    var surveys = document.getElementById(hdfId);
    var sButton = document.getElementById("select" + sId);
    var sImg = document.getElementById("img" + sId);
    if (surveys.value.indexOf(sId) != -1) {
        var array = surveys.value.split(',');
        surveys.value = surveys.value.replace(sId + ",", "");
        //sButton.style.visibility = 'hidden';
        $("#select" + sId).removeAttr("style");
        sImg.src = "../Files/Graphics/Dashboard/add.png";
        $("#" + sId).remove();
    }
    else {
        surveys.value += sId + ",";
        //sButton.style.visibility = 'visible'
        $("#select" + sId).css("visibility", "visible"); 
        sImg.src = "../Files/Graphics/Dashboard/tick.png";
        PageMethods.GetAssignmentText(sId, GetAssignmentCallback);
    }
//    if (panel == "completed")
//        document.getElementById("btnInitPanels").click();
}
function selectEntry(eId, hdfId) {
    var selected = document.getElementById(hdfId);
    var eButton = document.getElementById("select" + eId);
    var eImg = document.getElementById("img" + eId);
    if (selected.value.indexOf(eId) != -1) {
        var array = selected.value.split(',');
        selected.value = selected.value.replace(eId + ",", "");
        eButton.setAttribute("class", "entry-actions");
        eImg.setAttribute("class", "sy-button-check");
        $("#" + eId).remove();
    }
    else {
        selected.value += eId + ",";
        eImg.setAttribute("class", "sy-button-uncheck");
        eButton.setAttribute("class", "entry-actions-selected");
    }
}
function GetProductCallback(product) {
    var arr = product.split('*');
    $("#selected-products").append('<div id="' + arr[0] + '" style="margin-top:10px">-' + arr[1] + '</div>');
}
function SelectProduct(sId, hdfId, panel) {
    currPanel = panel;
    var surveys = document.getElementById(hdfId);
    var sButton = document.getElementById("select" + sId);
    var sImg = document.getElementById("img" + sId);
    if (surveys.value.indexOf(sId) != -1) {
        var array = surveys.value.split(',');
        surveys.value = surveys.value.replace(sId + ",", "");
        $("#select" + sId).removeAttr("style");
        sImg.src = "../Files/Graphics/Dashboard/add.png";
        $("#" + sId).remove();
    }
    else {
        surveys.value += sId + ",";
        $("#select" + sId).css("visibility", "visible");
        sImg.src = "../Files/Graphics/Dashboard/tick.png";
        PageMethods.GetProductText(sId, GetProductCallback);
    }
}

/*!
*
* Popup Image controller
* 
*/

var w = 1
var h = 1

if (document.getElementById || document.all)
    document.write('<div id="trailimageid" style="position:absolute;visibility:hidden;left:0px;overflow:hidden;top:-1000px;width:1px;height:1px;border:5px solid #000; background:#000;z-index:400"><img id="ttimg" src="../Files/Graphics/Search/trail-bg.png" alt="Loading.." /><br /></div>')

function gettrailobj() {
    if (document.getElementById) return document.getElementById("trailimageid").style
    else if (document.all) return document.all.trailimagid.style
}

function truebody() {
    return (!window.opera && document.compatMode && document.compatMode != "BackCompat") ? document.documentElement : document.body
}

function hidetrail() {
    document.onmousemove = "";
    document.getElementById('ttimg').src = '../Files/Graphics/Search/trail-bg.png';
    gettrailobj().visibility = "hidden";
    gettrailobj().left = -1000;
    gettrailobj().top = 0;
}
function showtrail(width, height, file, imgText) {
    if (navigator.userAgent.toLowerCase().indexOf('opera') == -1) {
        w = width
        h = height
        // followmouse()
        document.getElementById('ttimg').src = file
        document.onmousemove = followmouse
        gettrailobj().visibility = "visible"
        gettrailobj().width = w + "px"
        gettrailobj().height = h + "px"
    }
}
function followmouse(e) {
    if (navigator.userAgent.toLowerCase().indexOf('opera') == -1) {
        var xcoord = 20
        var ycoord = 20

        if (typeof e != "undefined") {
            xcoord += e.pageX
            ycoord += e.pageY
        }
        else if (typeof window.event != "undefined") {
            xcoord += truebody().scrollLeft + event.clientX
            ycoord += truebody().scrollTop + event.clientY
        }

        var docwidth = document.all ? truebody().scrollLeft + truebody().clientWidth : pageXOffset + window.innerWidth - 15
        var docheight = document.all ? Math.max(truebody().scrollHeight, truebody().clientHeight) : Math.max(document.body.offsetHeight, window.innerHeight)

        if (xcoord + w + 3 > docwidth)
            xcoord = xcoord - w - (20 * 2)

        if (ycoord - truebody().scrollTop + h > truebody().clientHeight)
            ycoord = ycoord - h - 20;

        gettrailobj().left = xcoord + "px"
        gettrailobj().top = ycoord + "px"
    }
}


/*!
*
* Old browser support for JSON object
* 
*/

// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

var JSON;
if (!JSON) {
    JSON = {};
}

(function () {
    "use strict";

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return isFinite(this.valueOf()) ?
                this.getUTCFullYear() + '-' +
                f(this.getUTCMonth() + 1) + '-' +
                f(this.getUTCDate()) + 'T' +
                f(this.getUTCHours()) + ':' +
                f(this.getUTCMinutes()) + ':' +
                f(this.getUTCSeconds()) + 'Z' : null;
        };

        String.prototype.toJSON =
            Number.prototype.toJSON =
            Boolean.prototype.toJSON = function (key) {
                return this.valueOf();
            };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"': '\\"',
            '\\': '\\\\'
        },
        rep;


    function quote(string) {

        // If the string contains no control characters, no quote characters, and no
        // backslash characters, then we can safely slap some quotes around it.
        // Otherwise we must also replace the offending characters with safe escape
        // sequences.

        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string' ? c :
                '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }


    function str(key, holder) {

        // Produce a string from holder[key].

        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            mind = gap,
            partial,
            value = holder[key];

        // If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

        // If we were called with a replacer function, then call the replacer to
        // obtain a replacement value.

        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }

        // What happens next depends on the value's type.

        switch (typeof value) {
            case 'string':
                return quote(value);

            case 'number':

                // JSON numbers must be finite. Encode non-finite numbers as null.

                return isFinite(value) ? String(value) : 'null';

            case 'boolean':
            case 'null':

                // If the value is a boolean or null, convert it to a string. Note:
                // typeof null does not produce 'null'. The case is included here in
                // the remote chance that this gets fixed someday.

                return String(value);

                // If the type is 'object', we might be dealing with an object or an array or
                // null.

            case 'object':

                // Due to a specification blunder in ECMAScript, typeof null is 'object',
                // so watch out for that case.

                if (!value) {
                    return 'null';
                }

                // Make an array to hold the partial results of stringifying this object value.

                gap += indent;
                partial = [];

                // Is the value an array?

                if (Object.prototype.toString.apply(value) === '[object Array]') {

                    // The value is an array. Stringify every element. Use null as a placeholder
                    // for non-JSON values.

                    length = value.length;
                    for (i = 0; i < length; i += 1) {
                        partial[i] = str(i, value) || 'null';
                    }

                    // Join all of the elements together, separated with commas, and wrap them in
                    // brackets.

                    v = partial.length === 0 ? '[]' : gap ?
                    '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
                    '[' + partial.join(',') + ']';
                    gap = mind;
                    return v;
                }

                // If the replacer is an array, use it to select the members to be stringified.

                if (rep && typeof rep === 'object') {
                    length = rep.length;
                    for (i = 0; i < length; i += 1) {
                        if (typeof rep[i] === 'string') {
                            k = rep[i];
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                } else {

                    // Otherwise, iterate through all of the keys in the object.

                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                }

                // Join all of the member texts together, separated with commas,
                // and wrap them in braces.

                v = partial.length === 0 ? '{}' : gap ?
                '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
                '{' + partial.join(',') + '}';
                gap = mind;
                return v;
        }
    }

    // If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {

            // The stringify method takes a value and an optional replacer, and an optional
            // space parameter, and returns a JSON text. The replacer can be a function
            // that can replace values, or an array of strings that will select the keys.
            // A default replacer method can be provided. Use of the space parameter can
            // produce text that is more easily readable.

            var i;
            gap = '';
            indent = '';

            // If the space parameter is a number, make an indent string containing that
            // many spaces.

            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }

                // If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === 'string') {
                indent = space;
            }

            // If there is a replacer, it must be a function or an array.
            // Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                    typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }

            // Make a fake root object containing our value under the key of ''.
            // Return the result of stringifying the value.

            return str('', { '': value });
        };
    }


    // If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {

            // The parse method takes a text and an optional reviver function, and returns
            // a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

                // The walk method is used to recursively walk the resulting structure so
                // that modifications can be made.

                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }


            // Parsing happens in four stages. In the first stage, we replace certain
            // Unicode characters with escape sequences. JavaScript handles many characters
            // incorrectly, either silently deleting them, or treating them as line endings.

            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

            // In the second stage, we run the text against regular expressions that look
            // for non-JSON patterns. We are especially concerned with '()' and 'new'
            // because they can cause invocation, and '=' because it can cause mutation.
            // But just to be safe, we want to reject all unexpected forms.

            // We split the second stage into 4 regexp operations in order to work around
            // crippling inefficiencies in IE's and Safari's regexp engines. First we
            // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
            // replace all simple value tokens with ']' characters. Third, we delete all
            // open brackets that follow a colon or comma or that begin the text. Finally,
            // we look to see that the remaining characters are only whitespace or ']' or
            // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/
                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

                // In the third stage we use the eval function to compile the text into a
                // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
                // in JavaScript: it can begin a block or an object literal. We wrap the text
                // in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

                // In the optional fourth stage, we recursively walk the new structure, passing
                // each name/value pair to a reviver function for possible transformation.

                return typeof reviver === 'function' ?
                    walk({ '': j }, '') : j;
            }

            // If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('JSON.parse');
        };
    }
} ());
