<commit_before>/*
Ads Auto Switcher - ©BSoft&Co 2012
----------------------------------

This script allows you to use both iAds and Admob at the same place within
a ©Titanium iOS project.

It works with ti.admob module (1.2) which can be found here :
https://github.com/appcelerator/titanium_modules/tree/master/admob/mobile/ios

For now, only iphone's screen size is managed, in portrait orientation.

Per default, Admob is always shown, while iAds is shown (on top of admob) and
hidden during some seconds.
The script manages to move or to resize an UI object when ads are visible.

Be carefull with admob when closing a window. The module ti.admob is not
really good to stop a refreshing admob.. :/
-------------------------------------------------------------------------------

history:
--------
v1.23
    + Now compatible with ti.admob 1.2
    + Add the values 'adDateOfBirth', 'adGender', 'adKeywords' and 'adTesting' for admob
    + Add the possibility to use or not iAds, like useAdmob : ads.useIads
v1.22
    - delete 'adDelay' parameter
v1.21
    + ads.typeMove = '' for fixed ads. Set ads.obj2Move = null
V1.2
    + ads.useAdmob = true/false : to enabled or not admob. If false, only iAds
    is shown
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------

How To use :
------------
Ti.include('ads.js');
ads.admobPublisherId = 'myadmobKey';
ads.iAdsShowHide = "iAdsShowHide.xx";
ads.init(tableViewObj,0,0,'top');
// --> This inits an ad positionned at top = 0. The tableview name tableViewObj
// will be moved down at top = 50 when an ad is visible.
ads.showAdvert();
//setTimeout(ads.showAdvert, 2000);
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
Ads Auto Switcher is licensed under the Apache Public License (Version 2).
Please see the LICENSE file for the full license.

*/

Titanium.Admob = Ti.Admob = require('ti.admob');
/*
//iads size 320,50 // 480,32 // ipad : 768,66 // 1024,66
// Size for the ads to print
var adSize = [{w:320,h:50},{w:300,h:250},{w:468,h:60},{w:728,h:90}];

*/
var ads = {};
(function() {
    // The object to be moved/resized when an ad is visible
    ads.obj2Move = {};
    // Top position (px) of the object when no ads on screen (typeMove = 'top')
    // || Top ads position (typeMove = 'height')
    ads.topPos = 0;
    // Max height (px) for the object when no ads (don't mind when typeMove = 'top'
    ads.objHmax = 460;
    // Top margin (px) for the object to move (optional)
    ads.objMargin = 0;
    // Type of move for the object (top/height/'').
    // - top : To move the top position of the object
    // - height : To resize the height of the object.
    //     /|\ : If the position of the object to resized is defined with TOP, the object will be resized from its bottom.
    //         Its TOP position stay fixed!
    //         So to resized the object from the top, with a fxed bottom position, it should be defined with bottom position.
    // - '' : To have a fixed ads position. (If so, also set obj2Move = null)
    ads.typeMove = 'top';
    // To enabled or not admob. Don't forget to change ads.iadsStartDelay if no admob
    ads.useAdmob = true;
    // To enabled or not iAds.
    ads.useIads = true;
    // Milliseconds before starting iads
    ads.iadsStartDelay = 30000;
    // Initial top position for the iads banner
    ads.iadsTopInit = -50;
    // Initial top position for the admob banner
    ads.admobTopInit = -50;
    // The event's name to be fired. It's recommanded to use one per ads's instance.
    ads.iAdsShowHide = 'iAdsShowHide';
    // Required : your admob id
    ads.admobPublisherId = null;
    // Date of birth, to better target the ads, new Date(1985, 10, 1, 12, 1, 1),
    ads.adDateOfBirth = '';
    // Gender 'male' or 'female'
    ads.adGender  = '';
    // Keywords about the ads to print
    ads.adKeywords = '';
    // Test mode for admob
    ads.adTesting = false;
    // admob's backgroundColor
    ads.adBackgroundColor =  "#FDFEFD";
    // iAds's border color
    ads.iAdsBorderColor = '#FDFEFD';
    // iAds's backgroundColor
    ads.iAdsBackgroundColor = '#FDFEFD';
    // How long iAds is visible (milliseconds)
    ads.iAdsTimeToShow = 14000;
    // How long iAds is hidden (milliseconds)
    ads.iAdsTimeToHide = 30000;

    // init values
    ads.iAdsVisible = false;
    ads.adMobVisible = false;
    ads.admob = null;
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
    // Minimal values to init
    ads.init = function(obj,objTopPos,objHmax,typeMove) {
        ads.obj2Move = obj;
        ads.topPos = objTopPos;
        ads.objHmax = objHmax;
        ads.typeMove = typeMove;
    };
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
    ads.showiAds = function() {
        var firstRun = true;
        if (parseFloat(Titanium.Platform.version) >= 3.2) {
            Ti.API.info('ads.showiAds - build iads');
            var iads = Ti.UI.iOS.createAdView({
                width: 'auto',
                height: 'auto',
                top: ads.iadsTopInit,
                borderColor: ads.iAdsBorderColor,
                backgroundColor: ads.iAdsBackgroundColor
            });
            iads.addEventListener('load', function(){
                Ti.API.info("ads.showiAds - iads loaded. First Run ? :"+firstRun);
                ads.iAdsVisible = true;
                if (firstRun) {
                    Ti.API.info("ads.showiAds - First iAds run : True");
                    iads.fireEvent(ads.iAdsShowHide);
                    firstRun = false;
                }
                else {
                    Ti.API.info("ads.showiAds - First iAds run ? : False");
                    setTimeout(function() { iads.fireEvent(ads.iAdsShowHide); Ti.API.info("ads.showiAds - iads loaded, firing iAdsShowHide to hide iAds");}, ads.iAdsTimeToShow);
                }
            });
            iads.addEventListener('error', function(e){
                Ti.API.info("ads.showiAds - iads error :"+e.message+ " --- adMobVisible="+ads.adMobVisible);
                if (ads.iAdsVisible &&  ads.typeMove != '') {
                    if (ads.adMobVisible) {
                        if (ads.typeMove === 'top') {
                            ads.obj2Move.animate({top:ads.topPos+50+ads.objMargin,duration:250});
                        } else {
                            ads.obj2Move.animate({height:ads.objHmax-50,duration:250});
                        }
                    }
                    else {
                        if (ads.typeMove === 'top') {
                            ads.obj2Move.animate({top:ads.topPos+ads.objMargin,duration:250});
                        } else {
                            ads.obj2Move.animate({height:ads.objHmax,duration:250});
                        }
                    }
                }
                ads.iAdsVisible = false;
            });
            iads.addEventListener(ads.iAdsShowHide, function(){
                Ti.API.info("ads.showiAds - Receive iAdsShowHide. ads.iAdsVisible :"+ads.iAdsVisible + " -- iads.visible: "+iads.visible+ " -- ads.adMobVisible: "+ads.adMobVisible);
                if (ads.iAdsVisible && iads.visible) {
                    if (ads.adMobVisible === false) {
                        // if admob's not visible, it's better to let iAds on screen. Then retry after 20s
                        setTimeout(function() { iads.fireEvent(ads.iAdsShowHide); Ti.API.info("admob not visible, let iads for 20s more"); }, 20000);
                    }
                    else {
                        if (ads.typeMove != '') {
                            if (ads.typeMove === 'top') {
                                ads.obj2Move.animate({top:ads.topPos+50+ads.objMargin,duration:500});
                            } else {
                                ads.obj2Move.animate({height:ads.objHmax-50,duration:500});
                            }
                        }
                        iads.animate({top:ads.iadsTopInit, duration:500}, function() { iads.hide(); Ti.API.info("ads.showiAds - hide iads"); });
                        // hide iAds for X s
                        setTimeout(function() { iads.fireEvent(ads.iAdsShowHide); Ti.API.info("ads.showiAds - fire iAdsShowHide to show iAds"); }, ads.iAdsTimeToHide);
                    }
                }
                else {
                    if (ads.adMobVisible === false && ads.typeMove != '') {
                        if (ads.typeMove === 'top') {
                            ads.obj2Move.animate({top:ads.topPos+50+ads.objMargin,duration:500});
                        } else {
                            ads.obj2Move.animate({height:ads.objHmax-50,duration:500});
                        }
                    }
                    iads.show();
                    Ti.API.info("ads.showiAds - iads show");
                    iads.animate({top:ads.topPos, duration:500});
                    setTimeout(function() { iads.fireEvent(ads.iAdsShowHide); Ti.API.info("ads.showiAds - fire iAdsShowHide to hide iAds"); }, ads.iAdsTimeToShow);
                }
            });
            Titanium.UI.currentWindow.add(iads);
        }
    };
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
    ads.buildAdmob = function() {
        if (ads.admob === null) {
            Ti.API.info("ads.buildAdmob - try buildAdmob");
            ads.admob = Ti.Admob.createView({
                publisherId: ads.admobPublisherId, // required
                top: ads.admobTopInit,
                left: 0,
                width: 320, // required
                height: 50, // required
                testing: ads.adTesting,
                adBackgroundColor: ads.adBackgroundColor,
                dateOfBirth: ads.adDateOfBirth, //new Date(1985, 10, 1, 12, 1, 1),
                gender: ads.adGender, //'male',
                keywords: ads.adKeywords, //'',
                refreshAd:15.0 //not working with actual ti.admob module (1.1), set refresh time within admob site
            });
            ads.admob.addEventListener('didFailToReceiveAd', function() {
                ads.admob = null;
                if (!ads.iAdsVisible && ads.typeMove != '') {
                    if (ads.typeMove === 'top') {
                        ads.obj2Move.animate({top:ads.topPos+ads.objMargin,duration:250});
                    } else {
                        ads.obj2Move.animate({height:ads.objHmax,duration:250});
                    }
                }
                ads.adMobVisible = false;
                setTimeout(ads.buildAdmob, 10000);
                Ti.API.info("ads.buildAdmob - admob error : didFailToReceiveAd. Retry in 10s. adMobVisible:"+ads.adMobVisible);
            });
            ads.admob.addEventListener('didReceiveAd', function() {
                Ti.API.info("ads.buildAdmob - admob event : didReceiveAd");
                ads.showAdmob();
            });
            Titanium.UI.currentWindow.add(ads.admob);
        }
    };
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
    ads.showAdmob = function() {
        if (ads.adMobVisible === false) {
            try {
                if (!ads.iAdsVisible && ads.typeMove != '') {
                    if (ads.typeMove === 'top') {
                        ads.obj2Move.animate({top:ads.topPos+50+ads.objMargin,duration:500});
                    } else {
                        ads.obj2Move.animate({height:ads.objHmax-50,duration:500});
                    }
                }
                ads.adMobVisible = true;
                ads.admob.animate({top:ads.topPos,duration:500});
            }
            catch (e) {
                ads.adMobVisible = false;
                Ti.API.info("ads.showAdmob - catch admob error showAdmob");
                setTimeout(ads.buildAdmob, 10000);
            }
        }
    };
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
    ads.showAdvert = function() {
        if (Titanium.Network.online) {
            if (ads.useAdmob) { ads.buildAdmob(); }
            if (ads.useIads) { setTimeout(ads.showiAds, ads.iadsStartDelay); }
        }
        else { // No internet connection. Retry in 30sec
            setTimeout(ads.showAdvert, 30000);
        }
    };
// ----------------------------------------------------------------------------

})();

