<commit_before>$.ajaxSetup ({
    cache: false
});
$.support.cors = true; // makes IE8 and IE9 support CORS
if (!window.console) {
    console = {log: function() {}};
}
var tiLinkColor = "#FF4E00"
var ajaxLoadImg = "";
var ajaxLoadImgTransparent = "";
var collectionAliases = []
var currentUserInputValue = ""
var currentlyUpdating = false

/*****************************************************************************
 * create collection page
 ****************************************************************************/

exampleStrings = {
    paste_input:"10.1038/171737a0\n13054692",
    paste_webpages: "https://github.com/egonw/cdk\nhttp://www.slideshare.net/cavlec/open-content",
    crossref_input: "Watson : A Structure for Deoxyribose Nucleic Acid",
    name: "My Collection"
}

inputExamplesIClickHandler = function(thisInput) {

}

flatten = function(idsArr) {
    // flattens id values that are themselves arrays (like github)
    var aliases = [];
    numIds = idsArr.length;
    for (var i=0; i<numIds; i++ ) {
        var id = idsArr[i][1]
        var namespace = idsArr[i][0]
        if( Object.prototype.toString.call( id ) === '[object Array]' ) {
            var strVal = id.join(",")
        }
        else {
            var strVal = id
        }
        aliases.push([namespace, strVal])
    }
    return(aliases)
}

addCollectionIds = function(idsArr, updateInfoLoc$, itemsAddedMsg$, showZeros) {
    if (itemsAddedMsg$ === undefined) {
        var itemsAddedMsg$ = $("items added")
    }

    var startingCollectionLength = collectionAliases.length
    var newIds = flatten(idsArr);

    // make an object with the unique key values
    var uniqueNamespaceIdPairs = {}
    for  (var i=0; i<collectionAliases.length; i++){
        var namespaceIdPair = collectionAliases[i].join(":")
        uniqueNamespaceIdPairs[namespaceIdPair] = 1
    }
    console.log(uniqueNamespaceIdPairs)

    for (var i=0; i < newIds.length; i++){
        var newNamespaceIdPair = newIds[i].join(":")
        if (!uniqueNamespaceIdPairs[newNamespaceIdPair]) {
            collectionAliases.push(newIds[i])
        }
    }
    $("div.progressbar").empty()
    $("span.loading").remove()

    // how many items are in the new collection now?
    var endingCollectionLength = collectionAliases.length;
    numIdsAdded = endingCollectionLength - startingCollectionLength;
    $("#artcounter span.count").html(endingCollectionLength);
    if (numIdsAdded || showZeros) {
        updateInfoLoc$.siblings("span.added").remove()
        $(""+numIdsAdded+"")
            .append(itemsAddedMsg$)
            .insertAfter(updateInfoLoc$)

        updateInfoLoc$.siblings("span.added")
            .find("span.count")
                .css({color: "#ff4e00"})
                .animate({color: "#555555"}, 1500)

    }
    return true;

}

// puts the textarea-entered ids in a format that addcollectionIds likes
parseTextareaArtifacts = function(str) {
    var ids = str.split("\n");
    var ret = [];
    for (i=0; i<ids.length; i++){
        var artifact = [];
        var thisId = ids[i];
        if (thisId.indexOf(":") != -1) {
            artifact[0] = thisId.split(':')[0]; // namespace
            artifact[1] = thisId.substr(artifact[0].length + 1) // id

            // handle urls:
            if ((artifact[0] == "http") || (artifact[0] == "https")) {
                if (thisId.indexOf("http://dx.doi.org/") != -1) {
                    artifact[0] = "doi";
                    artifact[1] = thisId.replace("http://dx.doi.org/", "");
                } else {
                    artifact[0] = "url";
                    artifact[1] = thisId;
                }
            }
        }
        else {
            if (thisId.length > 0) {
                var isnum_with_possible_period = /^[\d\.]+$/.test(thisId)
                // handle dois entered without the doi prefix
                if (thisId.substring(0,3) == "10.") {
                    artifact[0] = "doi"
                } else if (isnum_with_possible_period && (thisId.length > 5) && (thisId.length <= 8)) {
                    // definition of pmid from http://www.nlm.nih.gov/bsd/mms/medlineelements.html#pmid
                    // this doesn't catch short PMIDs, but that's ok
                    artifact[0] = "pmid"
                }
                else {
                   artifact[0] = "unknown"
                }
                artifact[1] = thisId
            }
        }
        if (typeof artifact[1] != "undefined") {
            ret.push(artifact);
        }
    }
    return ret;
}

progressbar = function(total, done, loc$) {
    percentDone = Math.round(done / total * 100)
    loc$.html(percentDone + "% done...")
}
bibtexUploadDone = function(numItemsTotal, aliases) {
    var numFailedItems = numItemsTotal - aliases.length
    var msg$ = $(ich.bibtexUploadDoneMsg({numFailedItems: numFailedItems}))
    msg$.find("a.help-text")
        .clickover()
    addCollectionIds(aliases, $("div.fileupload"), msg$, true)
}

update_bibtex_progress = function(query_hash) {
    $.ajax({
               url: "http://"+api_root+"/provider/bibtex/memberitems/"+query_hash+"?method=async",
               type: "GET",
               dataType: "json",
               success: function(response,status,xhr){
                   console.log("searched using query_hash " + query_hash)
                   console.log(response)
                   if (response.pages == response.complete) {
                       aliases = []
                       for (i=0; i<response.memberitems.length; i++) {
                           var aliases = aliases.concat(response.memberitems[i])
                       }
                       bibtexUploadDone(response.number_entries, aliases)
                   }
                   else {
                       progressbar(response.pages, response.complete, $("#bibtex_toggler_contents div.progressbar"))
                       setTimeout(function () {
                           update_bibtex_progress(query_hash)
                       }, 500)
                   }
               },
               error: function(XMLHttpRequest, textStatus, errorThrown) {
                   $("span.loading").remove()
                   $("div.fileupload span.added").remove()
                   $("div.fileupload").append("sorry, there was an error.")
               }
           });  }

upload_bibtex = function(files) {
    var fileInput = $("li #input_bibtex")[0];
    var file = fileInput.files[0];
    var formData = new FormData();
    formData.append('file', file);
    $("span.btn-file").remove()
    $(""+ajaxLoadImg+"").insertBefore("div.fileupload div.progressbar");

    console.log("starting ajax request now.")

    progressbar(1, 0, $("#bibtex_toggler_contents div.progressbar"))
    $.ajax({
            url: "http://"+api_root+'/provider/bibtex/memberitems',
            type: "POST",
            processData: false,
            contentType: false,
            timeout: 120*1000,  // 120 seconds, because bibtex is very slow
            data: formData,
            dataType: "json",
            success:  function(response,status,xhr){
                query_hash = response.query_hash
                console.log("started update request; got this query_hash back: " + query_hash)
                update_bibtex_progress(query_hash)
            },
          error: function(XMLHttpRequest, textStatus, errorThrown) {
              $("span.loading").remove()
              $("div.fileupload span.added").remove()
              $("div.fileupload").append("sorry, there was an error.")
          }
        });
    }

createCollectionInit = function(){

    $("#google-scholar-help-link").click(function(){
        $("#google-scholar-help").modal("show")
        return false
    })
    $("#orcid-help-link").click(function(){
        $("#orcid-help").modal("show")
        return false
    })
    $("#bibtex-fail-help a").click(function(){
        $(".modal").modal("hide")
        if ($(this).hasClass("uservoice")) {
            UserVoice.showPopupWidget()
        }
    })

    $("li textarea, input#name").each(function(){
        $(this).val(exampleStrings[this.id])
    })
    $("div.toggler_contents input, ul#manual-add textarea, input#name")
    .focus(function(){
        currentUserInputValue = $(this).val();
        $(this).removeClass("no-input-yet")

        // hid the example strings if they're still up.
        if (currentUserInputValue == exampleStrings[this.id]) {
            $(this).val("")
        }
    })
    .blur(function(){
        $this = $(this)
        if ($this.val() == "") {
            $this.addClass("no-input-yet")
            $this.val(exampleStrings[this.id])
            return false;
        }
        else if ($this.val() == currentUserInputValue) {
            return false;
        }

        if ($this.hasClass("artifactList")) {
            newIds = parseTextareaArtifacts($(this).val());
            // limit to adding first 250 lines
            newIds = newIds.slice(0, 100)
            addCollectionIds(newIds, $(this))
        }
        else if ($this.attr("id") == "name") {
            // do nothing, it's just an entry field. hack, ick.
        }
        else {
            var idStrParts = $(this).attr("id").split('_');
            var providerName = idStrParts[0];

            if (providerName == "crossref") { // hack, should generalize for all textareas
                var providerTypeQuery = "&type=import"
                var pipeVal = $this.val().replace(":", "|");
                var providerIdQuery = escape(pipeVal);
            } else {
                var providerIdQuery = escape($this.val());
            }
            $(this).siblings("span.added").remove()
            $(this).not("input#name").after(""+ajaxLoadImg+"");
            console.log("running memberitems")
            $.ajax({
              url: "http://"+api_root+"/provider/"+providerName+"/memberitems/"+providerIdQuery+"?method=sync",
              type: "GET",
              dataType: "json",
              success: function(response,status,xhr){
                addCollectionIds(response.memberitems, $this)
                },
              error: function(XMLHttpRequest, textStatus, errorThrown) {
                $("span.loading").remove()
                $this.siblings("span.added").remove()
                var explainString;
                if (XMLHttpRequest.status == 404) {
                    explainString = "sorry, not found. (ORCID imports are failing for new records, we're working to fix)"
                } else {
                    explainString = "sorry, there was an error."
                }
                $this.after("" + explainString + "")
                }
            });
        }
    })

    // creating a collection by submitting the object IDs from the homepage
    $("#id-form").submit(function(){

        // make sure the user input something at all
        if (collectionAliases.length == 0) {
            alert("Looks like you haven't added any research objects to the collection yet.")
            return false;

        // create a collection with these aliases
        } else {
            console.log("adding collection with new items.")

            $("#go-button").replaceWith(""+ajaxLoadImg+"")

            var requestObj = {
                title: $('#name').val(),
                aliases: collectionAliases
            }

            $.ajax({
                url: "http://"+api_root+'/v1/collection?key='+api_key,
                type: "POST",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                data:  JSON.stringify(requestObj),
                success: function(ret){
                    returnedCollection=ret.collection

                    // add the id of the newly-created coll to the user's coll list
                    user.addColl(returnedCollection._id, ret.key)

                    var email = $("#inline-register-email").val()
                    var pw = $("#inline-register-pw").val()
                    if (email && pw){
                        user.setCreds(email, pw)
                    }

                    var success = function(){
                        _gaq.push(['_trackPageview', '/user/created']);
                        location.href = "/collection/" +returnedCollection._id
                    }

                    if (user.hasCreds()){
                        user.syncWithServer("push", {on200: success})
                    }
                    else {
                        success()
                    }
                }
            });
            return false;
        }
    });
}

function homePageInit() {
    $('.carousel').carousel()
    $('.carousel').carousel("cycle")
}

function aboutPageInit() {
    if (location.href.indexOf("#contact") > 0) {
        $("#contact h3").css("color", tiLinkColor)
            .siblings("p").css({backgroundColor: "#ff834c"})
            .animate({backgroundColor: "#ffffff"}, 1000)
    }
}

$(document).ready(function(){
    $.cookie.defaults = {path: "/", raw: 1}

    userViews = new UserViews()
    user = new User(userViews)
    userController = new UserController(user, userViews);
    userController.init()

    collViews = new CollViews()
    coll = new Coll(collViews)
    collController = new CollController(coll, collViews);

    itemController = new ItemController($)

    // report pages
    if (typeof reportIdNamespace == "undefined"){
        // nothing
    }
    else if (reportIdNamespace == "impactstory_collection_id") {
        collController.collReportPageInit()
    }
    else { // must be an item report page
        itemController.itemReportPageInit()
    }

    // table of contents
    if ($("#toc")[0]) {
        $('#toc').tocBuilder({type: 'headings', startLevel: 3, endLevel: 3, insertBackLinks: 0});

    }

    createCollectionInit();
    homePageInit()
    aboutPageInit()

    // let people link straight to the item-help modal
    if(window.location.href.indexOf('#context') != -1) {
        $('#context').modal('show');
    }

    prettyPrint()

});
<commit_msg>silly, but switch the order so it matches label order below<commit_after>$.ajaxSetup ({
    cache: false
});
$.support.cors = true; // makes IE8 and IE9 support CORS
if (!window.console) {
    console = {log: function() {}};
}
var tiLinkColor = "#FF4E00"
var ajaxLoadImg = "";
var ajaxLoadImgTransparent = "";
var collectionAliases = []
var currentUserInputValue = ""
var currentlyUpdating = false

/*****************************************************************************
 * create collection page
 ****************************************************************************/

exampleStrings = {
    paste_input:"10.1038/171737a0\n13054692",
    paste_webpages: "http://www.slideshare.net/cavlec/open-content\nhttps://github.com/egonw/cdk",
    crossref_input: "Watson : A Structure for Deoxyribose Nucleic Acid",
    name: "My Collection"
}

inputExamplesIClickHandler = function(thisInput) {

}

flatten = function(idsArr) {
    // flattens id values that are themselves arrays (like github)
    var aliases = [];
    numIds = idsArr.length;
    for (var i=0; i<numIds; i++ ) {
        var id = idsArr[i][1]
        var namespace = idsArr[i][0]
        if( Object.prototype.toString.call( id ) === '[object Array]' ) {
            var strVal = id.join(",")
        }
        else {
            var strVal = id
        }
        aliases.push([namespace, strVal])
    }
    return(aliases)
}

addCollectionIds = function(idsArr, updateInfoLoc$, itemsAddedMsg$, showZeros) {
    if (itemsAddedMsg$ === undefined) {
        var itemsAddedMsg$ = $("items added")
    }

    var startingCollectionLength = collectionAliases.length
    var newIds = flatten(idsArr);

    // make an object with the unique key values
    var uniqueNamespaceIdPairs = {}
    for  (var i=0; i<collectionAliases.length; i++){
        var namespaceIdPair = collectionAliases[i].join(":")
        uniqueNamespaceIdPairs[namespaceIdPair] = 1
    }
    console.log(uniqueNamespaceIdPairs)

    for (var i=0; i < newIds.length; i++){
        var newNamespaceIdPair = newIds[i].join(":")
        if (!uniqueNamespaceIdPairs[newNamespaceIdPair]) {
            collectionAliases.push(newIds[i])
        }
    }
    $("div.progressbar").empty()
    $("span.loading").remove()

    // how many items are in the new collection now?
    var endingCollectionLength = collectionAliases.length;
    numIdsAdded = endingCollectionLength - startingCollectionLength;
    $("#artcounter span.count").html(endingCollectionLength);
    if (numIdsAdded || showZeros) {
        updateInfoLoc$.siblings("span.added").remove()
        $(""+numIdsAdded+"")
            .append(itemsAddedMsg$)
            .insertAfter(updateInfoLoc$)

        updateInfoLoc$.siblings("span.added")
            .find("span.count")
                .css({color: "#ff4e00"})
                .animate({color: "#555555"}, 1500)

    }
    return true;

}

// puts the textarea-entered ids in a format that addcollectionIds likes
parseTextareaArtifacts = function(str) {
    var ids = str.split("\n");
    var ret = [];
    for (i=0; i<ids.length; i++){
        var artifact = [];
        var thisId = ids[i];
        if (thisId.indexOf(":") != -1) {
            artifact[0] = thisId.split(':')[0]; // namespace
            artifact[1] = thisId.substr(artifact[0].length + 1) // id

            // handle urls:
            if ((artifact[0] == "http") || (artifact[0] == "https")) {
                if (thisId.indexOf("http://dx.doi.org/") != -1) {
                    artifact[0] = "doi";
                    artifact[1] = thisId.replace("http://dx.doi.org/", "");
                } else {
                    artifact[0] = "url";
                    artifact[1] = thisId;
                }
            }
        }
        else {
            if (thisId.length > 0) {
                var isnum_with_possible_period = /^[\d\.]+$/.test(thisId)
                // handle dois entered without the doi prefix
                if (thisId.substring(0,3) == "10.") {
                    artifact[0] = "doi"
                } else if (isnum_with_possible_period && (thisId.length > 5) && (thisId.length <= 8)) {
                    // definition of pmid from http://www.nlm.nih.gov/bsd/mms/medlineelements.html#pmid
                    // this doesn't catch short PMIDs, but that's ok
                    artifact[0] = "pmid"
                }
                else {
                   artifact[0] = "unknown"
                }
                artifact[1] = thisId
            }
        }
        if (typeof artifact[1] != "undefined") {
            ret.push(artifact);
        }
    }
    return ret;
}

progressbar = function(total, done, loc$) {
    percentDone = Math.round(done / total * 100)
    loc$.html(percentDone + "% done...")
}
bibtexUploadDone = function(numItemsTotal, aliases) {
    var numFailedItems = numItemsTotal - aliases.length
    var msg$ = $(ich.bibtexUploadDoneMsg({numFailedItems: numFailedItems}))
    msg$.find("a.help-text")
        .clickover()
    addCollectionIds(aliases, $("div.fileupload"), msg$, true)
}

update_bibtex_progress = function(query_hash) {
    $.ajax({
               url: "http://"+api_root+"/provider/bibtex/memberitems/"+query_hash+"?method=async",
               type: "GET",
               dataType: "json",
               success: function(response,status,xhr){
                   console.log("searched using query_hash " + query_hash)
                   console.log(response)
                   if (response.pages == response.complete) {
                       aliases = []
                       for (i=0; i<response.memberitems.length; i++) {
                           var aliases = aliases.concat(response.memberitems[i])
                       }
                       bibtexUploadDone(response.number_entries, aliases)
                   }
                   else {
                       progressbar(response.pages, response.complete, $("#bibtex_toggler_contents div.progressbar"))
                       setTimeout(function () {
                           update_bibtex_progress(query_hash)
                       }, 500)
                   }
               },
               error: function(XMLHttpRequest, textStatus, errorThrown) {
                   $("span.loading").remove()
                   $("div.fileupload span.added").remove()
                   $("div.fileupload").append("sorry, there was an error.")
               }
           });  }

upload_bibtex = function(files) {
    var fileInput = $("li #input_bibtex")[0];
    var file = fileInput.files[0];
    var formData = new FormData();
    formData.append('file', file);
    $("span.btn-file").remove()
    $(""+ajaxLoadImg+"").insertBefore("div.fileupload div.progressbar");

    console.log("starting ajax request now.")

    progressbar(1, 0, $("#bibtex_toggler_contents div.progressbar"))
    $.ajax({
            url: "http://"+api_root+'/provider/bibtex/memberitems',
            type: "POST",
            processData: false,
            contentType: false,
            timeout: 120*1000,  // 120 seconds, because bibtex is very slow
            data: formData,
            dataType: "json",
            success:  function(response,status,xhr){
                query_hash = response.query_hash
                console.log("started update request; got this query_hash back: " + query_hash)
                update_bibtex_progress(query_hash)
            },
          error: function(XMLHttpRequest, textStatus, errorThrown) {
              $("span.loading").remove()
              $("div.fileupload span.added").remove()
              $("div.fileupload").append("sorry, there was an error.")
          }
        });
    }

createCollectionInit = function(){

    $("#google-scholar-help-link").click(function(){
        $("#google-scholar-help").modal("show")
        return false
    })
    $("#orcid-help-link").click(function(){
        $("#orcid-help").modal("show")
        return false
    })
    $("#bibtex-fail-help a").click(function(){
        $(".modal").modal("hide")
        if ($(this).hasClass("uservoice")) {
            UserVoice.showPopupWidget()
        }
    })

    $("li textarea, input#name").each(function(){
        $(this).val(exampleStrings[this.id])
    })
    $("div.toggler_contents input, ul#manual-add textarea, input#name")
    .focus(function(){
        currentUserInputValue = $(this).val();
        $(this).removeClass("no-input-yet")

        // hid the example strings if they're still up.
        if (currentUserInputValue == exampleStrings[this.id]) {
            $(this).val("")
        }
    })
    .blur(function(){
        $this = $(this)
        if ($this.val() == "") {
            $this.addClass("no-input-yet")
            $this.val(exampleStrings[this.id])
            return false;
        }
        else if ($this.val() == currentUserInputValue) {
            return false;
        }

        if ($this.hasClass("artifactList")) {
            newIds = parseTextareaArtifacts($(this).val());
            // limit to adding first 250 lines
            newIds = newIds.slice(0, 100)
            addCollectionIds(newIds, $(this))
        }
        else if ($this.attr("id") == "name") {
            // do nothing, it's just an entry field. hack, ick.
        }
        else {
            var idStrParts = $(this).attr("id").split('_');
            var providerName = idStrParts[0];

            if (providerName == "crossref") { // hack, should generalize for all textareas
                var providerTypeQuery = "&type=import"
                var pipeVal = $this.val().replace(":", "|");
                var providerIdQuery = escape(pipeVal);
            } else {
                var providerIdQuery = escape($this.val());
            }
            $(this).siblings("span.added").remove()
            $(this).not("input#name").after(""+ajaxLoadImg+"");
            console.log("running memberitems")
            $.ajax({
              url: "http://"+api_root+"/provider/"+providerName+"/memberitems/"+providerIdQuery+"?method=sync",
              type: "GET",
              dataType: "json",
              success: function(response,status,xhr){
                addCollectionIds(response.memberitems, $this)
                },
              error: function(XMLHttpRequest, textStatus, errorThrown) {
                $("span.loading").remove()
                $this.siblings("span.added").remove()
                var explainString;
                if (XMLHttpRequest.status == 404) {
                    explainString = "sorry, not found. (ORCID imports are failing for new records, we're working to fix)"
                } else {
                    explainString = "sorry, there was an error."
                }
                $this.after("" + explainString + "")
                }
            });
        }
    })

    // creating a collection by submitting the object IDs from the homepage
    $("#id-form").submit(function(){

        // make sure the user input something at all
        if (collectionAliases.length == 0) {
            alert("Looks like you haven't added any research objects to the collection yet.")
            return false;

        // create a collection with these aliases
        } else {
            console.log("adding collection with new items.")

            $("#go-button").replaceWith(""+ajaxLoadImg+"")

            var requestObj = {
                title: $('#name').val(),
                aliases: collectionAliases
            }

            $.ajax({
                url: "http://"+api_root+'/v1/collection?key='+api_key,
                type: "POST",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                data:  JSON.stringify(requestObj),
                success: function(ret){
                    returnedCollection=ret.collection

                    // add the id of the newly-created coll to the user's coll list
                    user.addColl(returnedCollection._id, ret.key)

                    var email = $("#inline-register-email").val()
                    var pw = $("#inline-register-pw").val()
                    if (email && pw){
                        user.setCreds(email, pw)
                    }

                    var success = function(){
                        _gaq.push(['_trackPageview', '/user/created']);
                        location.href = "/collection/" +returnedCollection._id
                    }

                    if (user.hasCreds()){
                        user.syncWithServer("push", {on200: success})
                    }
                    else {
                        success()
                    }
                }
            });
            return false;
        }
    });
}

function homePageInit() {
    $('.carousel').carousel()
    $('.carousel').carousel("cycle")
}

function aboutPageInit() {
    if (location.href.indexOf("#contact") > 0) {
        $("#contact h3").css("color", tiLinkColor)
            .siblings("p").css({backgroundColor: "#ff834c"})
            .animate({backgroundColor: "#ffffff"}, 1000)
    }
}