<commit_msg>    + Change 'auto' size for iAds to Ti.UI.SIZE to be ready with Ti sdk 2     ± Only load ti.admob if needed<commit_after>/*
Ads Auto Switcher - ©BSoft&Co 2012
----------------------------------

This script allows you to use both iAds and Admob at the same place within
a ©Titanium iOS project.

It works with ti.admob module (1.2) which can be found here :
https://github.com/appcelerator/titanium_modules/tree/master/admob/mobile/ios

For now, only iphone's screen size is managed, in portrait orientation.

Per default, Admob is always shown, while iAds is shown (on top of admob) and
hidden during some seconds.
The script manages to move or to resize an UI object when ads are visible.

Be carefull with admob when closing a window. The module ti.admob is not
really good to stop a refreshing admob.. :/
-------------------------------------------------------------------------------

history:
--------
v1.24.01
    + Change 'auto' size for iAds to Ti.UI.SIZE to be ready with Ti sdk 2
    ± Only load ti.admob if needed
v1.23
    + Now compatible with ti.admob >= 1.2
    + Add the values 'adDateOfBirth', 'adGender', 'adKeywords' and 'adTesting' for admob
    + Add the possibility to use or not iAds, like useAdmob : ads.useIads
v1.22
    - delete 'adDelay' parameter
v1.21
    + ads.typeMove = '' for fixed ads. Set ads.obj2Move = null
V1.2
    + ads.useAdmob = true/false : to enabled or not admob. If false, only iAds
    is shown
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------

How To use :
------------
Ti.include('ads.js');
ads.admobPublisherId = 'myadmobKey';
ads.iAdsShowHide = "iAdsShowHide.xx";
ads.init(tableViewObj,0,0,'top');
// --> This inits an ad positionned at top = 0. The tableview name tableViewObj
// will be moved down at top = 50 when an ad is visible.
ads.showAdvert();
//setTimeout(ads.showAdvert, 2000);
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
Ads Auto Switcher is licensed under the Apache Public License (Version 2).
Please see the LICENSE file for the full license.

*/

Titanium.Admob = Ti.Admob;
/*
//iads size 320,50 // 480,32 // ipad : 768,66 // 1024,66
// Size for the ads to print
var adSize = [{w:320,h:50},{w:300,h:250},{w:468,h:60},{w:728,h:90}];

*/
var ads = {};
(function() {
    // The object to be moved/resized when an ad is visible
    ads.obj2Move = {};
    // Top position (px) of the object when no ads on screen (typeMove = 'top')
    // || Top ads position (typeMove = 'height')
    ads.topPos = 0;
    // Max height (px) for the object when no ads (don't mind when typeMove = 'top'
    ads.objHmax = 460;
    // Top margin (px) for the object to move (optional)
    ads.objMargin = 0;
    // Type of move for the object (top/height/'').
    // - top : To move the top position of the object
    // - height : To resize the height of the object.
    //     /|\ : If the position of the object to resized is defined with TOP, the object will be resized from its bottom.
    //         Its TOP position stay fixed!
    //         So to resized the object from the top, with a fxed bottom position, it should be defined with bottom position.
    // - '' : To have a fixed ads position. (If so, also set obj2Move = null)
    ads.typeMove = 'top';
    // To enabled or not admob. Don't forget to change ads.iadsStartDelay if no admob
    ads.useAdmob = true;
    // To enabled or not iAds.
    ads.useIads = true;
    // Milliseconds before starting iads
    ads.iadsStartDelay = 30000;
    // Initial top position for the iads banner
    ads.iadsTopInit = -50;
    // Initial top position for the admob banner
    ads.admobTopInit = -50;
    // The event's name to be fired. It's recommanded to use one per ads's instance.
    ads.iAdsShowHide = 'iAdsShowHide';
    // Required : your admob id
    ads.admobPublisherId = null;
    // Date of birth, to better target the ads, new Date(1985, 10, 1, 12, 1, 1),
    ads.adDateOfBirth = '';
    // Gender 'male' or 'female'
    ads.adGender  = '';
    // Keywords about the ads to print
    ads.adKeywords = '';
    // Test mode for admob
    ads.adTesting = false;
    // admob's backgroundColor
    ads.adBackgroundColor =  "#FDFEFD";
    // iAds's border color
    ads.iAdsBorderColor = '#FDFEFD';
    // iAds's backgroundColor
    ads.iAdsBackgroundColor = '#FDFEFD';
    // How long iAds is visible (milliseconds)
    ads.iAdsTimeToShow = 14000;
    // How long iAds is hidden (milliseconds)
    ads.iAdsTimeToHide = 30000;

    // init values
    ads.iAdsVisible = false;
    ads.adMobVisible = false;
    ads.admob = null;
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
    // Minimal values to init
    ads.init = function(obj,objTopPos,objHmax,typeMove) {
        ads.obj2Move = obj;
        ads.topPos = objTopPos;
        ads.objHmax = objHmax;
        ads.typeMove = typeMove;
    };
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
    ads.showiAds = function() {
        var firstRun = true;
        if (parseFloat(Titanium.Platform.version) >= 3.2) {
            Ti.API.info('ads.showiAds - build iads');
            var iads = Ti.UI.iOS.createAdView({
                width: Ti.UI.SIZE || 'auto',
                height: Ti.UI.SIZE || 'auto',
                top: ads.iadsTopInit,
                borderColor: ads.iAdsBorderColor,
                backgroundColor: ads.iAdsBackgroundColor
            });
            iads.addEventListener('load', function(){
                Ti.API.info("ads.showiAds - iads loaded. First Run ? :"+firstRun);
                ads.iAdsVisible = true;
                if (firstRun) {
                    Ti.API.info("ads.showiAds - First iAds run : True");
                    iads.fireEvent(ads.iAdsShowHide);
                    firstRun = false;
                }
                else {
                    Ti.API.info("ads.showiAds - First iAds run ? : False");
                    setTimeout(function() { iads.fireEvent(ads.iAdsShowHide); Ti.API.info("ads.showiAds - iads loaded, firing iAdsShowHide to hide iAds");}, ads.iAdsTimeToShow);
                }
            });
            iads.addEventListener('error', function(e){
                Ti.API.info("ads.showiAds - iads error :"+e.message+ " --- adMobVisible="+ads.adMobVisible);
                if (ads.iAdsVisible &&  ads.typeMove != '') {
                    if (ads.adMobVisible) {
                        if (ads.typeMove === 'top') {
                            ads.obj2Move.animate({top:ads.topPos+50+ads.objMargin,duration:250});
                        } else {
                            ads.obj2Move.animate({height:ads.objHmax-50,duration:250});
                        }
                    }
                    else {
                        if (ads.typeMove === 'top') {
                            ads.obj2Move.animate({top:ads.topPos+ads.objMargin,duration:250});
                        } else {
                            ads.obj2Move.animate({height:ads.objHmax,duration:250});
                        }
                    }
                }
                ads.iAdsVisible = false;
            });
            iads.addEventListener(ads.iAdsShowHide, function(){
                Ti.API.info("ads.showiAds - Receive iAdsShowHide. ads.iAdsVisible :"+ads.iAdsVisible + " -- iads.visible: "+iads.visible+ " -- ads.adMobVisible: "+ads.adMobVisible);
                if (ads.iAdsVisible && iads.visible) {
                    if (ads.adMobVisible === false) {
                        // if admob's not visible, it's better to let iAds on screen. Then retry after 20s
                        setTimeout(function() { iads.fireEvent(ads.iAdsShowHide); Ti.API.info("admob not visible, let iads for 20s more"); }, 20000);
                    }
                    else {
                        if (ads.typeMove != '') {
                            if (ads.typeMove === 'top') {
                                ads.obj2Move.animate({top:ads.topPos+50+ads.objMargin,duration:500});
                            } else {
                                ads.obj2Move.animate({height:ads.objHmax-50,duration:500});
                            }
                        }
                        iads.animate({top:ads.iadsTopInit, duration:500}, function() { iads.hide(); Ti.API.info("ads.showiAds - hide iads"); });
                        // hide iAds for X s
                        setTimeout(function() { iads.fireEvent(ads.iAdsShowHide); Ti.API.info("ads.showiAds - fire iAdsShowHide to show iAds"); }, ads.iAdsTimeToHide);
                    }
                }
                else {
                    if (ads.adMobVisible === false && ads.typeMove != '') {
                        if (ads.typeMove === 'top') {
                            ads.obj2Move.animate({top:ads.topPos+50+ads.objMargin,duration:500});
                        } else {
                            ads.obj2Move.animate({height:ads.objHmax-50,duration:500});
                        }
                    }
                    iads.show();
                    Ti.API.info("ads.showiAds - iads show");
                    iads.animate({top:ads.topPos, duration:500});
                    setTimeout(function() { iads.fireEvent(ads.iAdsShowHide); Ti.API.info("ads.showiAds - fire iAdsShowHide to hide iAds"); }, ads.iAdsTimeToShow);
                }
            });
            Titanium.UI.currentWindow.add(iads);
        }
    };
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
    ads.buildAdmob = function() {
        if (ads.admob === null) {
            Ti.API.info("ads.buildAdmob - try buildAdmob");
            ads.admob = Ti.Admob.createView({
                publisherId: ads.admobPublisherId, // required
                top: ads.admobTopInit,
                left: 0,
                width: 320, // required
                height: 50, // required
                testing: ads.adTesting,
                adBackgroundColor: ads.adBackgroundColor,
                dateOfBirth: ads.adDateOfBirth, //new Date(1985, 10, 1, 12, 1, 1),
                gender: ads.adGender, //'male',
                keywords: ads.adKeywords, //'',
                refreshAd:15.0 //not working with actual ti.admob module (1.1), set refresh time within admob site
            });
            ads.admob.addEventListener('didFailToReceiveAd', function() {
                ads.admob = null;
                if (!ads.iAdsVisible && ads.typeMove != '') {
                    if (ads.typeMove === 'top') {
                        ads.obj2Move.animate({top:ads.topPos+ads.objMargin,duration:250});
                    } else {
                        ads.obj2Move.animate({height:ads.objHmax,duration:250});
                    }
                }
                ads.adMobVisible = false;
                setTimeout(ads.buildAdmob, 10000);
                Ti.API.info("ads.buildAdmob - admob error : didFailToReceiveAd. Retry in 10s. adMobVisible:"+ads.adMobVisible);
            });
            ads.admob.addEventListener('didReceiveAd', function() {
                Ti.API.info("ads.buildAdmob - admob event : didReceiveAd");
                ads.showAdmob();
            });
            Titanium.UI.currentWindow.add(ads.admob);
        }
    };
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
    ads.showAdmob = function() {
        if (ads.adMobVisible === false) {
            try {
                if (!ads.iAdsVisible && ads.typeMove != '') {
                    if (ads.typeMove === 'top') {
                        ads.obj2Move.animate({top:ads.topPos+50+ads.objMargin,duration:500});
                    } else {
                        ads.obj2Move.animate({height:ads.objHmax-50,duration:500});
                    }
                }
                ads.adMobVisible = true;
                ads.admob.animate({top:ads.topPos,duration:500});
            }
            catch (e) {
                ads.adMobVisible = false;
                Ti.API.info("ads.showAdmob - catch admob error showAdmob");
                setTimeout(ads.buildAdmob, 10000);
            }
        }
    };
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
    ads.showAdvert = function() {
        if (Titanium.Network.online) {
            if (ads.useAdmob) {
                Ti.Admob = require('ti.admob');
                ads.buildAdmob();
            }
            if (ads.useIads) { setTimeout(ads.showiAds, ads.iadsStartDelay); }
        }
        else { // No internet connection. Retry in 30sec
            setTimeout(ads.showAdvert, 30000);
        }
    };
// ----------------------------------------------------------------------------

})();