$(document).ready(function(){
    $.cookie.defaults = {path: "/", raw: 1}

    userViews = new UserViews()
    user = new User(userViews)
    userController = new UserController(user, userViews);
    userController.init()

    collViews = new CollViews()
    coll = new Coll(collViews)
    collController = new CollController(coll, collViews);

    itemController = new ItemController($)

    // report pages
    if (typeof reportIdNamespace == "undefined"){
        // nothing
    }
    else if (reportIdNamespace == "impactstory_collection_id") {
        collController.collReportPageInit()
    }
    else { // must be an item report page
        itemController.itemReportPageInit()
    }

    // table of contents
    if ($("#toc")[0]) {
        $('#toc').tocBuilder({type: 'headings', startLevel: 3, endLevel: 3, insertBackLinks: 0});

    }

    createCollectionInit();
    homePageInit()
    aboutPageInit()

    // let people link straight to the item-help modal
    if(window.location.href.indexOf('#context') != -1) {
        $('#context').modal('show');
    }

    prettyPrint()

});
<|endoftext|>


<commit_before>// NOTE: The usage of user objects instead of userids is to deal with situations
//       such as where users change names and/or attempt to register multiple times.
//       This causes some operations to be O(N) instead of O(log N) or O(N*N) instead
//       of O(N*log N), until node.js implements the Map and Set objects.

var RoundRobin = (function () {
	function RoundRobin(isDoubles) {
		this.isDoubles = !!isDoubles;
		this.isBracketFrozen = false;
		this.users = [];
		this.isUsersBusy = null;
		this.matches = null;
		this.userScores = null;
		this.pendingMatches = 0;
	};

	RoundRobin.prototype.addUser = function (user) {
		if (this.isBracketFrozen)
			return 'BracketFrozen';

		if (this.users.indexOf(user) >= 0)
			return 'UserAlreadyAdded';

		this.users.push(user);
	};
	RoundRobin.prototype.removeUser = function (user) {
		if (this.isBracketFrozen)
			return 'BracketFrozen';

		var userIndex = this.users.indexOf(user);
		if (userIndex < 0)
			return 'UserNotAdded';

		this.users.splice(userIndex, 1);
	};
	RoundRobin.prototype.replaceUser = function (user, replacementUser) {
		var userIndex = this.users.indexOf(user);
		if (userIndex < 0)
			return 'UserNotAdded';

		if (this.users.indexOf(replacementUser) >= 0)
			return 'UserAlreadyAdded';

		this.users[userIndex] = replacementUser;
	};
	RoundRobin.prototype.getUsers = function () {
		return this.users.slice(0);
	};

	RoundRobin.prototype.getBracketData = function () {
		var data = {};
		data.type = 'table';
		data.tableHeaders = {
			cols: this.users.slice(0),
			rows: this.users.slice(0)
		};
		data.tableContents = this.users.map(function (userA, row) {
			return this.users.map(function (userB, col) {
				if (!this.isDoubles && col >= row)
					return null;
				if (userA === userB)
					return null;

				var cell = {};
				cell.teams = [userA, userB];
				if (!this.isBracketFrozen)
					cell.state = 'unavailable';
				else {
					var match = this.matches[row][col];
					cell.state = match.state;
					if (match.state === 'finished') {
						cell.result = match.result;
						cell.score = match.score.slice(0);
					}
				}
				return cell;
			}, this);
		}, this);
		data.scores = this.users.map(function (user, u) {
			var score = {};
			score.team = user;
			if (!this.isBracketFrozen)
				score.score = 0;
			else
				score.score = this.userScores[u];
			return score;
		}, this);
		return data;
	};
	RoundRobin.prototype.freezeBracket = function () {
		this.isBracketFrozen = true;
		this.isUsersBusy = this.users.map(function () { return false; });
		this.matches = this.users.map(function (userA, row) {
			return this.users.map(function (userB, col) {
				if (!this.isDoubles && col >= row)
					return null;
				if (userA === userB)
					return null;
				++this.pendingMatches;
				return {state: 'available'};
			}, this);
		}, this);
		this.userScores = this.users.map(function () { return 0; });
	};

	RoundRobin.prototype.disqualifyUser = function (user) {
		if (!this.isBracketFrozen)
			return 'BracketNotFrozen';

		var userIndex = this.users.indexOf(user);
		if (userIndex < 0)
			return 'UserNotAdded';

		this.matches[userIndex].forEach(function (match, col) {
			if (!match || match.state !== 'available')
				return;
			match.state = 'finished';
			match.result = 'loss';
			match.score = [0, 1];
			++this.userScores[col];
			--this.pendingMatches;
		}, this);

		this.matches.forEach(function (challenges, row) {
			var match = challenges[userIndex];
			if (!match || match.state !== 'available')
				return;
			match.state = 'finished';
			match.result = 'win';
			match.score = [1, 0];
			++this.userScores[row];
			--this.pendingMatches;
		}, this);
	};
	RoundRobin.prototype.getUserBusy = function (user) {
		if (!this.isBracketFrozen)
			return 'BracketNotFrozen';

		var userIndex = this.users.indexOf(user);
		if (userIndex < 0)
			return 'UserNotAdded';
		return this.isUsersBusy[userIndex];
	};
	RoundRobin.prototype.setUserBusy = function (user, isBusy) {
		if (!this.isBracketFrozen)
			return 'BracketNotFrozen';

		var userIndex = this.users.indexOf(user);
		if (userIndex < 0)
			return 'UserNotAdded';
		this.isUsersBusy[userIndex] = isBusy;
	};

	RoundRobin.prototype.getAvailableMatches = function () {
		if (!this.isBracketFrozen)
			return 'BracketNotFrozen';

		var matches = [];
		this.matches.forEach(function (challenges, row) {
			challenges.forEach(function (match, col) {
				if (!match)
					return;
				if (match.state === 'available' &&
					!this.isUsersBusy[row] && !this.isUsersBusy[col])
					matches.push([this.users[row], this.users[col]]);
			}, this);
		}, this);
		return matches;
	};
	RoundRobin.prototype.setMatchResult = function (match, result, score) {
		if (!this.isBracketFrozen)
			return 'BracketNotFrozen';

		if (!(result in {win:1, loss:1, draw:1}))
			return 'InvalidMatchResult';

		var userIndexA = this.users.indexOf(match[0]);
		var userIndexB = this.users.indexOf(match[1]);
		if (userIndexA < 0 || userIndexB < 0)
			return 'UserNotAdded';

		var match = this.matches[userIndexA][userIndexB];
		if (!match || match.state !== 'available')
			return 'InvalidMatch';

		var virtualScore;
		if (result === 'win')
			virtualScore = [1, 0];
		else if (result === 'loss')
			virtualScore = [0, 1];
		else
			virtualScore = [0.5, 0.5];
		if (!score)
			score = virtualScore;

		match.state = 'finished';
		match.result = result;
		match.score = score.slice(0);
		this.userScores[userIndexA] += virtualScore[0];
		this.userScores[userIndexB] += virtualScore[1];
		--this.pendingMatches;

		if (this.pendingMatches === 0)
			return true;
	};

	RoundRobin.prototype.getResults = function () {
		if (!this.isBracketFrozen || this.pendingMatches !== 0)
			return 'TournamentNotEnded';

		var sortedScores = this.userScores.map(function (score, userIndex) {
			return {userIndex: userIndex, score: score};
		}).sort(function (a, b) { return b.score - a.score; });

		var results = [];
		var currentScore = sortedScores[0].score;
		var currentRank = [];
		results.push(currentRank);
		sortedScores.forEach(function (score) {
			if (score.score < currentScore) {
				currentScore = score.score;
				currentRank = [];
				results.push(currentRank);
			}
			currentRank.push(this.users[score.userIndex]);
		}, this);
		return results;
	};

	return RoundRobin;
})();

exports.RoundRobin = RoundRobin;
<commit_msg>Add name property to generators<commit_after>// NOTE: The usage of user objects instead of userids is to deal with situations
//       such as where users change names and/or attempt to register multiple times.
//       This causes some operations to be O(N) instead of O(log N) or O(N*N) instead
//       of O(N*log N), until node.js implements the Map and Set objects.

var RoundRobin = (function () {
	function RoundRobin(isDoubles) {
		this.isDoubles = !!isDoubles;
		this.isBracketFrozen = false;
		this.users = [];
		this.isUsersBusy = null;
		this.matches = null;
		this.userScores = null;
		this.pendingMatches = 0;
	};

	RoundRobin.prototype.name = "Round Robin";

	RoundRobin.prototype.addUser = function (user) {
		if (this.isBracketFrozen)
			return 'BracketFrozen';

		if (this.users.indexOf(user) >= 0)
			return 'UserAlreadyAdded';

		this.users.push(user);
	};
	RoundRobin.prototype.removeUser = function (user) {
		if (this.isBracketFrozen)
			return 'BracketFrozen';

		var userIndex = this.users.indexOf(user);
		if (userIndex < 0)
			return 'UserNotAdded';

		this.users.splice(userIndex, 1);
	};
	RoundRobin.prototype.replaceUser = function (user, replacementUser) {
		var userIndex = this.users.indexOf(user);
		if (userIndex < 0)
			return 'UserNotAdded';

		if (this.users.indexOf(replacementUser) >= 0)
			return 'UserAlreadyAdded';

		this.users[userIndex] = replacementUser;
	};
	RoundRobin.prototype.getUsers = function () {
		return this.users.slice(0);
	};

	RoundRobin.prototype.getBracketData = function () {
		var data = {};
		data.type = 'table';
		data.tableHeaders = {
			cols: this.users.slice(0),
			rows: this.users.slice(0)
		};
		data.tableContents = this.users.map(function (userA, row) {
			return this.users.map(function (userB, col) {
				if (!this.isDoubles && col >= row)
					return null;
				if (userA === userB)
					return null;

				var cell = {};
				cell.teams = [userA, userB];
				if (!this.isBracketFrozen)
					cell.state = 'unavailable';
				else {
					var match = this.matches[row][col];
					cell.state = match.state;
					if (match.state === 'finished') {
						cell.result = match.result;
						cell.score = match.score.slice(0);
					}
				}
				return cell;
			}, this);
		}, this);
		data.scores = this.users.map(function (user, u) {
			var score = {};
			score.team = user;
			if (!this.isBracketFrozen)
				score.score = 0;
			else
				score.score = this.userScores[u];
			return score;
		}, this);
		return data;
	};
	RoundRobin.prototype.freezeBracket = function () {
		this.isBracketFrozen = true;
		this.isUsersBusy = this.users.map(function () { return false; });
		this.matches = this.users.map(function (userA, row) {
			return this.users.map(function (userB, col) {
				if (!this.isDoubles && col >= row)
					return null;
				if (userA === userB)
					return null;
				++this.pendingMatches;
				return {state: 'available'};
			}, this);
		}, this);
		this.userScores = this.users.map(function () { return 0; });
	};

	RoundRobin.prototype.disqualifyUser = function (user) {
		if (!this.isBracketFrozen)
			return 'BracketNotFrozen';

		var userIndex = this.users.indexOf(user);
		if (userIndex < 0)
			return 'UserNotAdded';

		this.matches[userIndex].forEach(function (match, col) {
			if (!match || match.state !== 'available')
				return;
			match.state = 'finished';
			match.result = 'loss';
			match.score = [0, 1];
			++this.userScores[col];
			--this.pendingMatches;
		}, this);

		this.matches.forEach(function (challenges, row) {
			var match = challenges[userIndex];
			if (!match || match.state !== 'available')
				return;
			match.state = 'finished';
			match.result = 'win';
			match.score = [1, 0];
			++this.userScores[row];
			--this.pendingMatches;
		}, this);
	};
	RoundRobin.prototype.getUserBusy = function (user) {
		if (!this.isBracketFrozen)
			return 'BracketNotFrozen';

		var userIndex = this.users.indexOf(user);
		if (userIndex < 0)
			return 'UserNotAdded';
		return this.isUsersBusy[userIndex];
	};
	RoundRobin.prototype.setUserBusy = function (user, isBusy) {
		if (!this.isBracketFrozen)
			return 'BracketNotFrozen';

		var userIndex = this.users.indexOf(user);
		if (userIndex < 0)
			return 'UserNotAdded';
		this.isUsersBusy[userIndex] = isBusy;
	};

	RoundRobin.prototype.getAvailableMatches = function () {
		if (!this.isBracketFrozen)
			return 'BracketNotFrozen';

		var matches = [];
		this.matches.forEach(function (challenges, row) {
			challenges.forEach(function (match, col) {
				if (!match)
					return;
				if (match.state === 'available' &&
					!this.isUsersBusy[row] && !this.isUsersBusy[col])
					matches.push([this.users[row], this.users[col]]);
			}, this);
		}, this);
		return matches;
	};
	RoundRobin.prototype.setMatchResult = function (match, result, score) {
		if (!this.isBracketFrozen)
			return 'BracketNotFrozen';

		if (!(result in {win:1, loss:1, draw:1}))
			return 'InvalidMatchResult';

		var userIndexA = this.users.indexOf(match[0]);
		var userIndexB = this.users.indexOf(match[1]);
		if (userIndexA < 0 || userIndexB < 0)
			return 'UserNotAdded';

		var match = this.matches[userIndexA][userIndexB];
		if (!match || match.state !== 'available')
			return 'InvalidMatch';

		var virtualScore;
		if (result === 'win')
			virtualScore = [1, 0];
		else if (result === 'loss')
			virtualScore = [0, 1];
		else
			virtualScore = [0.5, 0.5];
		if (!score)
			score = virtualScore;

		match.state = 'finished';
		match.result = result;
		match.score = score.slice(0);
		this.userScores[userIndexA] += virtualScore[0];
		this.userScores[userIndexB] += virtualScore[1];
		--this.pendingMatches;

		if (this.pendingMatches === 0)
			return true;
	};

	RoundRobin.prototype.getResults = function () {
		if (!this.isBracketFrozen || this.pendingMatches !== 0)
			return 'TournamentNotEnded';

		var sortedScores = this.userScores.map(function (score, userIndex) {
			return {userIndex: userIndex, score: score};
		}).sort(function (a, b) { return b.score - a.score; });

		var results = [];
		var currentScore = sortedScores[0].score;
		var currentRank = [];
		results.push(currentRank);
		sortedScores.forEach(function (score) {
			if (score.score < currentScore) {
				currentScore = score.score;
				currentRank = [];
				results.push(currentRank);
			}
			currentRank.push(this.users[score.userIndex]);
		}, this);
		return results;
	};

	return RoundRobin;
})();

exports.RoundRobin = RoundRobin;
<|endoftext|>


<commit_before>'use strict';

define({
  get: function get(url) {
    return new Promise(function (resolve, reject) {
      var req = new XMLHttpRequest();
      req.open('GET', url);

      req.onload = function onload() {
        if (req.status === 200) {
          resolve(req.response);
        } else {
          reject(Error(req.statusText));
        }
      };

      req.onerror = function onerror() {
        reject(Error('Network Error'));
      };

      req.send();
    });
  },

  getJSON: function getJSON(url) {
    return require('helper').get(url).then(JSON.parse);
  },

  sortByKey: function sortByKey(key, d) {
    return d.slice().sort(function (a, b) {
      return a[key] - b[key];
    }).reverse();
  },

  limit: function limit(key, m, d) {
    return d.filter(function (n) {
      return n[key].isAfter(m);
    });
  },

  sum: function sum(a) {
    return a.reduce(function (b, c) {
      return b + c;
    }, 0);
  },

  one: function one() {
    return 1;
  },

  trueDefault: function trueDefault(d) {
    return d === undefined ? true : d;
  },

  dictGet: function dictGet(dict, key) {
    var k = key.shift();

    if (!(k in dict)) {
      return null;
    }

    if (key.length === 0) {
      return dict[k];
    }

    return this.dictGet(dict[k], key);
  },

  localStorageTest: function localStorageTest() {
    var test = 'test';
    try {
      localStorage.setItem(test, test);
      localStorage.removeItem(test);
      return true;
    } catch (e) {
      return false;
    }
  },

  listReplace: function listReplace(s, subst) {
    for (var key in subst) {
      var re = new RegExp(key, 'g');
      s = s.replace(re, subst[key]);
    }
    return s;
  },

  /* Helpers working with nodes */

  offline: function offline(d) {
    return !d.flags.online;
  },

  online: function online(d) {
    return d.flags.online;
  },

  hasLocation: function hasLocation(d) {
    return 'location' in d.nodeinfo &&
      Math.abs(d.nodeinfo.location.latitude) < 90 &&
      Math.abs(d.nodeinfo.location.longitude) < 180;
  },

  subtract: function subtract(a, b) {
    var ids = {};

    b.forEach(function (d) {
      ids[d.nodeinfo.node_id] = true;
    });

    return a.filter(function (d) {
      return !(d.nodeinfo.node_id in ids);
    });
  },

  /* Helpers working with links */

  showDistance: function showDistance(d) {
    if (isNaN(d.distance)) {
      return '';
    }

    return d.distance.toFixed(0) + ' m';
  },

  showTq: function showTq(d) {
    return (1 / d.tq * 100).toFixed(0) + '%';
  },

  attributeEntry: function attributeEntry(el, label, value) {
    if (value === null || value === undefined) {
      return '';
    }

    var tr = document.createElement('tr');
    var th = document.createElement('th');
    th.textContent = _.t(label);
    tr.appendChild(th);

    var td = document.createElement('td');

    if (typeof value === 'function') {
      value(td);
    } else {
      td.appendChild(document.createTextNode(value));
    }

    tr.appendChild(td);

    el.appendChild(tr);

    return td;
  },

  createIframe: function createIframe(opt, width, height) {
    var el = document.createElement('iframe');
    width = typeof width !== 'undefined' ? width : '525px';
    height = typeof height !== 'undefined' ? height : '350px';

    if (opt.src) {
      el.src = opt.src;
    } else {
      el.src = opt;
    }

    if (opt.frameBorder) {
      el.frameBorder = opt.frameBorder;
    } else {
      el.frameBorder = 1;
    }

    if (opt.width) {
      el.width = opt.width;
    } else {
      el.width = width;
    }

    if (opt.height) {
      el.height = opt.height;
    } else {
      el.height = height;
    }

    el.scrolling = 'no';
    el.seamless = 'seamless';

    return el;
  },

  showStat: function showStat(o, subst) {
    var content;
    var caption;
    subst = typeof subst !== 'undefined' ? subst : {};

    if (o.thumbnail) {
      content = document.createElement('img');
      content.src = require('helper').listReplace(o.thumbnail, subst);
    }

    if (o.caption) {
      caption = require('helper').listReplace(o.caption, subst);

      if (!content) {
        content = document.createTextNode(caption);
      }
    }

    if (o.iframe) {
      content = require('helper').createIframe(o.iframe, o.width, o.height);
      if (o.iframe.src) {
        content.src = require('helper').listReplace(o.iframe.src, subst);
      } else {
        content.src = require('helper').listReplace(o.iframe, subst);
      }
    }

    var p = document.createElement('p');

    if (o.href) {
      var link = document.createElement('a');
      link.target = '_blank';
      link.href = require('helper').listReplace(o.href, subst);
      link.appendChild(content);

      if (caption && o.thumbnail) {
        link.title = caption;
      }

      p.appendChild(link);
    } else {
      p.appendChild(content);
    }

    return p;
  },
  getTileBBox: function getTileBBox(s, map, tileSize, margin) {
    var tl = map.unproject([s.x - margin, s.y - margin]);
    var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]);

    return [br.lat, tl.lng, tl.lat, br.lng];
  }
});
<commit_msg>[TASK] Remove iframe for stats<commit_after>'use strict';

define({
  get: function get(url) {
    return new Promise(function (resolve, reject) {
      var req = new XMLHttpRequest();
      req.open('GET', url);

      req.onload = function onload() {
        if (req.status === 200) {
          resolve(req.response);
        } else {
          reject(Error(req.statusText));
        }
      };

      req.onerror = function onerror() {
        reject(Error('Network Error'));
      };

      req.send();
    });
  },

  getJSON: function getJSON(url) {
    return require('helper').get(url).then(JSON.parse);
  },

  sortByKey: function sortByKey(key, d) {
    return d.slice().sort(function (a, b) {
      return a[key] - b[key];
    }).reverse();
  },

  limit: function limit(key, m, d) {
    return d.filter(function (n) {
      return n[key].isAfter(m);
    });
  },

  sum: function sum(a) {
    return a.reduce(function (b, c) {
      return b + c;
    }, 0);
  },

  one: function one() {
    return 1;
  },

  trueDefault: function trueDefault(d) {
    return d === undefined ? true : d;
  },

  dictGet: function dictGet(dict, key) {
    var k = key.shift();

    if (!(k in dict)) {
      return null;
    }

    if (key.length === 0) {
      return dict[k];
    }

    return this.dictGet(dict[k], key);
  },

  localStorageTest: function localStorageTest() {
    var test = 'test';
    try {
      localStorage.setItem(test, test);
      localStorage.removeItem(test);
      return true;
    } catch (e) {
      return false;
    }
  },

  listReplace: function listReplace(s, subst) {
    for (var key in subst) {
      var re = new RegExp(key, 'g');
      s = s.replace(re, subst[key]);
    }
    return s;
  },

  /* Helpers working with nodes */

  offline: function offline(d) {
    return !d.flags.online;
  },

  online: function online(d) {
    return d.flags.online;
  },

  hasLocation: function hasLocation(d) {
    return 'location' in d.nodeinfo &&
      Math.abs(d.nodeinfo.location.latitude) < 90 &&
      Math.abs(d.nodeinfo.location.longitude) < 180;
  },

  subtract: function subtract(a, b) {
    var ids = {};

    b.forEach(function (d) {
      ids[d.nodeinfo.node_id] = true;
    });

    return a.filter(function (d) {
      return !(d.nodeinfo.node_id in ids);
    });
  },

  /* Helpers working with links */

  showDistance: function showDistance(d) {
    if (isNaN(d.distance)) {
      return '';
    }

    return d.distance.toFixed(0) + ' m';
  },

  showTq: function showTq(d) {
    return (1 / d.tq * 100).toFixed(0) + '%';
  },

  attributeEntry: function attributeEntry(el, label, value) {
    if (value === null || value === undefined) {
      return '';
    }

    var tr = document.createElement('tr');
    var th = document.createElement('th');
    th.textContent = _.t(label);
    tr.appendChild(th);

    var td = document.createElement('td');

    if (typeof value === 'function') {
      value(td);
    } else {
      td.appendChild(document.createTextNode(value));
    }

    tr.appendChild(td);

    el.appendChild(tr);

    return td;
  },

  showStat: function showStat(o, subst) {
    var content;
    var caption;
    subst = typeof subst !== 'undefined' ? subst : {};

    if (o.thumbnail) {
      content = document.createElement('img');
      content.src = require('helper').listReplace(o.thumbnail, subst);
    }

    if (o.caption) {
      caption = require('helper').listReplace(o.caption, subst);

      if (!content) {
        content = document.createTextNode(caption);
      }
    }

    var p = document.createElement('p');

    if (o.href) {
      var link = document.createElement('a');
      link.target = '_blank';
      link.href = require('helper').listReplace(o.href, subst);
      link.appendChild(content);

      if (caption && o.thumbnail) {
        link.title = caption;
      }

      p.appendChild(link);
    } else {
      p.appendChild(content);
    }

    return p;
  },

  getTileBBox: function getTileBBox(s, map, tileSize, margin) {
    var tl = map.unproject([s.x - margin, s.y - margin]);
    var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]);

    return [br.lat, tl.lng, tl.lat, br.lng];
  }
});
<|endoftext|>