<|endoftext|>


<commit_before>// adt.js : Algebraic data types and immutable structures in Javascript
// Nathan Faubion <nathan@n-son.com>

;(function (window, module) {
  // adt namespace
  var adt = {};

  // Export
  window.adt = module.exports = adt;

  // Utility functions
  adt.util = {};

  // Cache some prototype methods for easy use.
  var slice = Array.prototype.slice;
  var funcApply = Function.prototype.apply;

  // Return an array of the supplied argument. Used for `arguments` objects.
  adt.util.toArray = function (a) {
    return slice.call(a);
  };

  // A basic extend method for copying attributes of on object into another.
  adt.util.extend = function (dest /*, sources... */) {
    var args = slice.call(arguments, 1);
    for (var i = 0, len = args.length; i < len; i++) {
      for (var k in args[i]) {
        dest[k] = args[i][k];
      }
    }
    return dest;
  };

  // Given a constructor and an array of args, instanciates a new objects with
  // said args. Like calling `apply` on a constructor.
  adt.util.ctrApply = function (ctr, args) {
    var C = function () {};
    C.prototype = ctr.prototype;
    var inst = new C();
    var ret = ctr.apply(inst, args);
    return Object(ret) === ret ? ret : inst;
  };

  // Partially applies arguments to a function in the same way `bind` would,
  // but doesn't cement the context in anyway.
  adt.util.partial = function (func) {
    var args = slice.call(arguments, 1);
    return function () {
      return func.apply(this, args.concat(adt.util.toArray(arguments)));
    };
  };

  // Automatically curries a function so that it can be called with too few
  // arguments. Calling it with too few arguments will return a function that
  // takes however many arguments are left.
  // Adapted from http://fitzgen.github.com/wu.js/
  adt.util.curry = function (func, len) {
    if (len === undefined) len = func.length;
    return function () {
      var args = adt.util.toArray(arguments);
      if (args.length < len) {
        var applied = adt.util.partial.apply(this, [func].concat(args));
        return len - args.length > 0
          ? adt.util.curry(applied, len - args.length)
          : applied;
      } else {
        return func.apply(this, args);
      }
    };
  };

  // A shared `toString` method that will work with any adt type that has a
  // className set on the constructor.
  adt.util.toString = function () {
    var ctr = this.constructor;
    var name = ctr.className || "Anonymous";
    var vals = ctr.unapply(this);
    return name + (vals.length ? "(" + vals.join(", ") + ")" : "");
  };

  // Base class from which all adt.js classes inherit.
  // All adt.js classes should have a clone method that clones the
  // object as best it can.
  adt.__Base__ = function () {};

  // Generates a new set of Algebraic data types. This will create a lot of
  // boilerplate to help with type checking. Each type will get is<type>()
  // methods that return true or false.
  adt.data = function () {
    var targ0 = typeof arguments[0];
    var callback, types, names;

    // adt.data(typesObj)
    if (targ0 === "object") {
      types = arguments[0];

      // Call adt.data again with a desugared callback.
      return adt.data(function (d, type) {
        for (var name in types) type(name, types[name]);
      });
    }

    // adt.data(callback)
    callback = arguments[0] || function () {};
    names = [];

    // Create a new parent class.
    // This class should never be created using `new`. You obviously can,
    // but it won't be of much use. You can however override the apply method
    // to create default instances.
    var D = function () {
      if (!(this instanceof D) && D.apply !== funcApply) {
        return D.apply(this, arguments);
      }
    };

    // Make it an instance of the adt Base class.
    D.prototype = new adt.__Base__();

    // Declares an adt type as part of the family.
    D.type = function (name, ctr) {
      if (typeof name !== "string") {
        ctr = name;
        name = uniqueId("Anonymous");
      }
      // Create a new adt constructor if not provided with one
      if (!ctr) ctr = adt.single();
      else if (!(ctr.prototype instanceof adt.__Base__)) ctr = adt.record(ctr);

      // Reset the prototype so its a subclass of D.
      var proto = ctr.prototype;
      ctr.prototype = new D();
      ctr.prototype.constructor = ctr;
      ctr.prototype.toString = adt.util.toString;
      ctr.className = name;

      // Add a typechecking function to D for this type.
      D.prototype["is" + name] = function () {
        return this instanceof ctr;
      };

      // Extend the contructor's prototype with its old prototype since we
      // overwrote it making it a subclass of D.
      adt.util.extend(ctr.prototype, proto);

      // Export constructor as a static property on the parent class.
      D[name] = ctr;
      names.push(name);
      return ctr;
    };

    // Call the callback with the constructor as the context.
    var types = callback.call(D, D, D.type);

    // If an object was returned in the callback, assume it's a mapping of
    // more types to add.
    if (typeof types === 'object') {
      for (var name in types) D.type(name, types[name]);
    }

    // Keep the type function around because it allows for nice type
    // declarations, but give the option to seel it. This will call `seel`
    // on any sub types to.
    D.seel = function () {
      var i = 0, len = names.length, seel;
      for (; i < len; i++) {
        seel = this[name].seel;
        seel instanceof Function && seel();
      }
      delete D.type;
      delete D.seel;
      return D;
    };

    // Export names as a meta object
    D.__names__ = names;
    return D;
  };

  // Create a single empty class that always return the same instance.
  // Useful for sentinal values such as Nothing, Empty, etc.
  adt.single = function () {
    var inst, ctr;

    ctr = function () {
      if (!(this instanceof ctr)) return new ctr();
      if (inst) return inst;
      inst = this;
    };

    ctr.prototype = new adt.__Base__();
    ctr.prototype.constructor = ctr;

    ctr.prototype.clone = function () { return inst; };
    ctr.prototype.equals = function (obj) { return this === obj; };

    ctr.create = function () { return ctr(); };
    ctr.unapply = function () { return []; };
    ctr.unapplyObj = function () { return {}; };

    return ctr;
  };

  // Create a new class that has named fields. Each value can be obtained by
  // calling the name as a method. Each value can be changed by calling `set`
  // with an object of values to update. `set` returns a clone of the object.
  adt.record = function () {
    var targ0 = typeof arguments[0];
    var names, constraints, ctr, fields, callback;

    // adt.record(fieldNames...)
    if (targ0 === 'string') {
      var args = adt.util.toArray(arguments);
      var i = 0, len = args.length;
      fields = {};
      for (; i < len; i++) fields[args[i]] = null;
      return adt.record(fields);
    }

    // adt.record(fieldsObj)
    else if (targ0 === 'object') {
      fields = arguments[0];
      return adt.record(function (r, field) {
        for (var name in fields) field(name, fields[name]);
      });
    }

    // adt.record(callback)
    callback = arguments[0] || function () {};
    names = [];
    constraints = [];

    // A record's constructor can be called without `new` and will also throw
    // an error if called with too many arguments. This constructor duplicates
    // some of the util.curry function so it can initially match on the length
    // of names (which we don't know yet, but will exist later on).
    ctr = function () {
      var args = adt.util.toArray(arguments);
      var len = names.length;
      if (args.length < len) {
        var applied = adt.util.partial.apply(this, [ctr].concat(args));
        return len - args.length > 0
          ? adt.util.curry(applied, len - args.length)
          : applied;
      } else {
        if (!(this instanceof ctr)) return adt.util.ctrApply(ctr, args);
        if (args.length > names.length) throw new Error("Too many arguments");
        for (var i = 0, len = args.length; i < len; i++) {
          this['_' + names[i]] = constraints[names[i]](args[i]);
        }
      }
    };

    // Inherit from Base in case someone wants to create a record outside of
    // a call to adt.data.
    ctr.prototype = new adt.__Base__();
    ctr.prototype.constructor = ctr;

    // This clone method will clone any values that are also adt types and
    // leaves anything else alone.
    ctr.prototype.clone = function () {
      var args = [];
      var i = 0, len = names.length, val;
      for (; i < len; i++) {
        val = this[names[i]]();
        args.push(n instanceof adt.__Base__ ? val.clone : val);
      }
      return ctr.apply(null, args);
    };

    // Lookup a field's value by either name or index. Looking up by name
    // requires that we use indexOf to verify that the field exists. Normally
    // in js, it would just return undefined, but undefined is a valid value
    // with these types.
    ctr.prototype.slot = function (field) {
      if (typeof field === "number") {
        if (field < 0 || field > names.length - 1) {
          throw new Error("Field index out of range");
        }
        field = names[field];
      } else {
        if (names.indexOf(field) === -1) {
          throw new Error("Field name does not exist");
        }
      }
      return this[field]();
    };

    // Returns a new instance.
    ctr.prototype.set = function (vals) {
      var args = []
      var i = 0, len = names.length, val, n;
      for (; i < len; i++) {
        n = names[i];
        val = n in vals ? vals[n] : this[n]();
        args.push(constraints[n](val));
      }
      return ctr.apply(null, args);
    };

    // Performs deep equality checks on each field as long as it holds an
    // adt type. Any other types will just be compared using ===.
    ctr.prototype.equals = function (that) {
      if (this === that) return true;
      if (that instanceof ctr) {
        var i = 0, len = names.length;
        var vala, valb;
        for (; i < len; i++) {
          vala = this[names[i]]();
          valb = that[names[i]]();
          if (vala instanceof adt.__Base__) {
            if (!vala.equals(valb)) return false;
          } else if (vala !== valb) return false;
        }
      }
      return true;
    };

    // Creates a new instance using key-value pairs instead of by
    // positional arguments.
    ctr.create = function (vals) {
      var args = [];
      var i = 0, len = names.length, n;
      for (; i < len; i++) {
        n = names[i];
        if (!(n in vals)) throw new Error("Too few arguments");
        args.push(constraints[n](vals[n]));
      }
      return ctr.apply(null, args);
    };

    // Returns an array representation of the fields
    ctr.unapply = function (inst) {
      var vals = [];
      var i = 0, len = names.length;
      for (; i < len; i++) vals.push(inst[names[i]]());
      return vals;
    };

    // Returns an object representation of the field
    ctr.unapplyObj = function (inst) {
      var vals = {};
      var i = 0, len = names.length;
      for (; i < len; i++) vals[names[i]] = inst[names[i]]();
      return vals;
    };

    // Declares a field as part of the type.
    ctr.field = function (name, constraint) {
      if (!constraint) constraint = adt.any;
      if (typeof constraint !== 'function') {
        throw new TypeError('Constraints must be functions')
      }
      names.push(name);
      constraints[name] = constraint;
      return ctr;
    };

    // Call the callback with the contructor as the context.
    fields = callback.call(ctr, ctr, ctr.field);

    // If an object was returned in the callback, assume it's a mapping of
    // more fields to add.
    if (typeof fields === 'object') {
      for (var name in fields) ctr.field(name, fields[name]);
    }

    // Generate boilerplate getters
    var i = 0, len = names.length;
    for (; i < len; i++) {
      (function (n) {
        ctr.prototype[n] = function () { return this['_' + n] };
      })(names[i]);
    }

    // Export names and constraints as meta attributes.
    ctr.__names__ = names;
    ctr.__constraints__ = constraints;

    // Keep the field function around because it allows for nice type
    // declarations, but give the option to seel it.
    ctr.seel = function () {
      delete ctr.field;
      delete ctr.seel;
      return ctr;
    };

    return ctr;
  };

  // A contraint function that will accept any value.
  adt.any = function (x) { return x; };

  // A constraint generator that will perform instanceof checks on the value
  // to make sure it is of the correct type. If a value besides a function
  // is passed in, it will perform equality checks on it.
  //
  // It has special handling for Number, String, and Boolean. Literals won't
  // match an instanceof check, but will match with typeof.
  adt.only = function () {
    var types = adt.util.toArray(arguments);
    return function (x) {
      var i = 0, len = types.length, type;
      for (; i < len; i++) {
        type = types[i];
        if (type instanceof Function) {
          if (x instanceof type) return x;
          if (type === Number && typeof x === "number") return x;
          if (type === String && typeof x === "string") return x;
          if (type === Boolean && typeof x === "boolean") return x;
        } else {
          if (type instanceof adt.__Base__ && type.equals(x)) return x;
          if (type === x) return x;
        }
      }
      throw new TypeError('Unexpected type');
    };
  };

  // Helper function to return a unique id
  var uniqueId = (function () {
    var id = 0;
    return function (pre) { return pre + (id++); };
  })();

})(
  typeof window !== "undefined" ? window : {},
  typeof module !== "undefined" ? module : {}
);
<commit_msg>Missing parens in clone<commit_after>// adt.js : Algebraic data types and immutable structures in Javascript
// Nathan Faubion <nathan@n-son.com>