<commit_before>
module.exports = prompt

var log = require("./log")
  , buffer = ""
  , stdio = process.binding("stdio")

function prompt (p, def, silent, cb) {
  if (!cb) cb = silent, silent = false
  if (!cb) cb = def, def = undefined
  if (def) p += "("+(silent ? "<hidden>" : def)+") "
  process.stdout.write(p)
  process.stdout.flush()

  return (silent) ? silentRead(def, cb) : read(def, cb)
}

function read (def, cb) {
  var stdin = process.openStdin()
    , val = ""
  stdin.resume()
  stdin.setEncoding("utf8")
  stdin.on("error", cb)
  stdin.on("data", function D (chunk) {
    val += buffer + chunk
    buffer = ""
    if (val.indexOf("\n") !== -1) {
      if (val !== "\n") val = val.replace(/^\n+/, "")
      buffer = val.substr(val.indexOf("\n"))
      val = val.substr(0, val.indexOf("\n"))
      stdin.pause()
      stdin.removeListener("data", D)
      val = val.trim() || def
      cb(null, val)
    }
  })
}

function silentRead (def, cb) {
  var stdin = process.openStdin()
    , val = ""
  stdio.setRawMode(true)
  stdin.resume()
  stdin.on("data", function D (c) {
    c = "" + c
    switch (c) {
      case "\n": case "\r": case "\r\n": case "\u0004":
        stdio.setRawMode(false)
        stdin.removeListener("data", D)
        val = val.trim() || def
        process.stdout.write("\n")
        process.stdout.flush()
        stdin.pause()
        return cb(null, val)
      case "\u0003": case "\0":
        process.exit()
        break
      default:
        val += buffer + c
        buffer = ""
        break
    }
  })
}
<commit_msg>dont' exit forcefully<commit_after>
module.exports = prompt

var log = require("./log")
  , buffer = ""
  , stdio = process.binding("stdio")

function prompt (p, def, silent, cb) {
  if (!cb) cb = silent, silent = false
  if (!cb) cb = def, def = undefined
  if (def) p += "("+(silent ? "<hidden>" : def)+") "
  process.stdout.write(p)
  process.stdout.flush()

  return (silent) ? silentRead(def, cb) : read(def, cb)
}

function read (def, cb) {
  var stdin = process.openStdin()
    , val = ""
  stdin.resume()
  stdin.setEncoding("utf8")
  stdin.on("error", cb)
  stdin.on("data", function D (chunk) {
    val += buffer + chunk
    buffer = ""
    if (val.indexOf("\n") !== -1) {
      if (val !== "\n") val = val.replace(/^\n+/, "")
      buffer = val.substr(val.indexOf("\n"))
      val = val.substr(0, val.indexOf("\n"))
      stdin.pause()
      stdin.removeListener("data", D)
      val = val.trim() || def
      cb(null, val)
    }
  })
}

function silentRead (def, cb) {
  var stdin = process.openStdin()
    , val = ""
  stdio.setRawMode(true)
  stdin.resume()
  stdin.on("data", function D (c) {
    c = "" + c
    switch (c) {
      case "\n": case "\r": case "\r\n": case "\u0004":
        stdio.setRawMode(false)
        stdin.removeListener("data", D)
        val = val.trim() || def
        process.stdout.write("\n")
        process.stdout.flush()
        stdin.pause()
        return cb(null, val)
      case "\u0003": case "\0":
        return cb("cancelled")
        break
      default:
        val += buffer + c
        buffer = ""
        break
    }
  })
}
<|endoftext|>


<commit_before>var formats = require('./formats').formats;
var common = require('./common'),
	getType = common.getType,
	prettyType = common.prettyType,
	isOfType = common.isOfType,
	getName = common.getName,
	deepEquals = common.deepEquals;

function throwInvalidValue(names, value, expected) {
	throw new Error('JSON object' + getName(names) + ' is ' + value + ' when it should be ' + expected);
}

function throwInvalidAttributeValue(names, attribFullName, value, expected) {
	throw new Error('JSON object' + getName(names) + ': ' + attribFullName + ' is ' + value + ' when it should be ' + expected);
}

function throwInvalidType(names, value, expected) {
	throw new Error('JSON object' + getName(names) + ' is ' + prettyType(getType(value)) + ' when it should be ' + expected);
}

function throwInvalidDisallow(names, value, expected) {
	throw new Error('JSON object' + getName(names) + ' is ' + prettyType(getType(value)) + ' when it should not be ' + expected);
}

function validateRequired(obj, schema, names) {
	//console.log('***', names, 'validateRequired');
	if (schema.required) {
		if (obj === undefined) {
			throw new Error('JSON object' + getName(names) + ' is required');
		}
	}
}

function applyDefault(obj, schema, names) {
	//console.log('***', names, 'applyDefault');
	if (schema.default !== undefined) {
		obj = schema.default;
	}

	return obj;
}

function validateType(obj, schema, names) {
	//console.log('***', names, 'validateType');
	if (schema.type !== undefined) {
		switch (getType(schema.type)) {
			case 'string':
				// simple type
				if (!isOfType(obj, schema.type)) {
					throwInvalidType(names, obj, prettyType(schema.type));
				}
				break;
			case 'array':
				// union type
				for (var i = 0; i < schema.type.length; ++i) {
					switch (getType(schema.type[i])) {
						case 'string':
							// simple type (inside union type)
							if (isOfType(obj, schema.type[i])) {
								return; // success
							}
							break;
						case 'object':
							// schema (inside union type)
							try {
								return validateSchema(obj, schema.type[i], names);
							} catch(err) {
								// validation failed
								// TOOD: consider propagating error message upwards
							}
							break;
					}
				}
				throwInvalidType(names, obj, 'either ' + schema.type.map(prettyType).join(' or '));
				break;
		}
	}
}

function validateDisallow(obj, schema, names) {
	//console.log('***', names, 'validateDisallow');
	if (schema.disallow !== undefined) {
		switch (getType(schema.disallow)) {
			case 'string':
				// simple type
				if (isOfType(obj, schema.disallow)) {
					throwInvalidDisallow(names, obj, prettyType(schema.disallow));
				}
				break;
			case 'array':
				// union type
				for (var i = 0; i < schema.disallow.length; ++i) {
					switch (getType(schema.disallow[i])) {
						case 'string':
							// simple type (inside union type)
							if (isOfType(obj, schema.disallow[i])) {
								throwInvalidType(names, obj, 'neither ' + schema.disallow.map(prettyType).join(' nor '));
							}
							break;
						case 'object':
							// schema (inside union type)
							try {
								validateSchema(obj, schema.disallow[i], names);
							} catch(err) {
								// validation failed
								continue;
							}
							throwInvalidType(names, obj, 'neither ' + schema.disallow.map(prettyType).join(' nor '));
							// TOOD: consider propagating error message upwards
							break;
					}
				}
				break;
		}
	}
}

function validateEnum(obj, schema, names) {
	//console.log('***', names, 'validateEnum');
	if (schema['enum'] !== undefined) {
		for (var i = 0; i < schema['enum'].length; ++i) {
			if (deepEquals(obj, schema['enum'][i])) {
				return;
			}
		}
		throw new Error('JSON object' + getName(names) + ' is not in enum');
	}
}

function validateArray(obj, schema, names) {
	//console.log('***', names, 'validateArray');
	var i, j;

	if (schema.minItems !== undefined) {
		if (obj.length < schema.minItems) {
			throwInvalidAttributeValue(names, 'number of items', obj.length, 'at least ' + schema.minItems);
		}
	}

	if (schema.maxItems !== undefined) {
		if (obj.length > schema.maxItems) {
			throwInvalidAttributeValue(names, 'number of items', obj.length, 'at most ' + schema.maxItems);
		}
	}

	if (schema.items !== undefined) {
		switch (getType(schema.items)) {
			case 'object':
				// all the items in the array MUST be valid according to the schema
				for (i = 0; i < obj.length; ++i) {
					obj[i] = validateSchema(obj[i], schema.items, names.concat([ '['+i+']' ]));
				}
				break;
			case 'array':
				// each position in the instance array MUST conform to the schema in the corresponding position for this array
				var numChecks = Math.min(obj.length, schema.items.length);
				for (i = 0; i < numChecks; ++i) {
					obj[i] = validateSchema(obj[i], schema.items[i], names.concat([ '['+i+']' ]));
				}
				if (obj.length > schema.items.length) {
					if (schema.additionalItems !== undefined) {
						if (schema.additionalItems === false) {
							throwInvalidAttributeValue(names, 'number of items', obj.length, 'at most ' + schema.items.length + ' - the length of schema items');
						}
						for (; i < obj.length; ++i) {
							obj[i] = validateSchema(obj[i], schema.additionalItems, names.concat([ '['+i+']' ]));
						}
					}
				}
				break;
		}
	}

	if (schema.uniqueItems !== undefined) {
		for (i = 0; i < obj.length - 1; ++i) {
			for (j = i + 1; j < obj.length; ++j) {
				if (deepEquals(obj[i], obj[j])) {
					throw new Error('JSON object' + getName(names) + ' items are not unique: element ' + i + ' equals element ' + j);
				}
			}
		}
	}
}

function validateObject(obj, schema, names) {
	//console.log('***', names, 'validateObject');
	var prop, property;
	if (schema.properties !== undefined) {
		for (property in schema.properties) {
			prop = validateSchema(obj[property], schema.properties[property], names.concat([property]));
			if (prop === undefined) {
				delete obj[property];
			} else {
				obj[property] = prop;
			}
		}
	}

	var matchingProperties = {};
	if (schema.patternProperties !== undefined) {
		for (var reStr in schema.patternProperties) {
			var re = RegExp(reStr);
			for (property in obj) {
				if (property.match(re)) {
					matchingProperties[property] = true;
					prop = validateSchema(obj[property], schema.patternProperties[reStr], names.concat(['patternProperties./' + property + '/']));
					if (prop === undefined) {
						delete obj[property];
					} else {
						obj[property] = prop;
					}
				}
			}
		}
	}

	if (schema.additionalProperties !== undefined) {
		for (property in obj) {
			if (schema.properties !== undefined && property in schema.properties) {
				continue;
			}
			if (property in matchingProperties) {
				continue;
			}
			// additional
			if (schema.additionalProperties === false) {
				throw new Error('JSON object' + getName(names.concat([property])) + ' is not explicitly defined and therefore not allowed');
			}
			obj[property] = validateSchema(obj[property], schema.additionalProperties, names.concat([property]));
		}
	}

	if (schema.dependencies !== undefined) {
		for (property in schema.dependencies) {
			switch (getType(schema.dependencies[property])) {
				case 'string':
					// simple dependency
					if (property in obj && !(schema.dependencies[property] in obj)) {
						throw new Error('JSON object' + getName(names.concat([schema.dependencies[property]])) + ' is required by property \'' + property + '\'');
					}
					break;
				case 'array':
					// simple dependency tuple
					for (var i = 0; i < schema.dependencies[property].length; ++i) {
						if (property in obj && !(schema.dependencies[property][i] in obj)) {
							throw new Error('JSON object' + getName(names.concat([schema.dependencies[property][i]])) + ' is required by property \'' + property + '\'');
						}
					}
					break;
				case 'object':
					// schema dependency
					validateSchema(obj, schema.dependencies[property], names.concat([ '[dependencies.'+property+']' ]));
					break;
			}
		}
	}
}

function validateNumber(obj, schema, names) {
	//console.log('***', names, 'validateNumber');

	if (schema.minimum !== undefined) {
		if (schema.exclusiveMinimum ? obj <= schema.minimum : obj < schema.minimum) {
			throwInvalidValue(names, obj, (schema.exclusiveMinimum ? 'greater than' : 'at least') + ' ' + schema.minimum);
		}
	}

	if (schema.maximum !== undefined) {
		if (schema.exclusiveMaximum ? obj >= schema.maximum : obj > schema.maximum) {
			throwInvalidValue(names, obj, (schema.exclusiveMaximum ? 'less than' : 'at most') + ' ' + schema.maximum);
		}
	}

	if (schema.divisibleBy !== undefined) {
		if (!isOfType(obj / schema.divisibleBy, 'integer')) {
			throwInvalidValue(names, obj, 'divisible by ' + schema.divisibleBy);
		}
	}
}

function validateString(obj, schema, names) {
	//console.log('***', names, 'validateString');

	if (schema.minLength !== undefined) {
		if (obj.length < schema.minLength) {
			throwInvalidAttributeValue(names, 'length', obj.length, 'at least ' + schema.minLength);
		}
	}

	if (schema.maxLength !== undefined) {
		if (obj.length > schema.maxLength) {
			throwInvalidAttributeValue(names, 'length', obj.length, 'at most ' + schema.maxLength);
		}
	}

	if (schema.pattern !== undefined) {
		if (!obj.match(RegExp(schema.pattern))) {
			throw new Error('JSON object' + getName(names) + ' does not match pattern');
		}
	}
}

function validateFormat(obj, schema, names) {
	//console.log('***', names, 'validateFormat');
	if (schema.format !== undefined) {
		var format = formats[schema.format];
		if (format !== undefined) {
			var conforms = true;
			if (format.regex) {
				conforms = obj.match(format.regex);
			} else if (format.func) {
				conforms = format.func(obj);
			}
			if (!conforms) {
				throw new Error('JSON object' + getName(names) + ' does not conform to the \'' + schema.format + '\' format');
			}
		}
	}
}

function validateItem(obj, schema, names) {
	//console.log('***', names, 'validateItem');
	switch (getType(obj)) {
		case 'number':
		case 'integer':
			validateNumber(obj, schema, names);
			break;
		case 'string':
			validateString(obj, schema, names);
			break;
	}

	validateFormat(obj, schema, names);
}

function validateSchema(obj, schema, names) {
	//console.log('***', names, 'validateSchema');

	validateRequired(obj, schema, names);
	if (obj === undefined) {
		obj = applyDefault(obj, schema, names);
	}
	if (obj !== undefined) {
		validateType(obj, schema, names);
		validateDisallow(obj, schema, names);
		validateEnum(obj, schema, names);

		switch (getType(obj)) {
			case 'object':
				validateObject(obj, schema, names);
				break;
			case 'array':
				validateArray(obj, schema, names);
				break;
			default:
				validateItem(obj, schema, names);
		}
	}

	return obj;
}

// Two operation modes:
// * Synchronous - done callback is not provided. will return nothing or throw error
// * Asynchronous - done callback is provided. will not throw error.
//        will call callback with error as first parameter and object as second
// Schema is expected to be validated.
module.exports = function(obj, schema, done) {
	try {
		if (obj === undefined) {
			throw new Error('JSON object is undefined');
		}

		validateSchema(obj, schema, []);

		if (done) {
			done(null, obj);
		}
	} catch(err) {
		if (done) {
			done(err);
		} else {
			throw err;
		}
	}
};
<commit_msg>Let validateRequired() check the undefined value, to generate proper error message<commit_after>var formats = require('./formats').formats;
var common = require('./common'),
	getType = common.getType,
	prettyType = common.prettyType,
	isOfType = common.isOfType,
	getName = common.getName,
	deepEquals = common.deepEquals;

function throwInvalidValue(names, value, expected) {
	throw new Error('JSON object' + getName(names) + ' is ' + value + ' when it should be ' + expected);
}

function throwInvalidAttributeValue(names, attribFullName, value, expected) {
	throw new Error('JSON object' + getName(names) + ': ' + attribFullName + ' is ' + value + ' when it should be ' + expected);
}

function throwInvalidType(names, value, expected) {
	throw new Error('JSON object' + getName(names) + ' is ' + prettyType(getType(value)) + ' when it should be ' + expected);
}

function throwInvalidDisallow(names, value, expected) {
	throw new Error('JSON object' + getName(names) + ' is ' + prettyType(getType(value)) + ' when it should not be ' + expected);
}

function validateRequired(obj, schema, names) {
	//console.log('***', names, 'validateRequired');
	if (schema.required) {
		if (obj === undefined) {
			throw new Error('JSON object' + getName(names) + ' is required');
		}
	}
}

function applyDefault(obj, schema, names) {
	//console.log('***', names, 'applyDefault');
	if (schema.default !== undefined) {
		obj = schema.default;
	}

	return obj;
}

function validateType(obj, schema, names) {
	//console.log('***', names, 'validateType');
	if (schema.type !== undefined) {
		switch (getType(schema.type)) {
			case 'string':
				// simple type
				if (!isOfType(obj, schema.type)) {
					throwInvalidType(names, obj, prettyType(schema.type));
				}
				break;
			case 'array':
				// union type
				for (var i = 0; i < schema.type.length; ++i) {
					switch (getType(schema.type[i])) {
						case 'string':
							// simple type (inside union type)
							if (isOfType(obj, schema.type[i])) {
								return; // success
							}
							break;
						case 'object':
							// schema (inside union type)
							try {
								return validateSchema(obj, schema.type[i], names);
							} catch(err) {
								// validation failed
								// TOOD: consider propagating error message upwards
							}
							break;
					}
				}
				throwInvalidType(names, obj, 'either ' + schema.type.map(prettyType).join(' or '));
				break;
		}
	}
}

function validateDisallow(obj, schema, names) {
	//console.log('***', names, 'validateDisallow');
	if (schema.disallow !== undefined) {
		switch (getType(schema.disallow)) {
			case 'string':
				// simple type
				if (isOfType(obj, schema.disallow)) {
					throwInvalidDisallow(names, obj, prettyType(schema.disallow));
				}
				break;
			case 'array':
				// union type
				for (var i = 0; i < schema.disallow.length; ++i) {
					switch (getType(schema.disallow[i])) {
						case 'string':
							// simple type (inside union type)
							if (isOfType(obj, schema.disallow[i])) {
								throwInvalidType(names, obj, 'neither ' + schema.disallow.map(prettyType).join(' nor '));
							}
							break;
						case 'object':
							// schema (inside union type)
							try {
								validateSchema(obj, schema.disallow[i], names);
							} catch(err) {
								// validation failed
								continue;
							}
							throwInvalidType(names, obj, 'neither ' + schema.disallow.map(prettyType).join(' nor '));
							// TOOD: consider propagating error message upwards
							break;
					}
				}
				break;
		}
	}
}

function validateEnum(obj, schema, names) {
	//console.log('***', names, 'validateEnum');
	if (schema['enum'] !== undefined) {
		for (var i = 0; i < schema['enum'].length; ++i) {
			if (deepEquals(obj, schema['enum'][i])) {
				return;
			}
		}
		throw new Error('JSON object' + getName(names) + ' is not in enum');
	}
}

function validateArray(obj, schema, names) {
	//console.log('***', names, 'validateArray');
	var i, j;

	if (schema.minItems !== undefined) {
		if (obj.length < schema.minItems) {
			throwInvalidAttributeValue(names, 'number of items', obj.length, 'at least ' + schema.minItems);
		}
	}

	if (schema.maxItems !== undefined) {
		if (obj.length > schema.maxItems) {
			throwInvalidAttributeValue(names, 'number of items', obj.length, 'at most ' + schema.maxItems);
		}
	}

	if (schema.items !== undefined) {
		switch (getType(schema.items)) {
			case 'object':
				// all the items in the array MUST be valid according to the schema
				for (i = 0; i < obj.length; ++i) {
					obj[i] = validateSchema(obj[i], schema.items, names.concat([ '['+i+']' ]));
				}
				break;
			case 'array':
				// each position in the instance array MUST conform to the schema in the corresponding position for this array
				var numChecks = Math.min(obj.length, schema.items.length);
				for (i = 0; i < numChecks; ++i) {
					obj[i] = validateSchema(obj[i], schema.items[i], names.concat([ '['+i+']' ]));
				}
				if (obj.length > schema.items.length) {
					if (schema.additionalItems !== undefined) {
						if (schema.additionalItems === false) {
							throwInvalidAttributeValue(names, 'number of items', obj.length, 'at most ' + schema.items.length + ' - the length of schema items');
						}
						for (; i < obj.length; ++i) {
							obj[i] = validateSchema(obj[i], schema.additionalItems, names.concat([ '['+i+']' ]));
						}
					}
				}
				break;
		}
	}

	if (schema.uniqueItems !== undefined) {
		for (i = 0; i < obj.length - 1; ++i) {
			for (j = i + 1; j < obj.length; ++j) {
				if (deepEquals(obj[i], obj[j])) {
					throw new Error('JSON object' + getName(names) + ' items are not unique: element ' + i + ' equals element ' + j);
				}
			}
		}
	}
}

function validateObject(obj, schema, names) {
	//console.log('***', names, 'validateObject');
	var prop, property;
	if (schema.properties !== undefined) {
		for (property in schema.properties) {
			prop = validateSchema(obj[property], schema.properties[property], names.concat([property]));
			if (prop === undefined) {
				delete obj[property];
			} else {
				obj[property] = prop;
			}
		}
	}

	var matchingProperties = {};
	if (schema.patternProperties !== undefined) {
		for (var reStr in schema.patternProperties) {
			var re = RegExp(reStr);
			for (property in obj) {
				if (property.match(re)) {
					matchingProperties[property] = true;
					prop = validateSchema(obj[property], schema.patternProperties[reStr], names.concat(['patternProperties./' + property + '/']));
					if (prop === undefined) {
						delete obj[property];
					} else {
						obj[property] = prop;
					}
				}
			}
		}
	}

	if (schema.additionalProperties !== undefined) {
		for (property in obj) {
			if (schema.properties !== undefined && property in schema.properties) {
				continue;
			}
			if (property in matchingProperties) {
				continue;
			}
			// additional
			if (schema.additionalProperties === false) {
				throw new Error('JSON object' + getName(names.concat([property])) + ' is not explicitly defined and therefore not allowed');
			}
			obj[property] = validateSchema(obj[property], schema.additionalProperties, names.concat([property]));
		}
	}

	if (schema.dependencies !== undefined) {
		for (property in schema.dependencies) {
			switch (getType(schema.dependencies[property])) {
				case 'string':
					// simple dependency
					if (property in obj && !(schema.dependencies[property] in obj)) {
						throw new Error('JSON object' + getName(names.concat([schema.dependencies[property]])) + ' is required by property \'' + property + '\'');
					}
					break;
				case 'array':
					// simple dependency tuple
					for (var i = 0; i < schema.dependencies[property].length; ++i) {
						if (property in obj && !(schema.dependencies[property][i] in obj)) {
							throw new Error('JSON object' + getName(names.concat([schema.dependencies[property][i]])) + ' is required by property \'' + property + '\'');
						}
					}
					break;
				case 'object':
					// schema dependency
					validateSchema(obj, schema.dependencies[property], names.concat([ '[dependencies.'+property+']' ]));
					break;
			}
		}
	}
}

function validateNumber(obj, schema, names) {
	//console.log('***', names, 'validateNumber');

	if (schema.minimum !== undefined) {
		if (schema.exclusiveMinimum ? obj <= schema.minimum : obj < schema.minimum) {
			throwInvalidValue(names, obj, (schema.exclusiveMinimum ? 'greater than' : 'at least') + ' ' + schema.minimum);
		}
	}

	if (schema.maximum !== undefined) {
		if (schema.exclusiveMaximum ? obj >= schema.maximum : obj > schema.maximum) {
			throwInvalidValue(names, obj, (schema.exclusiveMaximum ? 'less than' : 'at most') + ' ' + schema.maximum);
		}
	}

	if (schema.divisibleBy !== undefined) {
		if (!isOfType(obj / schema.divisibleBy, 'integer')) {
			throwInvalidValue(names, obj, 'divisible by ' + schema.divisibleBy);
		}
	}
}