;(function (window, module) {
  // adt namespace
  var adt = {};

  // Export
  window.adt = module.exports = adt;

  // Utility functions
  adt.util = {};

  // Cache some prototype methods for easy use.
  var slice = Array.prototype.slice;
  var funcApply = Function.prototype.apply;

  // Return an array of the supplied argument. Used for `arguments` objects.
  adt.util.toArray = function (a) {
    return slice.call(a);
  };

  // A basic extend method for copying attributes of on object into another.
  adt.util.extend = function (dest /*, sources... */) {
    var args = slice.call(arguments, 1);
    for (var i = 0, len = args.length; i < len; i++) {
      for (var k in args[i]) {
        dest[k] = args[i][k];
      }
    }
    return dest;
  };

  // Given a constructor and an array of args, instanciates a new objects with
  // said args. Like calling `apply` on a constructor.
  adt.util.ctrApply = function (ctr, args) {
    var C = function () {};
    C.prototype = ctr.prototype;
    var inst = new C();
    var ret = ctr.apply(inst, args);
    return Object(ret) === ret ? ret : inst;
  };

  // Partially applies arguments to a function in the same way `bind` would,
  // but doesn't cement the context in anyway.
  adt.util.partial = function (func) {
    var args = slice.call(arguments, 1);
    return function () {
      return func.apply(this, args.concat(adt.util.toArray(arguments)));
    };
  };

  // Automatically curries a function so that it can be called with too few
  // arguments. Calling it with too few arguments will return a function that
  // takes however many arguments are left.
  // Adapted from http://fitzgen.github.com/wu.js/
  adt.util.curry = function (func, len) {
    if (len === undefined) len = func.length;
    return function () {
      var args = adt.util.toArray(arguments);
      if (args.length < len) {
        var applied = adt.util.partial.apply(this, [func].concat(args));
        return len - args.length > 0
          ? adt.util.curry(applied, len - args.length)
          : applied;
      } else {
        return func.apply(this, args);
      }
    };
  };

  // A shared `toString` method that will work with any adt type that has a
  // className set on the constructor.
  adt.util.toString = function () {
    var ctr = this.constructor;
    var name = ctr.className || "Anonymous";
    var vals = ctr.unapply(this);
    return name + (vals.length ? "(" + vals.join(", ") + ")" : "");
  };

  // Base class from which all adt.js classes inherit.
  // All adt.js classes should have a clone method that clones the
  // object as best it can.
  adt.__Base__ = function () {};

  // Generates a new set of Algebraic data types. This will create a lot of
  // boilerplate to help with type checking. Each type will get is<type>()
  // methods that return true or false.
  adt.data = function () {
    var targ0 = typeof arguments[0];
    var callback, types, names;

    // adt.data(typesObj)
    if (targ0 === "object") {
      types = arguments[0];

      // Call adt.data again with a desugared callback.
      return adt.data(function (d, type) {
        for (var name in types) type(name, types[name]);
      });
    }

    // adt.data(callback)
    callback = arguments[0] || function () {};
    names = [];

    // Create a new parent class.
    // This class should never be created using `new`. You obviously can,
    // but it won't be of much use. You can however override the apply method
    // to create default instances.
    var D = function () {
      if (!(this instanceof D) && D.apply !== funcApply) {
        return D.apply(this, arguments);
      }
    };

    // Make it an instance of the adt Base class.
    D.prototype = new adt.__Base__();

    // Declares an adt type as part of the family.
    D.type = function (name, ctr) {
      if (typeof name !== "string") {
        ctr = name;
        name = uniqueId("Anonymous");
      }
      // Create a new adt constructor if not provided with one
      if (!ctr) ctr = adt.single();
      else if (!(ctr.prototype instanceof adt.__Base__)) ctr = adt.record(ctr);

      // Reset the prototype so its a subclass of D.
      var proto = ctr.prototype;
      ctr.prototype = new D();
      ctr.prototype.constructor = ctr;
      ctr.prototype.toString = adt.util.toString;
      ctr.className = name;

      // Add a typechecking function to D for this type.
      D.prototype["is" + name] = function () {
        return this instanceof ctr;
      };

      // Extend the contructor's prototype with its old prototype since we
      // overwrote it making it a subclass of D.
      adt.util.extend(ctr.prototype, proto);

      // Export constructor as a static property on the parent class.
      D[name] = ctr;
      names.push(name);
      return ctr;
    };

    // Call the callback with the constructor as the context.
    var types = callback.call(D, D, D.type);

    // If an object was returned in the callback, assume it's a mapping of
    // more types to add.
    if (typeof types === 'object') {
      for (var name in types) D.type(name, types[name]);
    }

    // Keep the type function around because it allows for nice type
    // declarations, but give the option to seel it. This will call `seel`
    // on any sub types to.
    D.seel = function () {
      var i = 0, len = names.length, seel;
      for (; i < len; i++) {
        seel = this[name].seel;
        seel instanceof Function && seel();
      }
      delete D.type;
      delete D.seel;
      return D;
    };

    // Export names as a meta object
    D.__names__ = names;
    return D;
  };

  // Create a single empty class that always return the same instance.
  // Useful for sentinal values such as Nothing, Empty, etc.
  adt.single = function () {
    var inst, ctr;

    ctr = function () {
      if (!(this instanceof ctr)) return new ctr();
      if (inst) return inst;
      inst = this;
    };

    ctr.prototype = new adt.__Base__();
    ctr.prototype.constructor = ctr;

    ctr.prototype.clone = function () { return inst; };
    ctr.prototype.equals = function (obj) { return this === obj; };

    ctr.create = function () { return ctr(); };
    ctr.unapply = function () { return []; };
    ctr.unapplyObj = function () { return {}; };

    return ctr;
  };

  // Create a new class that has named fields. Each value can be obtained by
  // calling the name as a method. Each value can be changed by calling `set`
  // with an object of values to update. `set` returns a clone of the object.
  adt.record = function () {
    var targ0 = typeof arguments[0];
    var names, constraints, ctr, fields, callback;

    // adt.record(fieldNames...)
    if (targ0 === 'string') {
      var args = adt.util.toArray(arguments);
      var i = 0, len = args.length;
      fields = {};
      for (; i < len; i++) fields[args[i]] = null;
      return adt.record(fields);
    }

    // adt.record(fieldsObj)
    else if (targ0 === 'object') {
      fields = arguments[0];
      return adt.record(function (r, field) {
        for (var name in fields) field(name, fields[name]);
      });
    }

    // adt.record(callback)
    callback = arguments[0] || function () {};
    names = [];
    constraints = [];

    // A record's constructor can be called without `new` and will also throw
    // an error if called with too many arguments. This constructor duplicates
    // some of the util.curry function so it can initially match on the length
    // of names (which we don't know yet, but will exist later on).
    ctr = function () {
      var args = adt.util.toArray(arguments);
      var len = names.length;
      if (args.length < len) {
        var applied = adt.util.partial.apply(this, [ctr].concat(args));
        return len - args.length > 0
          ? adt.util.curry(applied, len - args.length)
          : applied;
      } else {
        if (!(this instanceof ctr)) return adt.util.ctrApply(ctr, args);
        if (args.length > names.length) throw new Error("Too many arguments");
        for (var i = 0, len = args.length; i < len; i++) {
          this['_' + names[i]] = constraints[names[i]](args[i]);
        }
      }
    };

    // Inherit from Base in case someone wants to create a record outside of
    // a call to adt.data.
    ctr.prototype = new adt.__Base__();
    ctr.prototype.constructor = ctr;

    // This clone method will clone any values that are also adt types and
    // leaves anything else alone.
    ctr.prototype.clone = function () {
      var args = [];
      var i = 0, len = names.length, val;
      for (; i < len; i++) {
        val = this[names[i]]();
        args.push(n instanceof adt.__Base__ ? val.clone() : val);
      }
      return ctr.apply(null, args);
    };

    // Lookup a field's value by either name or index. Looking up by name
    // requires that we use indexOf to verify that the field exists. Normally
    // in js, it would just return undefined, but undefined is a valid value
    // with these types.
    ctr.prototype.slot = function (field) {
      if (typeof field === "number") {
        if (field < 0 || field > names.length - 1) {
          throw new Error("Field index out of range");
        }
        field = names[field];
      } else {
        if (names.indexOf(field) === -1) {
          throw new Error("Field name does not exist");
        }
      }
      return this[field]();
    };

    // Returns a new instance.
    ctr.prototype.set = function (vals) {
      var args = []
      var i = 0, len = names.length, val, n;
      for (; i < len; i++) {
        n = names[i];
        val = n in vals ? vals[n] : this[n]();
        args.push(constraints[n](val));
      }
      return ctr.apply(null, args);
    };

    // Performs deep equality checks on each field as long as it holds an
    // adt type. Any other types will just be compared using ===.
    ctr.prototype.equals = function (that) {
      if (this === that) return true;
      if (that instanceof ctr) {
        var i = 0, len = names.length;
        var vala, valb;
        for (; i < len; i++) {
          vala = this[names[i]]();
          valb = that[names[i]]();
          if (vala instanceof adt.__Base__) {
            if (!vala.equals(valb)) return false;
          } else if (vala !== valb) return false;
        }
      }
      return true;
    };

    // Creates a new instance using key-value pairs instead of by
    // positional arguments.
    ctr.create = function (vals) {
      var args = [];
      var i = 0, len = names.length, n;
      for (; i < len; i++) {
        n = names[i];
        if (!(n in vals)) throw new Error("Too few arguments");
        args.push(constraints[n](vals[n]));
      }
      return ctr.apply(null, args);
    };

    // Returns an array representation of the fields
    ctr.unapply = function (inst) {
      var vals = [];
      var i = 0, len = names.length;
      for (; i < len; i++) vals.push(inst[names[i]]());
      return vals;
    };

    // Returns an object representation of the field
    ctr.unapplyObj = function (inst) {
      var vals = {};
      var i = 0, len = names.length;
      for (; i < len; i++) vals[names[i]] = inst[names[i]]();
      return vals;
    };

    // Declares a field as part of the type.
    ctr.field = function (name, constraint) {
      if (!constraint) constraint = adt.any;
      if (typeof constraint !== 'function') {
        throw new TypeError('Constraints must be functions')
      }
      names.push(name);
      constraints[name] = constraint;
      return ctr;
    };

    // Call the callback with the contructor as the context.
    fields = callback.call(ctr, ctr, ctr.field);

    // If an object was returned in the callback, assume it's a mapping of
    // more fields to add.
    if (typeof fields === 'object') {
      for (var name in fields) ctr.field(name, fields[name]);
    }

    // Generate boilerplate getters
    var i = 0, len = names.length;
    for (; i < len; i++) {
      (function (n) {
        ctr.prototype[n] = function () { return this['_' + n] };
      })(names[i]);
    }

    // Export names and constraints as meta attributes.
    ctr.__names__ = names;
    ctr.__constraints__ = constraints;

    // Keep the field function around because it allows for nice type
    // declarations, but give the option to seel it.
    ctr.seel = function () {
      delete ctr.field;
      delete ctr.seel;
      return ctr;
    };

    return ctr;
  };

  // A contraint function that will accept any value.
  adt.any = function (x) { return x; };

  // A constraint generator that will perform instanceof checks on the value
  // to make sure it is of the correct type. If a value besides a function
  // is passed in, it will perform equality checks on it.
  //
  // It has special handling for Number, String, and Boolean. Literals won't
  // match an instanceof check, but will match with typeof.
  adt.only = function () {
    var types = adt.util.toArray(arguments);
    return function (x) {
      var i = 0, len = types.length, type;
      for (; i < len; i++) {
        type = types[i];
        if (type instanceof Function) {
          if (x instanceof type) return x;
          if (type === Number && typeof x === "number") return x;
          if (type === String && typeof x === "string") return x;
          if (type === Boolean && typeof x === "boolean") return x;
        } else {
          if (type instanceof adt.__Base__ && type.equals(x)) return x;
          if (type === x) return x;
        }
      }
      throw new TypeError('Unexpected type');
    };
  };

  // Helper function to return a unique id
  var uniqueId = (function () {
    var id = 0;
    return function (pre) { return pre + (id++); };
  })();

})(
  typeof window !== "undefined" ? window : {},
  typeof module !== "undefined" ? module : {}
);
<|endoftext|>