function validateString(obj, schema, names) {
	//console.log('***', names, 'validateString');

	if (schema.minLength !== undefined) {
		if (obj.length < schema.minLength) {
			throwInvalidAttributeValue(names, 'length', obj.length, 'at least ' + schema.minLength);
		}
	}

	if (schema.maxLength !== undefined) {
		if (obj.length > schema.maxLength) {
			throwInvalidAttributeValue(names, 'length', obj.length, 'at most ' + schema.maxLength);
		}
	}

	if (schema.pattern !== undefined) {
		if (!obj.match(RegExp(schema.pattern))) {
			throw new Error('JSON object' + getName(names) + ' does not match pattern');
		}
	}
}

function validateFormat(obj, schema, names) {
	//console.log('***', names, 'validateFormat');
	if (schema.format !== undefined) {
		var format = formats[schema.format];
		if (format !== undefined) {
			var conforms = true;
			if (format.regex) {
				conforms = obj.match(format.regex);
			} else if (format.func) {
				conforms = format.func(obj);
			}
			if (!conforms) {
				throw new Error('JSON object' + getName(names) + ' does not conform to the \'' + schema.format + '\' format');
			}
		}
	}
}

function validateItem(obj, schema, names) {
	//console.log('***', names, 'validateItem');
	switch (getType(obj)) {
		case 'number':
		case 'integer':
			validateNumber(obj, schema, names);
			break;
		case 'string':
			validateString(obj, schema, names);
			break;
	}

	validateFormat(obj, schema, names);
}

function validateSchema(obj, schema, names) {
	//console.log('***', names, 'validateSchema');

	validateRequired(obj, schema, names);
	if (obj === undefined) {
		obj = applyDefault(obj, schema, names);
	}
	if (obj !== undefined) {
		validateType(obj, schema, names);
		validateDisallow(obj, schema, names);
		validateEnum(obj, schema, names);

		switch (getType(obj)) {
			case 'object':
				validateObject(obj, schema, names);
				break;
			case 'array':
				validateArray(obj, schema, names);
				break;
			default:
				validateItem(obj, schema, names);
		}
	}

	return obj;
}

// Two operation modes:
// * Synchronous - done callback is not provided. will return nothing or throw error
// * Asynchronous - done callback is provided. will not throw error.
//        will call callback with error as first parameter and object as second
// Schema is expected to be validated.
module.exports = function(obj, schema, done) {
	try {
		validateSchema(obj, schema, []);

		if (done) {
			done(null, obj);
		}
	} catch(err) {
		if (done) {
			done(err);
		} else {
			throw err;
		}
	}
};
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var $documents = [];

if ( window ) $documents.push(window.document);

// TODO: clean this up, hide $WID__ in closure
var $WID__ = 0;
function $addWindow(w) {
   w.window.$WID = $WID__++;
   $documents.push(w.document);
}
function $removeWindow(w) {
  for ( var i = $documents.length - 1 ; i >= 0 ; i-- ) {
    if ( ! $documents[i].defaultView || $documents[i].defaultView === w )
      $documents.splice(i,1);
  }
}

/** Replacement for getElementById **/
var $ = function (id) {
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    if ( document.FOAM_OBJECTS && document.FOAM_OBJECTS[id] )
      return document.FOAM_OBJECTS[id];

    var ret = $documents[i].getElementById(id);

    if ( ret ) return ret;
  }
  return undefined;
};
/** Replacement for getElementByClassName **/
var $$ = function (cls) {
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    var ret = $documents[i].getElementsByClassName(cls);

    if ( ret.length > 0 ) return ret;
  }
  return [];
};

var FOAM = function(map) {
   var obj = JSONUtil.mapToObj(map);
   return obj;
};

/**
 * Register a lazy factory for the specified name within a
 * specified context.
 * The first time the name is looked up, the factory will be invoked
 * and its value will be stored in the named slot and then returned.
 * Future lookups to the same slot will return the originally created
 * value.
 **/
FOAM.putFactory = function(ctx, name, factory) {
  ctx.__defineGetter__(name, function() {
    console.log('Bouncing Factory: ', name);
    delete ctx[name];
    return ctx[name] = factory();
  });
};

/*
// Simple Immediate Model Definition
var MODEL = function(m) {
  var model = Model.create(m);

  GLOBAL[model.name] = model;
}
*/

/*
// Lazy Model Definition - Only creates Model when first referenced
var MODEL = function(m) {
  Object.defineProperty(GLOBAL, m.name, {
    get: function () {
      // console.log('bounceFactory: ', m.name);
      Object.defineProperty(GLOBAL, m.name, {value: null});
      var model = JSONUtil.mapToObj(m, Model);
      Object.defineProperty(GLOBAL, m.name, {value: model});
      return model;
    },
    configurable: true
  });
}
*/

var UNUSED_MODELS = {};
var USED_MODELS = {};

// Lazy Model Definition - Only creates Model when first referenced
function MODEL(m) {
  // Templates need to access document.currentScript in order to know
  // where to load the template from, so the instantiation of Models
  // with templates can't be delayed (yet).
  if ( m.templates ) {
    registerModel.call(this, JSONUtil.mapToObj(m, Model));
    USED_MODELS[m.name] = true;
    return;
  }

  UNUSED_MODELS[m.name] = true;
  Object.defineProperty(GLOBAL, m.name, {
    get: function () {
      USED_MODELS[m.name] = true;
      delete UNUSED_MODELS[m.name];
      Object.defineProperty(GLOBAL, m.name, {value: null, configurable: true});
      registerModel(JSONUtil.mapToObj(m, Model));
      return this[m.name];
    },
    configurable: true
  });
}

FOAM.browse = function(model, opt_dao) {
   var dao = opt_dao || GLOBAL[model.name + 'DAO'] || GLOBAL[model.plural];

   if ( ! dao ) {
      dao = StorageDAO.create({ model: model });
      GLOBAL[model.name + 'DAO'] = dao;
   }

   var ctrl = DAOController.create({
     model:     model,
     dao:       dao,
     useSearchView: false
   });

  var stack = GLOBAL.stack;
  ctrl.X.stack = stack;
  stack.pushView(ctrl);
};

FOAM.lookup = function(key, opt_X) {
  if ( ! ( typeof key === 'string' ) ) return key;

  var path = key.split('.');
  var root = opt_X || GLOBAL;
  for ( var i = 0 ; i < path.length ; i++ ) root = root[path[i]];

  return root;
};

MODEL({
  name: 'UnitTestResultView',
  extendsModel: 'View',
  properties: [
    {
      name: 'value',
      type: 'Value'
    }
  ],

  methods: {
    toHTML: function() {
      var test = this.data;
      var cssClass = test.failed > 0 ? 'foamTestFailed' : 'foamTestPassed';
      var results = test.results.replace(/\n/g, '
\n');
      var pre = '
' +
          '
' + test.description + '
' +
          '
Passed: ' + test.passed + '  ' +
          'Failed: ' + test.failed + '
';
      if (results.length) {
        pre += '
' + results + '
';
      }

      if (test.tests && test.tests.length > 0) {
        return pre + '
' + this.toInnerHTML() + '

';
      } else {
        return pre + "
";
      }
    },

    toInnerHTML: function() {
      var inner = ArrayListView.create({
        data: this.data.tests,
        listView: UnitTestResultView
      });
      this.addChild(inner);
      return inner.toHTML();
    }
  }
});

// Given a model and a DOM element, render test results into the element.
// TODO: Put me into a method on models.
function testModel(model, element) {
  if (!model.tests || !element) return;
  model.tests.forEach(function(t) {
    t.test();
    var view = UnitTestResultView.create({ data: t });
    element.insertAdjacentHTML('beforeend', view.toHTML());
    view.initHTML();
  });
}

function arequire(modelName, opt_X) {
  var X = opt_X || GLOBAL;
  var model = FOAM.lookup(modelName, X);

  /** This is so that if the model is arequire'd concurrently the
   *  initialization isn't done more than once.
   **/
  if ( ! model.required__ ) {
    // TODO: eventually this should just call the arequire() method on the Model
    var args = [];
    if ( model.templates ) for ( var i = 0 ; i < model.templates.length ; i++ ) {
      var t = model.templates[i];
      args.push(aseq(
        aevalTemplate(model.templates[i]),
        (function(t) { return function(ret, m) {
          model.getPrototype()[t.name] = m;
          ret();
        };})(t)
      ));
    }

    model.required__ = amemo(aseq(
      apar.apply(apar, args),
      aconstant(model)));
  }

  return model.required__;
}

var FOAM_POWERED = '\
\
FOAM\
 POWERED';
<commit_msg>Contextify FOAM.browse, let it create its own stack view if there's none in the stack.<commit_after>/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var $documents = [];

if ( window ) $documents.push(window.document);

// TODO: clean this up, hide $WID__ in closure
var $WID__ = 0;
function $addWindow(w) {
   w.window.$WID = $WID__++;
   $documents.push(w.document);
}
function $removeWindow(w) {
  for ( var i = $documents.length - 1 ; i >= 0 ; i-- ) {
    if ( ! $documents[i].defaultView || $documents[i].defaultView === w )
      $documents.splice(i,1);
  }
}

/** Replacement for getElementById **/
var $ = function (id) {
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    if ( document.FOAM_OBJECTS && document.FOAM_OBJECTS[id] )
      return document.FOAM_OBJECTS[id];

    var ret = $documents[i].getElementById(id);

    if ( ret ) return ret;
  }
  return undefined;
};
/** Replacement for getElementByClassName **/
var $$ = function (cls) {
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    var ret = $documents[i].getElementsByClassName(cls);

    if ( ret.length > 0 ) return ret;
  }
  return [];
};

var FOAM = function(map) {
   var obj = JSONUtil.mapToObj(map);
   return obj;
};

/**
 * Register a lazy factory for the specified name within a
 * specified context.
 * The first time the name is looked up, the factory will be invoked
 * and its value will be stored in the named slot and then returned.
 * Future lookups to the same slot will return the originally created
 * value.
 **/
FOAM.putFactory = function(ctx, name, factory) {
  ctx.__defineGetter__(name, function() {
    console.log('Bouncing Factory: ', name);
    delete ctx[name];
    return ctx[name] = factory();
  });
};

/*
// Simple Immediate Model Definition
var MODEL = function(m) {
  var model = Model.create(m);

  GLOBAL[model.name] = model;
}
*/

/*
// Lazy Model Definition - Only creates Model when first referenced
var MODEL = function(m) {
  Object.defineProperty(GLOBAL, m.name, {
    get: function () {
      // console.log('bounceFactory: ', m.name);
      Object.defineProperty(GLOBAL, m.name, {value: null});
      var model = JSONUtil.mapToObj(m, Model);
      Object.defineProperty(GLOBAL, m.name, {value: model});
      return model;
    },
    configurable: true
  });
}
*/

var UNUSED_MODELS = {};
var USED_MODELS = {};

// Lazy Model Definition - Only creates Model when first referenced
function MODEL(m) {
  // Templates need to access document.currentScript in order to know
  // where to load the template from, so the instantiation of Models
  // with templates can't be delayed (yet).
  if ( m.templates ) {
    registerModel.call(this, JSONUtil.mapToObj(m, Model));
    USED_MODELS[m.name] = true;
    return;
  }

  UNUSED_MODELS[m.name] = true;
  Object.defineProperty(GLOBAL, m.name, {
    get: function () {
      USED_MODELS[m.name] = true;
      delete UNUSED_MODELS[m.name];
      Object.defineProperty(GLOBAL, m.name, {value: null, configurable: true});
      registerModel(JSONUtil.mapToObj(m, Model));
      return this[m.name];
    },
    configurable: true
  });
}

FOAM.browse = function(model, opt_dao, opt_X) {
   var Y = opt_X || X.sub(undefined, "FOAM BROWSER");

   if ( typeof model === 'string' ) model = Y[model];

   var dao = opt_dao || Y[model.name + 'DAO'] || Y[model.plural];

   if ( ! dao ) {
      dao = Y.StorageDAO.create({ model: model });
      Y[model.name + 'DAO'] = dao;
   }

   var ctrl = Y.DAOController.create({
     model:     model,
     dao:       dao,
     useSearchView: false
   });

  if ( ! Y.stack ) {
    Y.stack = Y.StackView.create();
    Y.document.body.insertAdjacentHTML('beforeend', Y.stack.toHTML());
    Y.stack.initHTML();
    Y.stack.setTopView(ctrl);
  } else {
    Y.stack.pushView(ctrl);
  }
};

FOAM.lookup = function(key, opt_X) {
  if ( ! ( typeof key === 'string' ) ) return key;

  var path = key.split('.');
  var root = opt_X || GLOBAL;
  for ( var i = 0 ; i < path.length ; i++ ) root = root[path[i]];

  return root;
};

MODEL({
  name: 'UnitTestResultView',
  extendsModel: 'View',
  properties: [
    {
      name: 'value',
      type: 'Value'
    }
  ],

  methods: {
    toHTML: function() {
      var test = this.data;
      var cssClass = test.failed > 0 ? 'foamTestFailed' : 'foamTestPassed';
      var results = test.results.replace(/\n/g, '
\n');
      var pre = '
' +
          '
' + test.description + '
' +
          '
Passed: ' + test.passed + '  ' +
          'Failed: ' + test.failed + '
';
      if (results.length) {
        pre += '
' + results + '
';
      }

      if (test.tests && test.tests.length > 0) {
        return pre + '
' + this.toInnerHTML() + '

';
      } else {
        return pre + "
";
      }
    },

    toInnerHTML: function() {
      var inner = ArrayListView.create({
        data: this.data.tests,
        listView: UnitTestResultView
      });
      this.addChild(inner);
      return inner.toHTML();
    }
  }
});

// Given a model and a DOM element, render test results into the element.
// TODO: Put me into a method on models.
function testModel(model, element) {
  if (!model.tests || !element) return;
  model.tests.forEach(function(t) {
    t.test();
    var view = UnitTestResultView.create({ data: t });
    element.insertAdjacentHTML('beforeend', view.toHTML());
    view.initHTML();
  });
}

function arequire(modelName, opt_X) {
  var X = opt_X || GLOBAL;
  var model = FOAM.lookup(modelName, X);

  /** This is so that if the model is arequire'd concurrently the
   *  initialization isn't done more than once.
   **/
  if ( ! model.required__ ) {
    // TODO: eventually this should just call the arequire() method on the Model
    var args = [];
    if ( model.templates ) for ( var i = 0 ; i < model.templates.length ; i++ ) {
      var t = model.templates[i];
      args.push(aseq(
        aevalTemplate(model.templates[i]),
        (function(t) { return function(ret, m) {
          model.getPrototype()[t.name] = m;
          ret();
        };})(t)
      ));
    }

    model.required__ = amemo(aseq(
      apar.apply(apar, args),
      aconstant(model)));
  }

  return model.required__;
}

var FOAM_POWERED = '\
\
FOAM\
 POWERED';
<|endoftext|>


<commit_before>"use strict";
// requestAnimationFrame polyfill
var lastTime = 0;
if (!window.requestAnimationFrame) {
  ["webkit", "moz"].forEach(function(vendor) {
    window.requestAnimationFrame = window[vendor + "RequestAnimationFrame"];
  });
}

if (!window.requestAnimationFrame) {
  window.requestAnimationFrame = function(callback, element) {
    var currTime = new Date().getTime();
    var timeToCall = Math.max(0, 16 - (currTime - lastTime));
    var id = window.setTimeout(function(){callback(currTime + timeToCall);}, timeToCall);
    lastTime = currTime + timeToCall;
    return id;
  };
}
// end requestAnimationFrame polyfill

// declare global namespace
// TODO: move this global stuff to a globals class?
var Plx = {};

Plx.Game = function(width, height, firstSceneClass) {
  this.width = width;
  this.height = height;
  this.firstSceneClass = firstSceneClass;
  this.sceneDirector = new Plx.SceneDirector();
  this.scenes = [];
  this.lastTime = -1;
  this.updateRate = 1000 / 60;
  this.deltaTime = 0;
  this.preloader = new Plx.Preloader();
  // TODO: seems like the game shouldn't have all this specific code to mediate between the preloader and the sprite store in it
  this.preloader.beacon.observe(this, "imageLoaded", this.onImageLoaded);
  this.preloader.beacon.observe(this, "completed", this.onPreloaderCompleted);
  this.spriteStore = new Plx.SpriteStore();
  this.saveData = new Plx.SaveData();
  this.entityFactory = new Plx.EntityFactory(this);

  // TODO: move the default entity types somewhere more appropriate
  this.entityFactory.registerType("PlxButton", [
    {type: Plx.PhysicsComponent, name: "physics", params: {}, },
    {type: Plx.Sprite, name: "sprite", params: {autoSizePhysics: true}, },
    {type: Plx.Tappable, name: "tappable", params: {}, },
  ]);
  this.entityFactory.registerType("PlxSprient", [
    {type: Plx.PhysicsComponent, name: "physics", params: {}, },
    {type: Plx.Sprite, name: "sprite", params: {}, },
  ]);
};

Plx.Game.prototype.init = function() {
  this.preloader.load();
};

Plx.Game.prototype.update = function(event) {
  var time = new Date().getTime();
  if (this.lastTime == -1)
    this.lastTime = time;
  this.deltaTime += time - this.lastTime;
  this.lastTime = time;
  // if we've missed a ton of frames, then the user has navigated away or something, so just skip them
  if (this.deltaTime > this.updateRate * 10)
    this.deltaTime = 0;
  var scene, i;
  while (this.deltaTime > this.updateRate) {
    for (i = 0; i < this.scenes.length; i++) {
      scene = this.scenes[i];
      if (!scene.paused)
        scene.update();
    }
    this.deltaTime -= this.updateRate;
  }
  for (i = 0; i < this.scenes.length; i ++) {
    scene = this.scenes[i];
    scene.render(this.deltaTime / this.updateRate);
  }
  var _this = this;
  window.requestAnimationFrame(function(event){_this.update(event);});
};

Plx.Game.prototype.addScene = function(sceneClass, handoffData) {
  var scene = new sceneClass();
  scene.game = this;
  scene.init(handoffData);
  scene.beacon.observe(this, "completed", this.onSceneCompleted);
  this.scenes.push(scene);
  scene.beacon.emit("added", null);
};

Plx.Game.prototype.onSceneCompleted = function(event) {
  event.beacon.ignore(this, "completed", this.onSceneCompleted);
  var scene;
  for (var i = this.scenes.length - 1; i >= 0; i--) {
    scene = this.scenes[i];
    if (event.beacon.owner == scene)
      this.scenes.splice(i, 1);
  }
  event.beacon.owner.destroy();
  this.addScene(event.data.sceneClass, event.data.handoffData);
};

Plx.Game.prototype.onPreloaderCompleted = function(event) {
  this.preloader.beacon.ignore(this, "completed", this.onPreloaderCompleted);
  this.addScene(this.firstSceneClass);
  this.update();
};

Plx.Game.prototype.onImageLoaded = function(event) {
  this.spriteStore.addImage(event.data.image, event.data.path);
};
<commit_msg>added data store component to PlxButton<commit_after>"use strict";
// requestAnimationFrame polyfill
var lastTime = 0;
if (!window.requestAnimationFrame) {
  ["webkit", "moz"].forEach(function(vendor) {
    window.requestAnimationFrame = window[vendor + "RequestAnimationFrame"];
  });
}

if (!window.requestAnimationFrame) {
  window.requestAnimationFrame = function(callback, element) {
    var currTime = new Date().getTime();
    var timeToCall = Math.max(0, 16 - (currTime - lastTime));
    var id = window.setTimeout(function(){callback(currTime + timeToCall);}, timeToCall);
    lastTime = currTime + timeToCall;
    return id;
  };
}
// end requestAnimationFrame polyfill

// declare global namespace
// TODO: move this global stuff to a globals class?
var Plx = {};

Plx.Game = function(width, height, firstSceneClass) {
  this.width = width;
  this.height = height;
  this.firstSceneClass = firstSceneClass;
  this.sceneDirector = new Plx.SceneDirector();
  this.scenes = [];
  this.lastTime = -1;
  this.updateRate = 1000 / 60;
  this.deltaTime = 0;
  this.preloader = new Plx.Preloader();
  // TODO: seems like the game shouldn't have all this specific code to mediate between the preloader and the sprite store in it
  this.preloader.beacon.observe(this, "imageLoaded", this.onImageLoaded);
  this.preloader.beacon.observe(this, "completed", this.onPreloaderCompleted);
  this.spriteStore = new Plx.SpriteStore();
  this.saveData = new Plx.SaveData();
  this.entityFactory = new Plx.EntityFactory(this);

  // TODO: move the default entity types somewhere more appropriate
  this.entityFactory.registerType("PlxButton", [
    {type: Plx.PhysicsComponent, name: "physics", params: {}},
    {type: Plx.Sprite, name: "sprite", params: {autoSizePhysics: true}},
    {type: Plx.Tappable, name: "tappable", params: {}},
    {type: Plx.Data, name: "data", params: {}},
  ]);
  this.entityFactory.registerType("PlxSprient", [
    {type: Plx.PhysicsComponent, name: "physics", params: {}},
    {type: Plx.Sprite, name: "sprite", params: {}},
  ]);
};

Plx.Game.prototype.init = function() {
  this.preloader.load();
};

Plx.Game.prototype.update = function(event) {
  var time = new Date().getTime();
  if (this.lastTime == -1)
    this.lastTime = time;
  this.deltaTime += time - this.lastTime;
  this.lastTime = time;
  // if we've missed a ton of frames, then the user has navigated away or something, so just skip them
  if (this.deltaTime > this.updateRate * 10)
    this.deltaTime = 0;
  var scene, i;
  while (this.deltaTime > this.updateRate) {
    for (i = 0; i < this.scenes.length; i++) {
      scene = this.scenes[i];
      if (!scene.paused)
        scene.update();
    }
    this.deltaTime -= this.updateRate;
  }
  for (i = 0; i < this.scenes.length; i ++) {
    scene = this.scenes[i];
    scene.render(this.deltaTime / this.updateRate);
  }
  var _this = this;
  window.requestAnimationFrame(function(event){_this.update(event);});
};

Plx.Game.prototype.addScene = function(sceneClass, handoffData) {
  var scene = new sceneClass();
  scene.game = this;
  scene.init(handoffData);
  scene.beacon.observe(this, "completed", this.onSceneCompleted);
  this.scenes.push(scene);
  scene.beacon.emit("added", null);
};

Plx.Game.prototype.onSceneCompleted = function(event) {
  event.beacon.ignore(this, "completed", this.onSceneCompleted);
  var scene;
  for (var i = this.scenes.length - 1; i >= 0; i--) {
    scene = this.scenes[i];
    if (event.beacon.owner == scene)
      this.scenes.splice(i, 1);
  }
  event.beacon.owner.destroy();
  this.addScene(event.data.sceneClass, event.data.handoffData);
};

Plx.Game.prototype.onPreloaderCompleted = function(event) {
  this.preloader.beacon.ignore(this, "completed", this.onPreloaderCompleted);
  this.addScene(this.firstSceneClass);
  this.update();
};

Plx.Game.prototype.onImageLoaded = function(event) {
  this.spriteStore.addImage(event.data.image, event.data.path);
};
<|endoftext|>