<commit_before>/**************************************************************************
 * Copyright (c) 2012 Adtec Productions, Inc.
 *
 * 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
_URL = 'build.phonegap.com',
_fs = require('fs'),
_https = require('https'),
_mime = require('mime'),
_req = require('request'),
_URL = 'build.phonegap.com'

/******************************************************************
 * Executes the GET Phonegap API call,
 * and writes the output in a JSON-formatted file
 ******************************************************************/
getApiData = function(token, apiCall, callback){
   var
   options = null;

   options = {
      host: _URL,
      path: '/api/v1/' + apiCall + '?auth_token='+token
      };

   _https.get(options, function(res){
      var replyData = '';
      res.on('data', function(data){
         replyData+= data;
         });
      res.on('end', function(){
         if(callback instanceof Function){
            callback(JSON.parse(replyData));
            }
         else if (callback.success instanceof Function){
            callback.success(JSON.parse(replyData));
            }
         });
      }).on('error', function(e){
         console.log("AJAX Err. Message: " + e.message);
         if(callback.error && (callback.error instanceof Function)){callback.error(e.message);}
      });
   },

/******************************************************************
 * Downloads the file at the given URL and saves it in the provided
 * path.
 ******************************************************************/
downloadFile = function(url, outputFilepath, callback){
   var successCallback = (callback instanceof Function)? callback: (callback.success instanceof Function)? callback.success: function(){};
   var errCallback = (callback.error instanceof Function)? callback.error: function(){};

   _req.get(url).pipe(_fs.createWriteStream(outputFilepath))
      .on('error', function(e){errCallback(e.message);})
      .on('close', function(){successCallback(outputFilepath)});
},

/******************************************************************
 *  Get a JSON-encoded representation of the authenticated user,
 *  as well as a listing of associated resources.
 *
 *  This should be the starting point for applications traversing
 *  the PhoneGap Build API. It is aliased to
 *  https://build.phonegap.com/api/v1.
 *
 *  GET https://build.phonegap.com/api/v1/me
 *****************************************************************/
_getUserData = function(token, callback){
   getApiData(token, 'me', callback);
},

/******************************************************************
 *  Get a JSON-encoded representation of the authenticated user's
 *  apps.
 *
 *  API clients can follow the link attribute for each app to get
 *  further details, including the associated signing keys and
 *  collaborators.
 *
 *  GET https://build.phonegap.com/api/v1/apps
 *****************************************************************/
_getAppsData = function(token, callback){
   getApiData(token, 'apps', callback);
   },

/******************************************************************
 *  Get a JSON-encoded representation of a particular app, if the
 *  authenticated user has permission to access it.
 *
 *  In addition to the fields provided in the list of all apps,
 *  this detail view includes:
 *
 *  keys: all of the keys that the app is currently being built with.
 *    This will include the owner's default key for a platform,
 *    if selected
 *
 *  collaborators: each person who has access to this app, along
 *    with their role, if the authenticated user is the owner of
 *    the app. Collaborators who are registered with PhoneGap Build
 *    are listed under active; collaborators you have invited who
 *    have not yet created an account are listed as pending.
 *
 *  GET https://build.phonegap.com/api/v1/apps/:id
 *****************************************************************/
_getAppDataById = function(token, appId, callback){
   getApiData(token, 'apps/' + appId, callback);
   },

/******************************************************************
 *  Get a JSON-encoded list of all the signing keys associated with
 *  your account.
 *
 *  This returns a short listing of all the associated keys--it's
 *  very similar to the list you'll see when requesting /api/v1/me
 *
 *  GET https://build.phonegap.com/api/v1/keys
 *****************************************************************/
_getKeysData = function(token, callback){
   getApiData(token, 'keys', callback);
   },

/******************************************************************
 *  Get a JSON-encoded list of all the signing keys associated with
 *  your account, for a specific platform. That platform can be one
 *  of ios, android, or blackberry.
 *
 *  GET https://build.phonegap.com/api/v1/keys/:platform
 *****************************************************************/
_getPlatformKeys = function(platform){
   getApiData('keys/' + platform, METADATA_DIR + '/keys_' + platform + '.json');
   },

/******************************************************************
 *  Get a JSON-encoded representation of a single signing key.
 *
 *  GET https://build.phonegap.com/api/v1/keys/:platform/:appId
 *****************************************************************/
_getPlatformKeyById = function(platform, appId){
   getApiData('keys/' + platform + '/' + appId, METADATA_DIR + '/keys_' + platform + '_' + appId + '.json');
   },

/******************************************************************
 *  Download the app package for the given platform; available
 *  platforms are android, blackberry, ios, symbian, webos and
 *  winphone.
 *
 *  In the successful case, this API method will return a 302
 *  redirect to the application binary - the actual body of the
 *  response will point to the resource's correct location:
 *
 *  If using the optional argument for the download location,
 *  please ensure that you are using the right extension for
 *  the platform you are downloading.
 *
 *  apk for Android
 *  ipa for iOS
 *  ipk for webOS
 *  jad for unsigned BlackBerry builds; zip if you've uploaded your BlackBerry signing keys
 *  wgz for Symbian
 *  xap for Windows Phone
 *
 *  GET https://build.phonegap.com/api/v1/apps/:id/:platform
 *****************************************************************/
_downloadApp = function(token, appId, platform, outputFilepath, callback){
   var url= 'https://' + _URL + '/api/v1/apps/' + appId + '/' + platform + '?auth_token='+token;
   console.info("\n\nStarting Download...");
   downloadFile(url, outputFilepath, callback);
   },

/*****************************************************************
 * Get the main icon associated with an app - this is either the
 * biggest icon specified in your config.xml file, or an icon you
 * have uploaded through the API or the PhoneGap Build web interface.
 *
 * GET https://build.phonegap.com/api/v1/apps/:id/:icon
*****************************************************************/
_downloadIcon = function(token, appId, outputFilepath, callback){
   var url= 'https://' + _URL + '/api/v1/apps/' + appId + '/icon';
   downloadFile(url, outputFilepath, callback);
   },

/************************************
 * Init Write API
 */

encodeFieldHeader = function(boundary, name, value){
   var result = [
     "--" + boundary + "\r\n",
     "Content-Disposition: form-data; name=\"",
     "" + name + "\"\r\n\r\n" + value + "\r\n",
     ].join("");
   return result;
},

encodeFileHeader = function(boundary, type, name, filename){
   var result = [
      "--" + boundary + "\r\n",
      "Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"\r\n",
      "Content-Type: " + type + "\r\n\r\n"
     ].join("");
   return result;
},

postMultipart = function(token, postData, boundary, apiCall, callback){
   var
   len = 0, i=0,
   options = null,
   request = null;

   for( i=0; i<postData.length; i++){
      len += postData[i].length;
   }

   options = {
      host: _URL,
      path: '/api/v1/' + apiCall + '?auth_token='+token,
      method: 'POST',
      headers:{
         'Content-Type': 'multipart/form-data; boundary=' + boundary,
         'Content-Length': len
      }
   };

   request = _https.request(options, function(response){
      var replyData = '';
      response.on('data', function(chunk){
         replyData+= chunk;
      });
      response.on('end', function(){
         if(callback instanceof Function){
            callback(replyData);
            }
         else if(callback.success instanceof Function){
            callback.success(replyData);
            }
         return;
      });
      response.on('error', function(e){
         if(callback.error instanceof Function){
            callback.error(e.message);
            }
         return;
      });
   });

   for( i=0; i<postData.length; i++){
      request.write(postData[i]);
   }
   request.end();

},

initMultipartUpload = function(token, inputFile, reqData, apiCall, fieldName, callback){
   var
   boundary = 'bound' + Math.random(),
   postData = [],
   fileReader = null,
   fileContents = '';

   if(reqData){postData.push(new Buffer(encodeFieldHeader(boundary, "data", JSON.stringify(reqData)), 'ascii'))};
   postData.push(new Buffer(encodeFileHeader(boundary, _mime.lookup(inputFile), fieldName, inputFile), 'ascii'));
   fileReader = _fs.createReadStream(inputFile, {encoding: 'binary'});
   fileReader.on('data', function(fileData){ fileContents+= fileData;});
   fileReader.on('end', function(){
      postData.push(new Buffer(fileContents, 'binary'));
      postData.push(new Buffer("\r\n--" + boundary + "--", 'ascii'));
      postMultipart(token, postData, boundary, apiCall, callback);
      });
},

 /******************************************************************
 *
 *  Create a new File-based App.
 *
 *  Required parameters
 *
 *  title: You must specify a title for your app - if a title is also
 *         specified in a config.xml in your package, the one in the
 *         config.xml file will take preference.
 *  create_method: use "file" for this method
 *
 *  Optional parameters
 *
 *  package: Sets the package identifier for your app. This can also be
 *           done after creation, or in your config.xml file.
 *           Defaults to com.phonegap.www
 *  version: Sets the version of your app. This can also be done after
 *           creation, or in your config.xml file. Defaults to 0.0.1
 *  description: Sets the description for your app. This can also be
 *               done after creation, or in your config.xml file.
 *               Defaults to empty.
 *  debug: Builds your app in debug mode. Defaults to false.
 *  keys: Set the signing keys to use for each platform you wish to sign.
 *  private: Whether your app can be publicly downloaded. Defaults to
 *           true during beta period; will default to false once the
 *           beta period is complete
 *  phonegap_version: Which version of PhoneGap your app uses. See
 *                    config.xml for details on which are supported,
 *                    and which one is currently the default
 *
 * File-backed applications
 *
 * To create a file-backed application, set the create_method parameter
 * to file, and include a zip file, a tar.gz file, or an index.html
 * file in the multipart body of your post, with the parameter name file.
 *
 *    POST https://build.phonegap.com/api/v1/apps
 *****************************************************************/
_createFileBasedApp = function(token, inputFile, dataObj, callback){
   initMultipartUpload(token, inputFile, dataObj, 'apps', "file", callback);
},

/******************************************************************
 *    Updating a file-based application
 *
 *    If the application has been created from a file upload, you
 *    can include a new index.html, zip file, or tar.gz file as the
 *    file parameter in your request to update the contents.
 *
 *    PUT https://build.phonegap.com/api/v1/apps/:id
 *****************************************************************/
_updateFileBasedApp = function(token, inputFile, appId, callback){
   var apiPath = '/api/v1/apps/' + appId + '?auth_token=' + token;
   _fs.createReadStream(inputFile).pipe(_req.put('https://build.phonegap.com' + apiPath))
   .on('error', function(e){if(callback.error instanceof Function){callback.error(e.message);}})
   .on('end', function(){
      if(callback instanceof Function){
         callback();
         }
      else if (callback.success instanceof Function){
         callback.success();
         }
      });

},

/******************************************************************
 * POST https://build.phonegap.com/api/v1/apps/:id/:icon
 *
 * Sets an icon file for a given app. Send a png file as the icon
 * parameter in your post.
 * If you want to have multiple icons for different resolutions, you
 * should not use this API method. Instead, include the different
 * icon files in your application package and specify their use
 * in your config.xml file.
 *
 * The response will have a 201 created status, and the
 * application will be queued for building.
******************************************************************/
_uploadAppIcon = function(token, appId, inputFile, callback){
   initMultipartUpload(token, inputFile, null, 'apps/' + appId + "/icon", "icon", callback);
},

/******************************************************************
 * POST https://build.phonegap.com/api/v1/apps/:id/build
 *
 * Queue new builds for a specified app. The older builds will be
 * discarded, while new ones are queued.
 * The builds will use the most current app contents, as well as
 * the selected signing keys. The response will have a 202 (accepted)
 * status.
 *
 * To choose which platforms to build, include those as a JSON encoded
 * parameter in your post
 *
 * Once the builds are queued, you will want to watch the results of
 * GET /api/v1/apps/:id to see when each platform's status changes
 * from pending (to complete or error).
 *
 ******************************************************************/
//rebuildApp = function(token, appId, dataObj, callback){
//
//},

_createAuthToken = function(rawCredentials, callback){
   var
   auth = "Basic " + new Buffer(rawCredentials).toString("base64"),
   options = {
      url : 'https://'+_URL+"/token",
      headers : { "Authorization" : auth }
      };

   _req.post(options, function (error, response, body) {
      if((error!==null) || (response.statusCode!=200))
         {
         var errStr = "AJAX error.  Your request could not be completed. Please verify your login credentials and network access.  statusCode: " + response.statusCode;
         if(JSON.parse(body) && JSON.parse(body).error){errStr +="\n" + JSON.parse(body).error;}
         if(callback.error instanceof Function){callback.error(errStr);}
         return;
         }
      if(callback instanceof Function){
         callback(JSON.parse(body).token);
         }
      else if(callback.success instanceof Function){
         callback.success(JSON.parse(body).token);
         }
      });
}

/******************************************************************
 *  Module Public Members
 *****************************************************************/
module.exports = {
   //Read API
   getUserData: _getUserData,
   getAppsData: _getAppsData,
   getAppDataById: _getAppDataById,
   getKeysData: _getKeysData,
   getPlatformKeys:_getPlatformKeys,
   getPlatformKeyById: _getPlatformKeyById,
   downloadApp: _downloadApp,
   downloadIcon:_downloadIcon,

   //Write API
   createFileBasedApp:_createFileBasedApp,
   updateFileBasedApp:_updateFileBasedApp,
   uploadAppIcon: _uploadAppIcon,
   createAuthToken: _createAuthToken
};<commit_msg>Enhanced API to support deleting file-based apps.<commit_after>/**************************************************************************
 * Copyright (c) 2012 Adtec Productions, Inc.
 *
 * 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
_URL = 'build.phonegap.com',
_fs = require('fs'),
_https = require('https'),
_mime = require('mime'),
_req = require('request'),
_URL = 'build.phonegap.com'

/******************************************************************
 * Executes the GET Phonegap API call,
 * and writes the output in a JSON-formatted file
 ******************************************************************/
getApiData = function(token, apiCall, callback){
   var
   options = null;

   options = {
      host: _URL,
      path: '/api/v1/' + apiCall + '?auth_token='+token
      };

   _https.get(options, function(res){
      var replyData = '';
      res.on('data', function(data){
         replyData+= data;
         });
      res.on('end', function(){
         if(callback instanceof Function){
            callback(JSON.parse(replyData));
            }
         else if (callback.success instanceof Function){
            callback.success(JSON.parse(replyData));
            }
         });
      }).on('error', function(e){
         console.log("AJAX Err. Message: " + e.message);
         if(callback.error && (callback.error instanceof Function)){callback.error(e.message);}
      });
   },

/******************************************************************
 * Downloads the file at the given URL and saves it in the provided
 * path.
 ******************************************************************/
downloadFile = function(url, outputFilepath, callback){
   var successCallback = (callback instanceof Function)? callback: (callback.success instanceof Function)? callback.success: function(){};
   var errCallback = (callback.error instanceof Function)? callback.error: function(){};

   _req.get(url).pipe(_fs.createWriteStream(outputFilepath))
      .on('error', function(e){errCallback(e.message);})
      .on('close', function(){successCallback(outputFilepath)});
},

/******************************************************************
 *  Get a JSON-encoded representation of the authenticated user,
 *  as well as a listing of associated resources.
 *
 *  This should be the starting point for applications traversing
 *  the PhoneGap Build API. It is aliased to
 *  https://build.phonegap.com/api/v1.
 *
 *  GET https://build.phonegap.com/api/v1/me
 *****************************************************************/
_getUserData = function(token, callback){
   getApiData(token, 'me', callback);
},

/******************************************************************
 *  Get a JSON-encoded representation of the authenticated user's
 *  apps.
 *
 *  API clients can follow the link attribute for each app to get
 *  further details, including the associated signing keys and
 *  collaborators.
 *
 *  GET https://build.phonegap.com/api/v1/apps
 *****************************************************************/
_getAppsData = function(token, callback){
   getApiData(token, 'apps', callback);
   },

/******************************************************************
 *  Get a JSON-encoded representation of a particular app, if the
 *  authenticated user has permission to access it.
 *
 *  In addition to the fields provided in the list of all apps,
 *  this detail view includes:
 *
 *  keys: all of the keys that the app is currently being built with.
 *    This will include the owner's default key for a platform,
 *    if selected
 *
 *  collaborators: each person who has access to this app, along
 *    with their role, if the authenticated user is the owner of
 *    the app. Collaborators who are registered with PhoneGap Build
 *    are listed under active; collaborators you have invited who
 *    have not yet created an account are listed as pending.
 *
 *  GET https://build.phonegap.com/api/v1/apps/:id
 *****************************************************************/
_getAppDataById = function(token, appId, callback){
   getApiData(token, 'apps/' + appId, callback);
   },

/******************************************************************
 *  Get a JSON-encoded list of all the signing keys associated with
 *  your account.
 *
 *  This returns a short listing of all the associated keys--it's
 *  very similar to the list you'll see when requesting /api/v1/me
 *
 *  GET https://build.phonegap.com/api/v1/keys
 *****************************************************************/
_getKeysData = function(token, callback){
   getApiData(token, 'keys', callback);
   },

/******************************************************************
 *  Get a JSON-encoded list of all the signing keys associated with
 *  your account, for a specific platform. That platform can be one
 *  of ios, android, or blackberry.
 *
 *  GET https://build.phonegap.com/api/v1/keys/:platform
 *****************************************************************/
_getPlatformKeys = function(platform){
   getApiData('keys/' + platform, METADATA_DIR + '/keys_' + platform + '.json');
   },

/******************************************************************
 *  Get a JSON-encoded representation of a single signing key.
 *
 *  GET https://build.phonegap.com/api/v1/keys/:platform/:appId
 *****************************************************************/
_getPlatformKeyById = function(platform, appId){
   getApiData('keys/' + platform + '/' + appId, METADATA_DIR + '/keys_' + platform + '_' + appId + '.json');
   },

/******************************************************************
 *  Download the app package for the given platform; available
 *  platforms are android, blackberry, ios, symbian, webos and
 *  winphone.
 *
 *  In the successful case, this API method will return a 302
 *  redirect to the application binary - the actual body of the
 *  response will point to the resource's correct location:
 *
 *  If using the optional argument for the download location,
 *  please ensure that you are using the right extension for
 *  the platform you are downloading.
 *
 *  apk for Android
 *  ipa for iOS
 *  ipk for webOS
 *  jad for unsigned BlackBerry builds; zip if you've uploaded your BlackBerry signing keys
 *  wgz for Symbian
 *  xap for Windows Phone
 *
 *  GET https://build.phonegap.com/api/v1/apps/:id/:platform
 *****************************************************************/
_downloadApp = function(token, appId, platform, outputFilepath, callback){
   var url= 'https://' + _URL + '/api/v1/apps/' + appId + '/' + platform + '?auth_token='+token;
   console.info("\n\nStarting Download...");
   downloadFile(url, outputFilepath, callback);
   },

/*****************************************************************
 * Get the main icon associated with an app - this is either the
 * biggest icon specified in your config.xml file, or an icon you
 * have uploaded through the API or the PhoneGap Build web interface.
 *
 * GET https://build.phonegap.com/api/v1/apps/:id/:icon
*****************************************************************/
_downloadIcon = function(token, appId, outputFilepath, callback){
   var url= 'https://' + _URL + '/api/v1/apps/' + appId + '/icon';
   downloadFile(url, outputFilepath, callback);
   },

/************************************
 * Init Write API
 */

encodeFieldHeader = function(boundary, name, value){
   var result = [
     "--" + boundary + "\r\n",
     "Content-Disposition: form-data; name=\"",
     "" + name + "\"\r\n\r\n" + value + "\r\n",
     ].join("");
   return result;
},

encodeFileHeader = function(boundary, type, name, filename){
   var result = [
      "--" + boundary + "\r\n",
      "Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"\r\n",
      "Content-Type: " + type + "\r\n\r\n"
     ].join("");
   return result;
},

postMultipart = function(token, postData, boundary, apiCall, callback){
   var
   len = 0, i=0,
   options = null,
   request = null;

   for( i=0; i<postData.length; i++){
      len += postData[i].length;
   }

   options = {
      host: _URL,
      path: '/api/v1/' + apiCall + '?auth_token='+token,
      method: 'POST',
      headers:{
         'Content-Type': 'multipart/form-data; boundary=' + boundary,
         'Content-Length': len
      }
   };

   request = _https.request(options, function(response){
      var replyData = '';
      response.on('data', function(chunk){
         replyData+= chunk;
      });
      response.on('end', function(){
         if(callback instanceof Function){
            callback(replyData);
            }
         else if(callback.success instanceof Function){
            callback.success(replyData);
            }
         return;
      });
      response.on('error', function(e){
         if(callback.error instanceof Function){
            callback.error(e.message);
            }
         return;
      });
   });

   for( i=0; i<postData.length; i++){
      request.write(postData[i]);
   }
   request.end();

},

initMultipartUpload = function(token, inputFile, reqData, apiCall, fieldName, callback){
   var
   boundary = 'bound' + Math.random(),
   postData = [],
   fileReader = null,
   fileContents = '';

   if(reqData){postData.push(new Buffer(encodeFieldHeader(boundary, "data", JSON.stringify(reqData)), 'ascii'))};
   postData.push(new Buffer(encodeFileHeader(boundary, _mime.lookup(inputFile), fieldName, inputFile), 'ascii'));
   fileReader = _fs.createReadStream(inputFile, {encoding: 'binary'});
   fileReader.on('data', function(fileData){ fileContents+= fileData;});
   fileReader.on('end', function(){
      postData.push(new Buffer(fileContents, 'binary'));
      postData.push(new Buffer("\r\n--" + boundary + "--", 'ascii'));
      postMultipart(token, postData, boundary, apiCall, callback);
      });
},

 /******************************************************************
 *
 *  Create a new File-based App.
 *
 *  Required parameters
 *
 *  title: You must specify a title for your app - if a title is also
 *         specified in a config.xml in your package, the one in the
 *         config.xml file will take preference.
 *  create_method: use "file" for this method
 *
 *  Optional parameters
 *
 *  package: Sets the package identifier for your app. This can also be
 *           done after creation, or in your config.xml file.
 *           Defaults to com.phonegap.www
 *  version: Sets the version of your app. This can also be done after
 *           creation, or in your config.xml file. Defaults to 0.0.1
 *  description: Sets the description for your app. This can also be
 *               done after creation, or in your config.xml file.
 *               Defaults to empty.
 *  debug: Builds your app in debug mode. Defaults to false.
 *  keys: Set the signing keys to use for each platform you wish to sign.
 *  private: Whether your app can be publicly downloaded. Defaults to
 *           true during beta period; will default to false once the
 *           beta period is complete
 *  phonegap_version: Which version of PhoneGap your app uses. See
 *                    config.xml for details on which are supported,
 *                    and which one is currently the default
 *
 * File-backed applications
 *
 * To create a file-backed application, set the create_method parameter
 * to file, and include a zip file, a tar.gz file, or an index.html
 * file in the multipart body of your post, with the parameter name file.
 *
 *    POST https://build.phonegap.com/api/v1/apps
 *****************************************************************/
_createFileBasedApp = function(token, inputFile, dataObj, callback){
   initMultipartUpload(token, inputFile, dataObj, 'apps', "file", callback);
},

/******************************************************************
 *    Updating a file-based application
 *
 *    If the application has been created from a file upload, you
 *    can include a new index.html, zip file, or tar.gz file as the
 *    file parameter in your request to update the contents.
 *
 *    PUT https://build.phonegap.com/api/v1/apps/:id
 *****************************************************************/
_updateFileBasedApp = function(token, inputFile, appId, callback){
   var apiPath = '/api/v1/apps/' + appId + '?auth_token=' + token;
   _fs.createReadStream(inputFile).pipe(_req.put('https://build.phonegap.com' + apiPath))
   .on('error', function(e){if(callback.error instanceof Function){callback.error(e.message);}})
   .on('end', function(){
      if(callback instanceof Function){
         callback();
         }
      else if (callback.success instanceof Function){
         callback.success();
         }
      });

},

/******************************************************************
 * POST https://build.phonegap.com/api/v1/apps/:id/:icon
 *
 * Sets an icon file for a given app. Send a png file as the icon
 * parameter in your post.
 * If you want to have multiple icons for different resolutions, you
 * should not use this API method. Instead, include the different
 * icon files in your application package and specify their use
 * in your config.xml file.
 *
 * The response will have a 201 created status, and the
 * application will be queued for building.
******************************************************************/
_uploadAppIcon = function(token, appId, inputFile, callback){
   initMultipartUpload(token, inputFile, null, 'apps/' + appId + "/icon", "icon", callback);
},

/******************************************************************
 *
 * DELETE https://build.phonegap.com/api/v1/apps/:id
 *
 * Delete your application from PhoneGap Build - will return either
 * a 202 (accepted) status, or 404 (if the app cannot be found).
 *****************************************************************/
_deleteFileBasedApp = function(token, appId, callback){
   var options = {
      url : 'https://'+_URL+"/api/v1/apps/" + appId + '?auth_token=' + token
      };

   _req.del(options, function (error, response, body) {
      if(error){
         if(callback instanceof Function){
            callback("Error deleting app " + appId);
            }
         else if(callback.error instanceof Function){
            callback.error("Error deleting app " + appId);
            }
         }
      else{

         if(callback instanceof Function){
            callback(body);
            }
         else if(callback.success instanceof Function){
            callback.success(body);
            }
      }
   });

},

/******************************************************************
 * POST https://build.phonegap.com/api/v1/apps/:id/build
 *
 * Queue new builds for a specified app. The older builds will be
 * discarded, while new ones are queued.
 * The builds will use the most current app contents, as well as
 * the selected signing keys. The response will have a 202 (accepted)
 * status.
 *
 * To choose which platforms to build, include those as a JSON encoded
 * parameter in your post
 *
 * Once the builds are queued, you will want to watch the results of
 * GET /api/v1/apps/:id to see when each platform's status changes
 * from pending (to complete or error).
 *
 ******************************************************************/
//rebuildApp = function(token, appId, dataObj, callback){
//
//},

_createAuthToken = function(rawCredentials, callback){
   var
   auth = "Basic " + new Buffer(rawCredentials).toString("base64"),
   options = {
      url : 'https://'+_URL+"/token",
      headers : { "Authorization" : auth }
      };

   _req.post(options, function (error, response, body) {
      if((error!==null) || (response.statusCode!=200))
         {
         var errStr = "AJAX error.  Your request could not be completed. Please verify your login credentials and network access.  statusCode: " + response.statusCode;
         if(JSON.parse(body) && JSON.parse(body).error){errStr +="\n" + JSON.parse(body).error;}
         if(callback.error instanceof Function){callback.error(errStr);}
         return;
         }
      if(callback instanceof Function){
         callback(JSON.parse(body).token);
         }
      else if(callback.success instanceof Function){
         callback.success(JSON.parse(body).token);
         }
      });
}

/******************************************************************
 *  Module Public Members
 *****************************************************************/
module.exports = {
   //Read API
   getUserData: _getUserData,
   getAppsData: _getAppsData,
   getAppDataById: _getAppDataById,
   getKeysData: _getKeysData,
   getPlatformKeys:_getPlatformKeys,
   getPlatformKeyById: _getPlatformKeyById,
   downloadApp: _downloadApp,
   downloadIcon:_downloadIcon,

   //Write API
   createFileBasedApp:_createFileBasedApp,
   updateFileBasedApp:_updateFileBasedApp,
   uploadAppIcon: _uploadAppIcon,
   deleteFileBasedApp: _deleteFileBasedApp,
   createAuthToken: _createAuthToken
};<|endoftext|>