<commit_before>"use strict";
var dateString = '2015-03-14 10:12:00';
var dateObject = new Date('2015-03-14 10:12:00');
var dateMoment = moment(dateObject);

var dateStringAfter = '2015-07-22 15:19:22';
var dateObjectAfter = new Date(dateStringAfter);
var dateMomentAfter = moment(dateObjectAfter);

//helpers tests
if (Meteor.isClient) {

  Tinytest.add('helpers - init templates', function (test) {
    test.equal(Blaze.toHTML(Template.testTemplateInit), 'test');
  });

  Tinytest.add('moFormat - helper with all variables', function (test) {
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateMoment,
      formatToken: 'YYYY-MM-DD Dd Mo'
    }), '2015-03-14 146 3rd');
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateMoment,
      formatToken: 'hh:mm a'
    }), '10:12 am');
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      date: dateMoment,
      formatToken: '[Quarter] Q, Wo [week]'
    }), 'Quarter 1, 11th week');
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      date: dateMoment,
      formatToken: 'dddd [the] Do [of] MMMM, YYYY'
    }), 'Saturday the 14th of March, 2015');
  });

  Tinytest.add('moFormat - different date vars provided', function (test) {
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateObject,
      formatToken: 'YYYY-MM-DD Dd Mo'
    }), '2015-03-14 146 3rd');
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateString,
      formatToken: 'hh:mm a'
    }), '10:12 am');
  });

  Tinytest.add('moFormat - helper with missing variables', function (test) {
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateMoment,
    }), '2015-03-14'); //default format
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      formatToken: 'hh:mm a'
    }), '');
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      date: dateMoment,
    }), '2015-03-14'); //default format
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      formatToken: 'dddd [the] Do [of] MMMM, YYYY'
    }), '');
  });

  Tinytest.add('moFormat - helper with empty strings & return now', function (test) {
    var fmt = 'dddd [the] Do [of] MMMM, YYYY';
    mo.configure({returnNowIfDateNotGiven: true});
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: '', formatToken: fmt}),
      moment().format(fmt)
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: 'null', formatToken: fmt}),
      moment().format(fmt)
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: 'undefined', formatToken: fmt}),
      moment().format(fmt)
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: null, formatToken: fmt}),
      moment().format(fmt)
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: undefined, formatToken: fmt}),
      moment().format(fmt)
    );
    //error could cause this
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: '|', formatToken: fmt}),
      moment().format(fmt)
    );
    mo.configure({returnNowIfDateNotGiven: false});
  });

  Tinytest.add('moFormat - helper with empty strings & return null', function (test) {
    mo.configure({returnNowIfDateNotGiven: false});
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: ''}),
      ''
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: 'null'}),
      ''
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: 'undefined'}),
      ''
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: null}),
      ''
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: undefined}),
      ''
    );
    //error could cause this
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: '|'}),
      ''
    );
  });

  Tinytest.add('moFormat - missing vars + defaults changed', function (test) {
    mo.configure({
      returnNowIfDateNotGiven: true,
      formatTokens: {
        'default': 'dddd [the] Do [of] MMMM, YYYY',
      }
    });
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateMoment,
    }), 'Saturday the 14th of March, 2015');
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      formatToken: 'YYYY'
    }), moment().format('YYYY'));
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      date: dateMoment,
    }), 'Saturday the 14th of March, 2015');
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      formatToken: 'MMM, YYYY'
    }), moment().format('MMM, YYYY'));
    mo.configure({returnNowIfDateNotGiven: false});
  });

  Tinytest.add('moFromNow - with all variables', function (test) {

    test.equal(Blaze.toHTMLWithData(Template.moFromNowArgs, {
      date: dateMoment,
      withoutSuffix: true,
    }), dateMoment.fromNow(true));
    test.equal(Blaze.toHTMLWithData(Template.moFromNowArgs, {
      date: dateMoment,
      withoutSuffix: false,
    }), dateMoment.fromNow(false));

    test.equal(Blaze.toHTMLWithData(Template.moFromNowVars, {
      date: dateMoment,
      withoutSuffix: true,
    }), dateMoment.fromNow(true));
    test.equal(Blaze.toHTMLWithData(Template.moFromNowVars, {
      date: dateMoment,
      withoutSuffix: false,
    }), dateMoment.fromNow(false));

  });

  Tinytest.add('moFromNow - different date types provided', function (test) {

    test.equal(Blaze.toHTMLWithData(Template.moFromNowArgs, {
      date: dateString,
      withoutSuffix: true,
    }), dateMoment.fromNow(true));

    test.equal(Blaze.toHTMLWithData(Template.moFromNowArgs, {
      date: dateObject,
      withoutSuffix: false,
    }), dateMoment.fromNow(false));

    test.equal(Blaze.toHTMLWithData(Template.moFromNowVars, {
      date: dateString,
    }), dateMoment.fromNow());

  });

  Tinytest.add('moDiff - mix of everything as args', function (test) {

    test.equal(Blaze.toHTMLWithData(Template.moDiffArgs, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'days',
      //returnFloat: false
    }), '130');

    test.equal(Blaze.toHTMLWithData(Template.moDiffArgs, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'months',
      //returnFloat: false
    }), '4');

    test.equal(Blaze.toHTMLWithData(Template.moDiffArgs, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      //units: 'days', // defaults to seconds
      returnFloat: true
    }), '11250442');

    test.matches(Blaze.toHTMLWithData(Template.moDiffArgs, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'months',
      returnFloat: true
    }), /(4\.2)[0-9]*/);

  });

  Tinytest.add('moDiff - mix of everything as vars', function (test) {

    test.equal(Blaze.toHTMLWithData(Template.moDiffVars, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'days',
      //returnFloat: false
    }), '130');

    test.equal(Blaze.toHTMLWithData(Template.moDiffVars, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'months',
      //returnFloat: false
    }), '4');

    test.equal(Blaze.toHTMLWithData(Template.moDiffVars, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      //units: 'days', // defaults to seconds
      returnFloat: true
    }), '11250442');

    test.matches(Blaze.toHTMLWithData(Template.moDiffVars, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'months',
      returnFloat: true
    }), /(4\.2)[0-9]*/);

  });

  Tinytest.add('debug - configuration', function (test) {
    mo.configure({debug:true});
    test.equal(mo.options.debug, true);
    mo.configure({debug:false});
  });

  Tinytest.add('debug - logging enabled test', function (test) {
    mo.configure({debug:true});
    mo.log('test');
    test.equal(mo.logged,'test');
    mo.configure({debug:false});
  });

  Tinytest.add('debug - logging disabled test', function (test) {
    mo.configure({debug:false});
    mo.log('test');
    test.isUndefined(mo.logged);
  });

  Tinytest.add('debug - log if returning date', function (test) {
    mo.configure({
      debug: true,
      returnNowIfDateNotGiven: true
    });

    test.equal(
      Blaze.toHTMLWithData(Template.moFormatArgs, {}),
      moment().format(mo.options.formatTokens.default)
    );
    test.equal(mo.logged, mo._msg.dateNotValidReturnNow);

    mo.configure({debug:false});
  });

  Tinytest.add('debug - log if returning null', function (test) {
    mo.configure({
      debug: true,
      returnNowIfDateNotGiven: false
    });

    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {}), '');
    test.equal(mo.logged, mo._msg.dateNotValidReturnNull);

    mo.configure({debug:false});
  });

  Tinytest.add('providing dates as string with format', function (test) {
    var fmt = 'YYYYMMDD';
    var str = '20150105';
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatArgs, {
        date: str + '|' + fmt
      }),
      moment(str, fmt).format(mo.options.formatTokens.default)
    );

    var fmt = 'YYYYDDD-HHmm';
    var str = '2015332-1732';
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatArgs, {
        date: str + '|' + fmt
      }),
      moment(str, fmt).format(mo.options.formatTokens.default)
    );

  });

}
<commit_msg>More Tests for formatToken library<commit_after>"use strict";
var dateString = '2015-03-14 10:12:00';
var dateObject = new Date('2015-03-14 10:12:00');
var dateMoment = moment(dateObject);

var dateStringAfter = '2015-07-22 15:19:22';
var dateObjectAfter = new Date(dateStringAfter);
var dateMomentAfter = moment(dateObjectAfter);

//helpers tests
if (Meteor.isClient) {

  Tinytest.add('helpers - init templates', function (test) {
    test.equal(Blaze.toHTML(Template.testTemplateInit), 'test');
  });

  Tinytest.add('moFormat - helper with all variables', function (test) {
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateMoment,
      formatToken: 'YYYY-MM-DD Dd Mo'
    }), '2015-03-14 146 3rd');
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateMoment,
      formatToken: 'hh:mm a'
    }), '10:12 am');
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      date: dateMoment,
      formatToken: '[Quarter] Q, Wo [week]'
    }), 'Quarter 1, 11th week');
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      date: dateMoment,
      formatToken: 'dddd [the] Do [of] MMMM, YYYY'
    }), 'Saturday the 14th of March, 2015');
  });

  Tinytest.add('moFormat - different date vars provided', function (test) {
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateObject,
      formatToken: 'YYYY-MM-DD Dd Mo'
    }), '2015-03-14 146 3rd');
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateString,
      formatToken: 'hh:mm a'
    }), '10:12 am');
  });

  Tinytest.add('moFormat - helper with missing variables', function (test) {
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateMoment,
    }), '2015-03-14'); //default format
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      formatToken: 'hh:mm a'
    }), '');
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      date: dateMoment,
    }), '2015-03-14'); //default format
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      formatToken: 'dddd [the] Do [of] MMMM, YYYY'
    }), '');
  });

  Tinytest.add('moFormat - helper with empty strings & return now', function (test) {
    var fmt = 'dddd [the] Do [of] MMMM, YYYY';
    mo.configure({returnNowIfDateNotGiven: true});
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: '', formatToken: fmt}),
      moment().format(fmt)
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: 'null', formatToken: fmt}),
      moment().format(fmt)
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: 'undefined', formatToken: fmt}),
      moment().format(fmt)
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: null, formatToken: fmt}),
      moment().format(fmt)
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: undefined, formatToken: fmt}),
      moment().format(fmt)
    );
    //error could cause this
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: '|', formatToken: fmt}),
      moment().format(fmt)
    );
    mo.configure({returnNowIfDateNotGiven: false});
  });

  Tinytest.add('moFormat - helper with empty strings & return null', function (test) {
    mo.configure({returnNowIfDateNotGiven: false});
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: ''}),
      ''
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: 'null'}),
      ''
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: 'undefined'}),
      ''
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: null}),
      ''
    );
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: undefined}),
      ''
    );
    //error could cause this
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatVars, {date: '|'}),
      ''
    );
  });

  Tinytest.add('moFormat - missing vars + defaults changed', function (test) {
    mo.configure({
      returnNowIfDateNotGiven: true,
      formatTokens: {
        'default': 'dddd [the] Do [of] MMMM, YYYY',
      }
    });
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      date: dateMoment,
    }), 'Saturday the 14th of March, 2015');
    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {
      formatToken: 'YYYY'
    }), moment().format('YYYY'));
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      date: dateMoment,
    }), 'Saturday the 14th of March, 2015');
    test.equal(Blaze.toHTMLWithData(Template.moFormatVars, {
      formatToken: 'MMM, YYYY'
    }), moment().format('MMM, YYYY'));
    mo.configure({returnNowIfDateNotGiven: false});
  });

  Tinytest.add('moFromNow - with all variables', function (test) {

    test.equal(Blaze.toHTMLWithData(Template.moFromNowArgs, {
      date: dateMoment,
      withoutSuffix: true,
    }), dateMoment.fromNow(true));
    test.equal(Blaze.toHTMLWithData(Template.moFromNowArgs, {
      date: dateMoment,
      withoutSuffix: false,
    }), dateMoment.fromNow(false));

    test.equal(Blaze.toHTMLWithData(Template.moFromNowVars, {
      date: dateMoment,
      withoutSuffix: true,
    }), dateMoment.fromNow(true));
    test.equal(Blaze.toHTMLWithData(Template.moFromNowVars, {
      date: dateMoment,
      withoutSuffix: false,
    }), dateMoment.fromNow(false));

  });

  Tinytest.add('moFromNow - different date types provided', function (test) {

    test.equal(Blaze.toHTMLWithData(Template.moFromNowArgs, {
      date: dateString,
      withoutSuffix: true,
    }), dateMoment.fromNow(true));

    test.equal(Blaze.toHTMLWithData(Template.moFromNowArgs, {
      date: dateObject,
      withoutSuffix: false,
    }), dateMoment.fromNow(false));

    test.equal(Blaze.toHTMLWithData(Template.moFromNowVars, {
      date: dateString,
    }), dateMoment.fromNow());

  });

  Tinytest.add('moDiff - mix of everything as args', function (test) {

    test.equal(Blaze.toHTMLWithData(Template.moDiffArgs, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'days',
      //returnFloat: false
    }), '130');

    test.equal(Blaze.toHTMLWithData(Template.moDiffArgs, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'months',
      //returnFloat: false
    }), '4');

    test.equal(Blaze.toHTMLWithData(Template.moDiffArgs, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      //units: 'days', // defaults to seconds
      returnFloat: true
    }), '11250442');

    test.matches(Blaze.toHTMLWithData(Template.moDiffArgs, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'months',
      returnFloat: true
    }), /(4\.2)[0-9]*/);

  });

  Tinytest.add('moDiff - mix of everything as vars', function (test) {

    test.equal(Blaze.toHTMLWithData(Template.moDiffVars, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'days',
      //returnFloat: false
    }), '130');

    test.equal(Blaze.toHTMLWithData(Template.moDiffVars, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'months',
      //returnFloat: false
    }), '4');

    test.equal(Blaze.toHTMLWithData(Template.moDiffVars, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      //units: 'days', // defaults to seconds
      returnFloat: true
    }), '11250442');

    test.matches(Blaze.toHTMLWithData(Template.moDiffVars, {
      dateA: dateMomentAfter,
      dateB: dateMoment,
      units: 'months',
      returnFloat: true
    }), /(4\.2)[0-9]*/);

  });

  Tinytest.add('debug - configuration', function (test) {
    mo.configure({debug:true});
    test.equal(mo.options.debug, true);
    mo.configure({debug:false});
  });

  Tinytest.add('debug - logging enabled test', function (test) {
    mo.configure({debug:true});
    mo.log('test');
    test.equal(mo.logged,'test');
    mo.configure({debug:false});
  });

  Tinytest.add('debug - logging disabled test', function (test) {
    mo.configure({debug:false});
    mo.log('test');
    test.isUndefined(mo.logged);
  });

  Tinytest.add('debug - log if returning date', function (test) {
    mo.configure({
      debug: true,
      returnNowIfDateNotGiven: true
    });

    test.equal(
      Blaze.toHTMLWithData(Template.moFormatArgs, {}),
      moment().format(mo.options.formatTokens.default)
    );
    test.equal(mo.logged, mo._msg.dateNotValidReturnNow);

    mo.configure({debug:false});
  });

  Tinytest.add('debug - log if returning null', function (test) {
    mo.configure({
      debug: true,
      returnNowIfDateNotGiven: false
    });

    test.equal(Blaze.toHTMLWithData(Template.moFormatArgs, {}), '');
    test.equal(mo.logged, mo._msg.dateNotValidReturnNull);

    mo.configure({debug:false});
  });

  Tinytest.add('providing dates as string with format', function (test) {
    var fmt = 'YYYYMMDD';
    var str = '20150105';
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatArgs, {
        date: str + '|' + fmt
      }),
      moment(str, fmt).format(mo.options.formatTokens.default)
    );

    var fmt = 'YYYYDDD-HHmm';
    var str = '2015332-1732';
    test.equal(
      Blaze.toHTMLWithData(Template.moFormatArgs, {
        date: str + '|' + fmt
      }),
      moment(str, fmt).format(mo.options.formatTokens.default)
    );

  });

  Tinytest.add('formatToken library - function as formatToken', function (test) {

    //this enables you to make the formatToken a result of a function
    //this (inside the function) should be the moment object
    mo.configure({
      formatTokens: {
        'smartDate': function () {
          if (this.format('HHmm') === '0000') {
            return 'dddd'; //if no time, assume date and return day like 'Sunday'
          }
          // if has time, assume date AND time and return like Sunday 1400
          return 'dddd h:mma';
        }
      }
    });

    var dateWithTime = moment('2015-08-15 09:22', 'YYYY-MM-DD HH:mm');

    var dateWithoutTime = moment('2015-08-15', 'YYYY-MM-DD');

    test.equal(
      Blaze.toHTMLWithData(
        Template.moFormatArgs, {date: dateWithoutTime, formatToken: 'smartDate'}
      ),
      'Saturday'
    );

    test.equal(
      Blaze.toHTMLWithData(
        Template.moFormatArgs, {date: dateWithTime, formatToken: 'smartDate'}
      ),
      'Saturday 9:22am'
    );

    //make a formatToken function that returns 'Game Day!' if it is a Friday
    var isGameDay = moment('2015-08-14', 'YYYY-MM-DD');
    var isNotGameDayA = moment('2015-08-16', 'YYYY-MM-DD');
    var isNotGameDayB = moment('2015-09-22', 'YYYY-MM-DD');

    mo.configure({
      formatTokens: {
        'gameDay': function () {
          if (this.format('dddd') === 'Friday') {
            //if Friday, return this
            return '[Game Day!]';
          }
          //otherwise return just a day with a countdown
          var nextFriday = moment(this).day(5);
          var daysUntilFriday = nextFriday.diff(this, 'days');
          return 'dddd ' + '[' + daysUntilFriday + ' days until Game Day]';
        }
      }
    });

    test.equal(
      Blaze.toHTMLWithData(
        Template.moFormatArgs,
        {date: isGameDay, formatToken: 'gameDay'}
      ), 'Game Day!'
    );

    test.equal(
      Blaze.toHTMLWithData(
        Template.moFormatArgs,
        {date: isNotGameDayA, formatToken: 'gameDay'}
      ), 'Sunday 5 days until Game Day'
    );

    test.equal(
      Blaze.toHTMLWithData(
        Template.moFormatArgs,
        {date: isNotGameDayB, formatToken: 'gameDay'}
      ), 'Tuesday 3 days until Game Day'
    );

  });

  Tinytest.add('formatToken library - add to library', function (test) {

    mo.configure({
      formatTokens: {
        'quickDate': 'D MMM',
        'notSoQuickDate': 'dddd Do MMMM YYYY',
      }
    });

    test.equal(
      Blaze.toHTMLWithData(
        Template.moFormatArgs,
        {date: dateMoment, formatToken: 'quickDate'}
      ), '14 Mar'
    );

    test.equal(
      Blaze.toHTMLWithData(
        Template.moFormatArgs,
        {date: dateMoment, formatToken: 'notSoQuickDate'}
      ), 'Saturday 14th March 2015'
    );

  });

  Tinytest.add('formatToken library - overwrite built in library', function (test) {

    mo.configure({
      formatTokens: {
        'dayOfWeek': 'dd',
        'time': 'HH:MM',
      }
    });

    test.equal(
      Blaze.toHTMLWithData(
        Template.moFormatArgs,
        {date: dateMoment, formatToken: 'dayOfWeek'}
      ), 'Sa'
    );

    test.equal(
      Blaze.toHTMLWithData(
        Template.moFormatArgs,
        {date: dateMoment, formatToken: 'time'}
      ), '10:03'
    );

  });
}
<|endoftext|>


<commit_before>test('basics', function() {
	ok(Overload, 'Overload exists');
	ok(new Overload() instanceof Overload, 'Overload object created');

	ok(O, 'O exists');
	ok(O.any, 'O.any exists');
	ok(O.truthy, 'O.truthy exists');
	ok(O.falsy, 'O.falsy exists');
});

test('method registration', function() {
	var a = function() { return false; },
		b = function() { return true; };

	var o = new Overload();
	o.args().use(a);

	ok(_.isFunction(o.expose()), 'Expose is a function');
	strictEqual(o.expose()(), false, 'Method registered and called');
	strictEqual(o.expose()(), false, 'Method returns return value');
	strictEqual(o.expose()(), false, 'Overload can be used without arguments');
});

test('overload paths', function() {
	var overload = new Overload();

	overload.args(null).use(function() { return null; })
			.args(undefined).use(function() { return undefined; })
			.args(Infinity).use(function() { return Infinity; })
			.args(Date).use(function() { return Date; })
			.args(NaN).use(function() { return NaN; })
			.args(Number).use(function() { return Number; })
			.args(String).use(function() { return String; })
			.args(Object).use(function() { return Object; })
			.args(Array).use(function() { return Array; })
			.args(RegExp).use(function() { return RegExp; })
			.args(Boolean).use(function() { return Boolean; })
			.args(Function).use(function() { return Function; })
			.args(Element).use(function() { return Element; });

	var method = overload.expose();

	var types = [
		{
			type: null,
			name: 'null',
			param: null
		},
		{
			type: undefined,
			name: 'undefined',
			param: undefined
		},
		{
			type: Infinity,
			name: 'Infinity',
			param: Infinity
		},
		{
			type: Date,
			name: 'Date',
			param: new Date()
		},
		{
			type: Number,
			name: 'Number',
			param: 0
		},
		{
			type: String,
			name: 'String',
			param: ''
		},
		{
			type: Object,
			name: 'Object',
			param: {}
		},
		{
			type: Array,
			name: 'Array',
			param: []
		},
		{
			type: RegExp,
			name: 'RegExp',
			param: /-/gi
		},
		{
			type: Boolean,
			name: 'Boolean',
			param: false
		},
		{
			type: Function,
			name: 'Function',
			param: function() {}
		},
		{
			type: Element,
			name: 'Element',
			param: document.getElementsByTagName('body')[0]
		}
	];

	var idx = types.length;
	while (idx--) {
		var type = types[idx];
		strictEqual(method(type.param), type.type, type.name + ' passed overload test');
	}

	// NaN is special because NaN !== NaN
	strictEqual(_.isNaN(method(NaN)), _.isNaN(NaN), 'NaN passed overload test');
});

test('"this" context', function() {
	var a = function() { return this; };

	var o = new Overload();
	o.args().use(a);

	var method = o.expose();

	equal(method.call('one'), 'one', 'Exposed method can be called with context');
	equal(method.apply('two'), 'two', 'Exposed method can be applied with context');
	equal(method.bind('three')(), 'three', 'Exposed method can be bound with context');
	equal(o.call('four'), 'four', 'Overload can be called directly with context');
	equal(o.apply('five'), 'five', 'Overload can be called applied with context');
	equal(o.bind('six')(), 'six', 'Overload can be bound with context');
});

test('truthy', function() {
	var a = function() { return true; };

	var o = new Overload();
	o.err = function() { return 'error'; };
	o.args(O.truthy).use(a);

	var method = o.expose();

	strictEqual(method(1), true, 'truthy works with #1');
	strictEqual(method(true), true, 'truthy works with true');
	strictEqual(method('1'), true, 'truthy works with "1"');
	strictEqual(method({}), true, 'truthy works with Object');
	strictEqual(method([]), true, 'truthy works with Array');
	strictEqual(method(0), 'error', 'truthy throws error with falsy value');
});

test('falsy', function() {
	var a = function() { return false; };

	var o = new Overload();
	o.err = function() { return 'error'; };
	o.args(O.falsy).use(a);

	var method = o.expose();

	strictEqual(method(0), false, 'falsy works with #0');
	strictEqual(method(false), false, 'falsy works with false');
	strictEqual(method(''), false, 'falsy works with ""');
	strictEqual(method(null), false, 'falsy works with null');
	strictEqual(method(undefined), false, 'falsy works with undefined');
	strictEqual(method(1), 'error', 'falsy throws error with truthy value');
});

test('length', function() {
	var a = function() { return 1; },
		b = function() { return 2; },
		c = function() { return 3; };

	var o = new Overload();
	o.length(0).use(a);
	o.length(1).use(b);
	o.length(2).use(c);

	var method = o.expose();
	strictEqual(method(), 1, 'No params called first function');
	strictEqual(method(1), 2, 'One param called second function');
	strictEqual(method(1, 2), 3, 'Two params called third function');

	var ov = new Overload();
	ov.length().use(function(a, b, c, d) { return 4; });

	var method2 = ov.expose();

	strictEqual(method2(1, 2, 3, 4), 4, 'No length gets length from function');
});

test('any', function() {
	var a = function() { return 0; },
		b = function() { return 1; };

	var o = new Overload();
	o.err = function() { return 'error'; };
	o.args(O.any(String, Boolean, Date)).use(a);
	o.args(O.any(Array, Object, Function)).use(b);

	var method = o.expose();

	strictEqual(method(''), 0, 'Any first test passed');
	strictEqual(method(true), 0, 'Any first test passed');
	strictEqual(method(new Date()), 0, 'Any first test passed');

	strictEqual(method([]), 1, 'Any second test passed');
	strictEqual(method({}), 1, 'Any second test passed');
	strictEqual(method(function() {}), 1, 'Any second test passed');
});

test('except', function() {
	var a = function() { return 0; },
		b = function() { return 1; };

	var o = new Overload();
	o.err = function() { return 'error'; };
	o.args(O.except(String, Boolean, Date)).use(a);
	o.args(O.except(Array, Object, Function)).use(b);

	var method = o.expose();

	strictEqual(method(''), 1, 'Except first test passed');
	strictEqual(method(true), 1, 'Except first test passed');
	strictEqual(method(new Date()), 1, 'Except first test passed');

	strictEqual(method([]), 0, 'Except second test passed');
	strictEqual(method({}), 0, 'Except second test passed');
	strictEqual(method(function() {}), 0, 'Except second test passed');
});

test('fallback', function() {
	var a = function() { return 0; };

	var o = new Overload();
	o.args(String, Boolean, Date).use(function() {});
	o.fallback(a);

	var method = o.expose();

	strictEqual(method(), 0, 'Fallback function called');
});

test('passed parameters', function() {
	var a = function(param) { return param; };

	var o = new Overload();
	o.args(O.any(String, Number, Boolean)).use(a);

	var method = o.expose();

	equal(method('one'), 'one', 'String passed and returned');
	equal(method(2), 2, 'Number passed and returned');
	equal(method(true), true, 'Boolean passed and returned');
});

test('custom', function() {
	Overload.defineType('$', function(val) {
		return (val instanceof jQuery);
	});

	ok(O.$, 'Custom type added');

	var a = function() { return 0; };

	var o = new Overload();
	o.args(O.$).use(a);

	var method = o.expose();
	strictEqual(method($('body')), 0, 'Custom function works as a definition');
});<commit_msg>Add additional unit tests for O.any<commit_after>test('basics', function() {
	ok(Overload, 'Overload exists');
	ok(new Overload() instanceof Overload, 'Overload object created');

	ok(O, 'O exists');
	ok(O.any, 'O.any exists');
	ok(O.truthy, 'O.truthy exists');
	ok(O.falsy, 'O.falsy exists');
});

test('method registration', function() {
	var a = function() { return false; },
		b = function() { return true; };

	var o = new Overload();
	o.args().use(a);

	ok(_.isFunction(o.expose()), 'Expose is a function');
	strictEqual(o.expose()(), false, 'Method registered and called');
	strictEqual(o.expose()(), false, 'Method returns return value');
	strictEqual(o.expose()(), false, 'Overload can be used without arguments');
});

test('overload paths', function() {
	var overload = new Overload();

	overload.args(null).use(function() { return null; })
			.args(undefined).use(function() { return undefined; })
			.args(Infinity).use(function() { return Infinity; })
			.args(Date).use(function() { return Date; })
			.args(NaN).use(function() { return NaN; })
			.args(Number).use(function() { return Number; })
			.args(String).use(function() { return String; })
			.args(Object).use(function() { return Object; })
			.args(Array).use(function() { return Array; })
			.args(RegExp).use(function() { return RegExp; })
			.args(Boolean).use(function() { return Boolean; })
			.args(Function).use(function() { return Function; })
			.args(Element).use(function() { return Element; });

	var method = overload.expose();

	var types = [
		{
			type: null,
			name: 'null',
			param: null
		},
		{
			type: undefined,
			name: 'undefined',
			param: undefined
		},
		{
			type: Infinity,
			name: 'Infinity',
			param: Infinity
		},
		{
			type: Date,
			name: 'Date',
			param: new Date()
		},
		{
			type: Number,
			name: 'Number',
			param: 0
		},
		{
			type: String,
			name: 'String',
			param: ''
		},
		{
			type: Object,
			name: 'Object',
			param: {}
		},
		{
			type: Array,
			name: 'Array',
			param: []
		},
		{
			type: RegExp,
			name: 'RegExp',
			param: /-/gi
		},
		{
			type: Boolean,
			name: 'Boolean',
			param: false
		},
		{
			type: Function,
			name: 'Function',
			param: function() {}
		},
		{
			type: Element,
			name: 'Element',
			param: document.getElementsByTagName('body')[0]
		}
	];

	var idx = types.length;
	while (idx--) {
		var type = types[idx];
		strictEqual(method(type.param), type.type, type.name + ' passed overload test');
	}

	// NaN is special because NaN !== NaN
	strictEqual(_.isNaN(method(NaN)), _.isNaN(NaN), 'NaN passed overload test');
});

test('"this" context', function() {
	var a = function() { return this; };

	var o = new Overload();
	o.args().use(a);

	var method = o.expose();

	equal(method.call('one'), 'one', 'Exposed method can be called with context');
	equal(method.apply('two'), 'two', 'Exposed method can be applied with context');
	equal(method.bind('three')(), 'three', 'Exposed method can be bound with context');
	equal(o.call('four'), 'four', 'Overload can be called directly with context');
	equal(o.apply('five'), 'five', 'Overload can be called applied with context');
	equal(o.bind('six')(), 'six', 'Overload can be bound with context');
});

test('truthy', function() {
	var a = function() { return true; };

	var o = new Overload();
	o.err = function() { return 'error'; };
	o.args(O.truthy).use(a);

	var method = o.expose();

	strictEqual(method(1), true, 'truthy works with #1');
	strictEqual(method(true), true, 'truthy works with true');
	strictEqual(method('1'), true, 'truthy works with "1"');
	strictEqual(method({}), true, 'truthy works with Object');
	strictEqual(method([]), true, 'truthy works with Array');
	strictEqual(method(0), 'error', 'truthy throws error with falsy value');
});

test('falsy', function() {
	var a = function() { return false; };

	var o = new Overload();
	o.err = function() { return 'error'; };
	o.args(O.falsy).use(a);

	var method = o.expose();

	strictEqual(method(0), false, 'falsy works with #0');
	strictEqual(method(false), false, 'falsy works with false');
	strictEqual(method(''), false, 'falsy works with ""');
	strictEqual(method(null), false, 'falsy works with null');
	strictEqual(method(undefined), false, 'falsy works with undefined');
	strictEqual(method(1), 'error', 'falsy throws error with truthy value');
});

test('length', function() {
	var a = function() { return 1; },
		b = function() { return 2; },
		c = function() { return 3; };

	var o = new Overload();
	o.length(0).use(a);
	o.length(1).use(b);
	o.length(2).use(c);

	var method = o.expose();
	strictEqual(method(), 1, 'No params called first function');
	strictEqual(method(1), 2, 'One param called second function');
	strictEqual(method(1, 2), 3, 'Two params called third function');

	var ov = new Overload();
	ov.length().use(function(a, b, c, d) { return 4; });

	var method2 = ov.expose();

	strictEqual(method2(1, 2, 3, 4), 4, 'No length gets length from function');
});

test('any', function() {
	var a = function() { return 0; },
		b = function() { return 1; };

	var o = new Overload();
	o.err = function() { return 'error'; };
	o.args(O.any(String, Boolean, Date)).use(a);
	o.args(O.any(Array, Object, Function)).use(b);

	var method = o.expose();

	strictEqual(method(''), 0, 'Any first test passed');
	strictEqual(method(true), 0, 'Any first test passed');
	strictEqual(method(new Date()), 0, 'Any first test passed');

	strictEqual(method([]), 1, 'Any second test passed');
	strictEqual(method({}), 1, 'Any second test passed');
	strictEqual(method(function() {}), 1, 'Any second test passed');
});

test('except', function() {
	var a = function() { return 0; },
		b = function() { return 1; };

	var o = new Overload();
	o.err = function() { return 'error'; };
	o.args(O.except(String, Boolean, Date)).use(a);
	o.args(O.except(Array, Object, Function)).use(b);

	var method = o.expose();

	strictEqual(method(''), 1, 'Except first test passed');
	strictEqual(method(true), 1, 'Except first test passed');
	strictEqual(method(new Date()), 1, 'Except first test passed');

	strictEqual(method([]), 0, 'Except second test passed');
	strictEqual(method({}), 0, 'Except second test passed');
	strictEqual(method(function() {}), 0, 'Except second test passed');
});

test('fallback', function() {
	var a = function() { return 0; };

	var o = new Overload();
	o.args(String, Boolean, Date).use(function() {});
	o.fallback(a);

	var method = o.expose();

	strictEqual(method(), 0, 'Fallback function called');
});

test('passed parameters', function() {
	var a = function(param) { return param; };
	var b = function() { return 'fallback'; };
	var method = Overload().args(O.any(String, Number, Boolean)).use(a).fallback(b).expose();

	equal(method('one'), 'one', 'String passed and returned');
	equal(method(2), 2, 'Number passed and returned');
	equal(method(true), true, 'Boolean passed and returned');
	equal(method(null), 'fallback', 'No items matched and defered to the fallback');
});

test('custom', function() {
	Overload.defineType('$', function(val) {
		return (val instanceof jQuery);
	});

	ok(O.$, 'Custom type added');

	var a = function() { return 0; };

	var o = new Overload();
	o.args(O.$).use(a);

	var method1 = o.expose();
	strictEqual(method1($('body')), 0, 'Custom function works as a definition');

	var method2 = new Overload().args(O.any(Boolean, O.$)).use(a).expose();
	strictEqual(method2($('body')), 0, 'Custom function work inside any() custom definition');
});<|endoftext|>


<commit_before>QUnit.test("core API",function t1(assert){
	assert.expect( 35 );

	assert.ok( typeof FPO.identity == "function", "identity()" );
	assert.ok( typeof FPO.constant == "function", "constant()" );
	assert.ok( typeof FPO.pick == "function", "pick()" );
	assert.ok( typeof FPO.pickAll == "function", "pickAll()" );
	assert.ok( typeof FPO.nAry == "function", "nAry()" );
	assert.ok( typeof FPO.unary == "function", "unary()" );
	assert.ok( typeof FPO.binary == "function", "binary()" );
	assert.ok( typeof FPO.curry == "function", "curry()" );
	assert.ok( typeof FPO.curryMultiple == "function", "curryMultiple()" );
	assert.ok( typeof FPO.uncurry == "function", "uncurry()" );
	assert.ok( typeof FPO.partial == "function", "partial()" );
	assert.ok( typeof FPO.complement == "function", "complement()" );
	assert.ok( typeof FPO.apply == "function", "apply()" );
	assert.ok( typeof FPO.unapply == "function", "unapply()" );
	assert.ok( typeof FPO.compose == "function", "compose()" );
	assert.ok( typeof FPO.pipe == "function", "pipe()" );
	assert.ok( typeof FPO.prop == "function", "prop()" );
	assert.ok( typeof FPO.setProp == "function", "setProp()" );
	assert.ok( typeof FPO.filterIn == "function", "filterIn()" );
	assert.ok( typeof FPO.filterOut == "function", "filterOut()" );
	assert.ok( typeof FPO.map == "function", "map()" );
	assert.ok( typeof FPO.flatMap == "function", "flatMap()" );
	assert.ok( typeof FPO.reduce == "function", "reduce()" );
	assert.ok( typeof FPO.reduceRight == "function", "reduceRight()" );
	assert.ok( typeof FPO.flatten == "function", "flatten()" );
	assert.ok( typeof FPO.zip == "function", "zip()" );
	assert.ok( typeof FPO.trampoline == "function", "trampoline()" );
	assert.ok( FPO.transducers && typeof FPO.transducers == "object", "transducers" );
	assert.ok( typeof FPO.transducers.transduce == "function", "transducers.transduce()" );
	assert.ok( typeof FPO.transducers.into == "function", "transducers.into()" );
	assert.ok( typeof FPO.transducers.map == "function", "transducers.map()" );
	assert.ok( typeof FPO.transducers.filter == "function", "transducers.filter()" );
	assert.ok( typeof FPO.transducers.string == "function", "transducers.string()" );
	assert.ok( typeof FPO.transducers.array == "function", "transducers.array()" );
	assert.ok( typeof FPO.transducers.default == "function", "transducers.default()" );
});

QUnit.test("std API",function t2(assert){
	assert.expect( 35 );

	assert.ok( typeof FPO.std.identity == "function", "identity()" );
	assert.ok( typeof FPO.std.constant == "function", "constant()" );
	assert.ok( typeof FPO.std.pick == "function", "pick()" );
	assert.ok( typeof FPO.std.pickAll == "function", "pickAll()" );
	assert.ok( typeof FPO.std.nAry == "function", "nAry()" );
	assert.ok( typeof FPO.std.unary == "function", "unary()" );
	assert.ok( typeof FPO.std.binary == "function", "binary()" );
	assert.ok( typeof FPO.std.curry == "function", "curry()" );
	assert.ok( typeof FPO.std.curryMultiple == "function", "curryMultiple()" );
	assert.ok( typeof FPO.std.uncurry == "function", "uncurry()" );
	assert.ok( typeof FPO.std.partial == "function", "partial()" );
	assert.ok( typeof FPO.std.complement == "function", "complement()" );
	assert.ok( typeof FPO.std.apply == "function", "apply()" );
	assert.ok( typeof FPO.std.unapply == "function", "unapply()" );
	assert.ok( typeof FPO.std.compose == "function", "compose()" );
	assert.ok( typeof FPO.std.pipe == "function", "pipe()" );
	assert.ok( typeof FPO.std.prop == "function", "prop()" );
	assert.ok( typeof FPO.std.setProp == "function", "setProp()" );
	assert.ok( typeof FPO.std.filterIn == "function", "filterIn()" );
	assert.ok( typeof FPO.std.filterOut == "function", "filterOut()" );
	assert.ok( typeof FPO.std.map == "function", "map()" );
	assert.ok( typeof FPO.std.flatMap == "function", "flatMap()" );
	assert.ok( typeof FPO.std.reduce == "function", "reduce()" );
	assert.ok( typeof FPO.std.reduceRight == "function", "reduceRight()" );
	assert.ok( typeof FPO.std.flatten == "function", "flatten()" );
	assert.ok( typeof FPO.std.zip == "function", "zip()" );
	assert.ok( typeof FPO.std.trampoline == "function", "trampoline()" );
	assert.ok( FPO.std.transducers && typeof FPO.std.transducers == "object", "transducers" );
	assert.ok( typeof FPO.std.transducers.transduce == "function", "transducers.transduce()" );
	assert.ok( typeof FPO.std.transducers.into == "function", "transducers.into()" );
	assert.ok( typeof FPO.std.transducers.map == "function", "transducers.map()" );
	assert.ok( typeof FPO.std.transducers.filter == "function", "transducers.filter()" );
	assert.ok( typeof FPO.std.transducers.string == "function", "transducers.string()" );
	assert.ok( typeof FPO.std.transducers.array == "function", "transducers.array()" );
	assert.ok( typeof FPO.std.transducers.default == "function", "transducers.default()" );
});

QUnit.test("API aliases",function t3(assert){
	assert.expect( 11 );

	assert.ok( FPO.always === FPO.constant && FPO.std.always === FPO.std.constant, "always -> constant" );
	assert.ok( FPO.flow === FPO.pipe && FPO.std.flow === FPO.std.pipe, "flow -> pipe" );
	assert.ok( FPO.flowRight === FPO.compose && FPO.std.flowRight === FPO.std.compose, "flowRight -> compose" );
	assert.ok( FPO.spread === FPO.apply && FPO.std.spread === FPO.std.apply, "spread -> apply" );
	assert.ok( FPO.gather === FPO.unapply && FPO.std.gather === FPO.std.unapply, "gather -> unapply" );
	assert.ok( FPO.assoc === FPO.setProp && FPO.std.assoc === FPO.std.setProp, "assoc -> setProp" );
	assert.ok( FPO.filter === FPO.filterIn && FPO.std.filter === FPO.std.filterIn, "filter -> filterIn" );
	assert.ok( FPO.chain === FPO.flatMap && FPO.std.chain === FPO.std.flatMap, "chain -> flatMap" );
	assert.ok( FPO.fold === FPO.reduce && FPO.std.fold === FPO.std.reduce, "fold -> reduce" );
	assert.ok( FPO.foldL === FPO.reduce && FPO.std.foldL === FPO.std.reduce, "foldL -> reduce" );
	assert.ok( FPO.foldR === FPO.reduceRight && FPO.std.foldR === FPO.std.reduceRight, "foldR -> reduceRight" );
});

QUnit.test("identity()",function t4(assert){
	assert.expect( 6 );

	var v = FPO.identity( {v: 2} );
	assert.ok( v === 2, "core: v === 2" );

	v = FPO.identity()( {} )( { v: undefined } );
	assert.ok( v === undefined, "core: v === undefined" );

	v = FPO.identity()( {} )( {v: 3} );
	assert.ok( v === 3, "core: v === 3" );

	v = FPO.std.identity( 2 );
	assert.ok( v === 2, "std: v === 2" );

	v = FPO.std.identity()( undefined );
	assert.ok( v === undefined, "std: v === undefined" );

	v = FPO.std.identity()( 3 );
	assert.ok( v === 3, "std: v === 3" );
});

<commit_msg>adding more tests<commit_after>QUnit.test("core API methods present",function t1(assert){
	assert.expect( 35 );

	assert.ok( typeof FPO.identity == "function", "identity()" );
	assert.ok( typeof FPO.constant == "function", "constant()" );
	assert.ok( typeof FPO.pick == "function", "pick()" );
	assert.ok( typeof FPO.pickAll == "function", "pickAll()" );
	assert.ok( typeof FPO.nAry == "function", "nAry()" );
	assert.ok( typeof FPO.unary == "function", "unary()" );
	assert.ok( typeof FPO.binary == "function", "binary()" );
	assert.ok( typeof FPO.curry == "function", "curry()" );
	assert.ok( typeof FPO.curryMultiple == "function", "curryMultiple()" );
	assert.ok( typeof FPO.uncurry == "function", "uncurry()" );
	assert.ok( typeof FPO.partial == "function", "partial()" );
	assert.ok( typeof FPO.complement == "function", "complement()" );
	assert.ok( typeof FPO.apply == "function", "apply()" );
	assert.ok( typeof FPO.unapply == "function", "unapply()" );
	assert.ok( typeof FPO.compose == "function", "compose()" );
	assert.ok( typeof FPO.pipe == "function", "pipe()" );
	assert.ok( typeof FPO.prop == "function", "prop()" );
	assert.ok( typeof FPO.setProp == "function", "setProp()" );
	assert.ok( typeof FPO.filterIn == "function", "filterIn()" );
	assert.ok( typeof FPO.filterOut == "function", "filterOut()" );
	assert.ok( typeof FPO.map == "function", "map()" );
	assert.ok( typeof FPO.flatMap == "function", "flatMap()" );
	assert.ok( typeof FPO.reduce == "function", "reduce()" );
	assert.ok( typeof FPO.reduceRight == "function", "reduceRight()" );
	assert.ok( typeof FPO.flatten == "function", "flatten()" );
	assert.ok( typeof FPO.zip == "function", "zip()" );
	assert.ok( typeof FPO.trampoline == "function", "trampoline()" );
	assert.ok( FPO.transducers && typeof FPO.transducers == "object", "transducers" );
	assert.ok( typeof FPO.transducers.transduce == "function", "transducers.transduce()" );
	assert.ok( typeof FPO.transducers.into == "function", "transducers.into()" );
	assert.ok( typeof FPO.transducers.map == "function", "transducers.map()" );
	assert.ok( typeof FPO.transducers.filter == "function", "transducers.filter()" );
	assert.ok( typeof FPO.transducers.string == "function", "transducers.string()" );
	assert.ok( typeof FPO.transducers.array == "function", "transducers.array()" );
	assert.ok( typeof FPO.transducers.default == "function", "transducers.default()" );
});

QUnit.test("std API methods present",function t2(assert){
	assert.expect( 35 );

	assert.ok( typeof FPO.std.identity == "function", "identity()" );
	assert.ok( typeof FPO.std.constant == "function", "constant()" );
	assert.ok( typeof FPO.std.pick == "function", "pick()" );
	assert.ok( typeof FPO.std.pickAll == "function", "pickAll()" );
	assert.ok( typeof FPO.std.nAry == "function", "nAry()" );
	assert.ok( typeof FPO.std.unary == "function", "unary()" );
	assert.ok( typeof FPO.std.binary == "function", "binary()" );
	assert.ok( typeof FPO.std.curry == "function", "curry()" );
	assert.ok( typeof FPO.std.curryMultiple == "function", "curryMultiple()" );
	assert.ok( typeof FPO.std.uncurry == "function", "uncurry()" );
	assert.ok( typeof FPO.std.partial == "function", "partial()" );
	assert.ok( typeof FPO.std.complement == "function", "complement()" );
	assert.ok( typeof FPO.std.apply == "function", "apply()" );
	assert.ok( typeof FPO.std.unapply == "function", "unapply()" );
	assert.ok( typeof FPO.std.compose == "function", "compose()" );
	assert.ok( typeof FPO.std.pipe == "function", "pipe()" );
	assert.ok( typeof FPO.std.prop == "function", "prop()" );
	assert.ok( typeof FPO.std.setProp == "function", "setProp()" );
	assert.ok( typeof FPO.std.filterIn == "function", "filterIn()" );
	assert.ok( typeof FPO.std.filterOut == "function", "filterOut()" );
	assert.ok( typeof FPO.std.map == "function", "map()" );
	assert.ok( typeof FPO.std.flatMap == "function", "flatMap()" );
	assert.ok( typeof FPO.std.reduce == "function", "reduce()" );
	assert.ok( typeof FPO.std.reduceRight == "function", "reduceRight()" );
	assert.ok( typeof FPO.std.flatten == "function", "flatten()" );
	assert.ok( typeof FPO.std.zip == "function", "zip()" );
	assert.ok( typeof FPO.std.trampoline == "function", "trampoline()" );
	assert.ok( FPO.std.transducers && typeof FPO.std.transducers == "object", "transducers" );
	assert.ok( typeof FPO.std.transducers.transduce == "function", "transducers.transduce()" );
	assert.ok( typeof FPO.std.transducers.into == "function", "transducers.into()" );
	assert.ok( typeof FPO.std.transducers.map == "function", "transducers.map()" );
	assert.ok( typeof FPO.std.transducers.filter == "function", "transducers.filter()" );
	assert.ok( typeof FPO.std.transducers.string == "function", "transducers.string()" );
	assert.ok( typeof FPO.std.transducers.array == "function", "transducers.array()" );
	assert.ok( typeof FPO.std.transducers.default == "function", "transducers.default()" );
});

QUnit.test("API aliases present",function t3(assert){
	assert.expect( 11 );

	assert.ok( FPO.always === FPO.constant && FPO.std.always === FPO.std.constant, "always -> constant" );
	assert.ok( FPO.flow === FPO.pipe && FPO.std.flow === FPO.std.pipe, "flow -> pipe" );
	assert.ok( FPO.flowRight === FPO.compose && FPO.std.flowRight === FPO.std.compose, "flowRight -> compose" );
	assert.ok( FPO.spread === FPO.apply && FPO.std.spread === FPO.std.apply, "spread -> apply" );
	assert.ok( FPO.gather === FPO.unapply && FPO.std.gather === FPO.std.unapply, "gather -> unapply" );
	assert.ok( FPO.assoc === FPO.setProp && FPO.std.assoc === FPO.std.setProp, "assoc -> setProp" );
	assert.ok( FPO.filter === FPO.filterIn && FPO.std.filter === FPO.std.filterIn, "filter -> filterIn" );
	assert.ok( FPO.chain === FPO.flatMap && FPO.std.chain === FPO.std.flatMap, "chain -> flatMap" );
	assert.ok( FPO.fold === FPO.reduce && FPO.std.fold === FPO.std.reduce, "fold -> reduce" );
	assert.ok( FPO.foldL === FPO.reduce && FPO.std.foldL === FPO.std.reduce, "foldL -> reduce" );
	assert.ok( FPO.foldR === FPO.reduceRight && FPO.std.foldR === FPO.std.reduceRight, "foldR -> reduceRight" );
});

QUnit.test("identity()",function t4(assert){
	assert.expect( 6 );

	var v = FPO.identity( {v: 2} );
	assert.ok( v === 2, "core: v === 2" );

	v = FPO.identity()( {} )( { v: undefined } );
	assert.ok( v === undefined, "core: v === undefined" );

	v = FPO.identity()( {} )( {v: 3} );
	assert.ok( v === 3, "core: v === 3" );

	// **************************************

	v = FPO.std.identity( 2 );
	assert.ok( v === 2, "std: v === 2" );

	v = FPO.std.identity()( undefined );
	assert.ok( v === undefined, "std: v === undefined" );

	v = FPO.std.identity()( 3 );
	assert.ok( v === 3, "std: v === 3" );
});

QUnit.test("constant()",function t5(assert){
	assert.expect( 6 );

	var f = FPO.constant( {v: 2} );
	assert.ok( f() === 2, "core: f() === 2" );

	f = FPO.constant()( {} )( { v: undefined } );
	assert.ok( f() === undefined, "core: f() === undefined" );

	f = FPO.constant()( {} )( {v: 3} );
	assert.ok( f() === 3, "core: f() === 3" );

	// **************************************

	f = FPO.std.constant( 2 );
	assert.ok( f() === 2, "std: f() === 2" );

	f = FPO.std.constant()( undefined );
	assert.ok( f() === undefined, "std: f() === undefined" );

	f = FPO.std.constant()( 3 );
	assert.ok( f() === 3, "std: f() === 3" );
});

QUnit.test("pick()",function t6(assert){
	assert.expect( 18 );

	var obj = { x: 1, y: 2, z: 3, w: 4 };

	var o = FPO.pick( {v: obj, props: ["x","z","x","f"]} );
	assert.ok( o && typeof o == "object", "core: o is an object" );
	assert.ok(
		_hasProp( o, "x" ) && _hasProp( o, "z" ),
		"core: o has 'x' and 'z' properties"
	);
	assert.ok(
		!_hasProp( o, "y" ) && !_hasProp( o, "w" ) && !_hasProp( o, "f" ),
		"core: o does not have 'y', 'w', or 'f' properties"
	);
	assert.ok( Object.keys( o ).length === 2, "core: o has just 2 properties" );
	assert.ok( o.x === 1 && o.z === 3, "core: o.x === 1, o.z === 3" );

	var p = FPO.pick()( {} )( {v: obj} )( {props: ["y"]} );
	assert.ok( p && typeof p == "object", "core: p is an object" );
	assert.ok( _hasProp( p, "y" ), "core: p has 'y' property" );
	assert.ok( Object.keys( p ).length == 1, "core: p has only 1 property" );
	assert.ok( p.y === 2, "core: p.y === 2" );

	// **************************************

	o = FPO.std.pick( ["x","z","x","f"], o );
	assert.ok( o && typeof o == "object", "std: o is an object" );
	assert.ok(
		_hasProp( o, "x" ) && _hasProp( o, "z" ),
		"std: o has 'x' and 'z' properties"
	);
	assert.ok(
		!_hasProp( o, "y" ) && !_hasProp( o, "w" ) && !_hasProp( o, "f" ),
		"std: o does not have 'y', 'w', or 'f' properties"
	);
	assert.ok( Object.keys( o ).length == 2, "std: o has only 2 properties" );
	assert.ok( o.x === 1 && o.z === 3, "std: o.x === 1, o.z === 3" );

	p = FPO.std.pick()( ["y"] )( obj );
	assert.ok( p && typeof p == "object", "std: p is an object" );
	assert.ok( _hasProp( p, "y" ), "std: p has 'y' property" );
	assert.ok( Object.keys( p ).length == 1, "std: p has only 1 property" );
	assert.ok( p.y === 2, "std: p.y === 2" );
});

QUnit.test("pickAll()",function t6(assert){
	assert.expect( 18 );

	var obj = { x: 1, y: 2, z: 3, w: 4 };

	var o = FPO.pickAll( {v: obj, props: ["x","z","x","f"]} );
	assert.ok( o && typeof o == "object", "core: o is an object" );
	assert.ok(
		_hasProp( o, "x" ) && _hasProp( o, "z" ) && _hasProp( o, "f" ),
		"core: o has 'x', 'z', and 'f' properties"
	);
	assert.ok(
		!_hasProp( o, "y" ) && !_hasProp( o, "w" ),
		"core: o does not have 'y' and 'w' properties"
	);
	assert.ok( Object.keys( o ).length === 3, "core: o has just 3 properties" );
	assert.ok( o.x === 1 && o.z === 3 && o.f === undefined, "core: o.x === 1, o.z === 3, o.f === undefined" );

	var p = FPO.pickAll()( {} )( {v: obj} )( {props: ["y"]} );
	assert.ok( p && typeof p == "object", "core: p is an object" );
	assert.ok( _hasProp( p, "y" ), "core: p has 'y' property" );
	assert.ok( Object.keys( p ).length == 1, "core: p has only 1 property" );
	assert.ok( p.y === 2, "core: p.y === 2" );

	// **************************************

	o = FPO.std.pickAll( ["x","z","x","f"], o );
	assert.ok( o && typeof o == "object", "std: o is an object" );
	assert.ok(
		_hasProp( o, "x" ) && _hasProp( o, "z" ) && _hasProp( o, "f" ),
		"std: o has 'x', 'z', and 'f' properties"
	);
	assert.ok(
		!_hasProp( o, "y" ) && !_hasProp( o, "w" ),
		"std: o does not have 'y' and 'w' properties"
	);
	assert.ok( Object.keys( o ).length === 3, "std: o has just 3 properties" );
	assert.ok( o.x === 1 && o.z === 3 && o.f === undefined, "std: o.x === 1, o.z === 3, o.f === undefined" );

	p = FPO.std.pickAll()( ["y"] )( obj );
	assert.ok( p && typeof p == "object", "std: p is an object" );
	assert.ok( _hasProp( p, "y" ), "std: p has 'y' property" );
	assert.ok( Object.keys( p ).length == 1, "std: p has only 1 property" );
	assert.ok( p.y === 2, "std: p.y === 2" );
});

function _hasProp(obj,prop) {
	return Object.hasOwnProperty.call( obj, prop );
}
<|endoftext|>


<commit_before>// Copyright (C) 2012,2013 Colin Walters <walters@verbum.org>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.

const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Format = imports.format;

const GSystem = imports.gi.GSystem;

const Builtin = imports.builtin;
const SubTask = imports.subtask;
const JsonDB = imports.jsondb;
const ProcUtil = imports.procutil;
const JsonUtil = imports.jsonutil;
const Snapshot = imports.snapshot;
const Config = imports.config;

const loop = GLib.MainLoop.new(null, true);

var AutoBuilderIface = <interface name="org.gnome.OSTreeBuild.AutoBuilder">
<method name="queueResolve">
    <arg type="as" direction="in" />
</method>
<property name="Status" type="s" access="read" />
</interface>;

const Autobuilder = new Lang.Class({
    Name: 'Autobuilder',
    Extends: Builtin.Builtin,

    DESCRIPTION: "Automatically fetch git repositories and build",

    _init: function() {
	this.parent();

        this.parser.addArgument('--prefix');
        this.parser.addArgument('--autoupdate-self', { action: 'storeTrue' });

	this._build_needed = true;
	this._full_resolve_needed = true;
	this._queued_force_resolve = [];
	this._resolve_timeout = 0;
	this._source_snapshot_path = null;
	this._prev_source_snapshot_path = null;
    },

    execute: function(args, loop, cancellable) {
	this._initSnapshot(args.prefix, null, cancellable);

	this._autoupdate_self = args.autoupdate_self;

	this._status_path = this.workdir.get_child('autobuilder-' + this.prefix + '.json');
	this._manifestPath = Gio.File.new_for_path('manifest.json');

	this._ownId = Gio.DBus.session.own_name('org.gnome.OSTreeBuild', Gio.BusNameOwnerFlags.NONE,
						function(name) {},
						function(name) { loop.quit(); });

	this._impl = Gio.DBusExportedObject.wrapJSObject(AutoBuilderIface, this);
	this._impl.export(Gio.DBus.session, '/org/gnome/OSTreeBuild/AutoBuilder');

	this._snapshot_dir = this.workdir.get_child('snapshots').get_child(this.prefix);
	this._src_db = new JsonDB.JsonDB(this._snapshot_dir);

	let taskdir = this.workdir.get_child('tasks');
	this._resolve_taskset = new SubTask.TaskSet(taskdir.get_child(this.prefix + '-resolve'));
	this._build_taskset = new SubTask.TaskSet(taskdir.get_child(this.prefix + '-build'));

	this._source_snapshot_path = this._src_db.getLatestPath();

	this._status_path = this.workdir.get_child('autobuilder-' + this.prefix + '.json');

	this._resolve_timeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT,
							 60 * 10, Lang.bind(this, this._fetchAll));
	this._fetchAll();
	if (this._source_snapshot_path != null)
	    this._run_build();

	this._updateStatus();

	loop.run();
    },

    _updateStatus: function() {
	let newStatus = "";
	if (!this._resolve_taskset.isRunning() && !this._build_taskset.isRunning()) {
	    newStatus = "idle";
	} else {
	    if (this._resolve_taskset.isRunning())
		newStatus += "[resolving] ";
	    if (this._build_taskset.isRunning())
		newStatus += "[building] ";
	}
	if (newStatus != this._status) {
	    this._status = newStatus;
	    print(this._status);
	    this._impl.emit_property_changed('Status', new GLib.Variant("s", this._status));
	}

	this._writeStatusFile();
    },

    get Status() {
	return this._status;
    },

    queueResolve: function(srcUrls) {
	let matchingComponents = [];
	let snapshotData = this._src_db.loadFromPath(this._source_snapshot_path, null);
	let snapshot = new Snapshot.Snapshot(snapshotData, this._source_snapshot_path);
	for (let i = 0; i < srcUrls.length; i++) {
	    let matches = snapshot.getMatchingSrc(srcUrls[i]);
	    for (let j = 0; j < matches.length; j++)
		matchingComponents.push(matches[j]['name']);
	}
	if (matchingComponents.length > 0) {
	    this._queued_force_resolve.push.apply(this._queued_force_resolve, matchingComponents);
	    print("queued resolves: " + matchingComponents.join(' '));
	    if (!this._resolve_taskset.isRunning())
		this._fetch();
	} else {
	    print("Ignored fetch requests for unknown URLs: " + srcUrls.join(','));
	}
    },

    _fetchAll: function() {
	this._full_resolve_needed = true;
	if (!this._resolve_taskset.isRunning())
	    this._fetch();
	return true;
    },

    _fetch: function() {
	let cancellable = null;

	if (this._autoupdate_self)
	    ProcUtil.runSync(['git', 'pull', '-r'], cancellable)

	let args = ['ostbuild', 'resolve', '--manifest=manifest.json',
		    '--fetch', '--fetch-keep-going'];
	let isFull;
	if (this._full_resolve_needed) {
	    this._full_resolve_needed = false;
	    isFull = true;
	} else if (this._queued_force_resolve.length > 0) {
	    args.push.apply(args, this._queued_force_resolve);
	    isFull = false;
	} else {
	    throw new Error("_fetch() when not needed");
	}
	this._queued_force_resolve = [];
	let context = new GSystem.SubprocessContext({ argv: args });
	let workdir = this._resolve_taskset.prepare();
	let tmpManifest = workdir.get_child(this._manifestPath.get_basename());
	GSystem.file_linkcopy(this._manifestPath, tmpManifest, Gio.FileCopyFlags.OVERWRITE, cancellable);
	let t = this._resolve_taskset.start(context,
					    cancellable,
					    Lang.bind(this, this._onResolveExited));
	print(Format.vprintf("Resolve task %s.%s started (%s)", [t.major, t.minor,
								 isFull ? "full" : "incremental"]));

	this._updateStatus();

	return false;
    },

    _onResolveExited: function(resolveTask, success, msg) {
	print(Format.vprintf("resolve exited; success=%s msg=%s", [success, msg]))
	this._prev_source_snapshot_path = this._source_snapshot_path;
	this._source_snapshot_path = this._src_db.getLatestPath();
	let changed = (this._prev_source_snapshot_path == null ||
		       !this._prev_source_snapshot_path.equal(this._source_snapshot_path));
        if (changed)
            print(Format.vprintf("New version is %s", [this._source_snapshot_path.get_path()]))
	if (!this._build_needed)
	    this._build_needed = changed;
	if (this._build_needed && !this._build_taskset.isRunning())
	    this._run_build();

	if (this._full_resolve_needed || this._queued_force_resolve.length > 0) {
	    this._fetch();
	}

	this._updateStatus();
    },

    _run_build: function() {
	let cancellable = null;
	if (this._build_taskset.isRunning()) throw new Error();
	if (!this._build_needed) throw new Error();

	this._build_needed = false;

	let snapshotName = this._source_snapshot_path.get_basename();

	let workdir = this._build_taskset.prepare();
	let tmpSnapshotPath = workdir.get_child(snapshotName);
	GSystem.file_linkcopy(this._source_snapshot_path, tmpSnapshotPath,
			      Gio.FileCopyFlags.OVERWRITE, cancellable);

	let version = this._src_db.parseVersionStr(this._source_snapshot_path.get_basename());
	let meta = {'version': version,
		    'version-path': this._snapshot_dir.get_relative_path(this._source_snapshot_path)};
	let metaPath = workdir.get_child('meta.json');
	JsonUtil.writeJsonFileAtomic(metaPath, meta, cancellable);

	let args = ['ostbuild', 'build', '--snapshot=' + snapshotName];

	let context = new GSystem.SubprocessContext({ argv: args });
	let task = this._build_taskset.start(context,
					     cancellable,
					     Lang.bind(this, this._onBuildExited));
	print(Format.vprintf("Build task %s.%s started", [task.major, task.minor]));

	this._updateStatus();
    },

    _onBuildExited: function(buildTaskset, success, msg) {
	print(Format.vprintf("build exited; success=%s msg=%s", [success, msg]))
	if (this._build_needed)
	    this._run_build()

	this._updateStatus();
    },

    _getBuildDiffForTask: function(task) {
	let cancellable = null;
        if (task.build_diff != undefined)
            return task.build_diff;
        let metaPath = task.path.get_child('meta.json');
	if (!metaPath.query_exists(null)) {
	    task.build_diff = null;
	    return task.build_diff;
	}
	let meta = JsonUtil.loadJson(metaPath, cancellable);
        let snapshotPath = this._snapshot_dir.get_child(meta['version-path']);
        let prevSnapshotPath = this._src_db.getPreviousPath(snapshotPath);
        if (prevSnapshotPath == null) {
            task.build_diff = null;
        } else {
            task.build_diff = Snapshot.snapshotDiff(this._src_db.loadFromPath(snapshotPath, cancellable),
                                                    this._src_db.loadFromPath(prevSnapshotPath, cancellable));
	}
	return task.build_diff;
    },

    _buildHistoryToJson: function() {
	let cancellable = null;
        let history = this._build_taskset.getHistory();
	let l = history.length;
        let MAXITEMS = 5;
        let entries = [];
	for (let i = Math.max(l - MAXITEMS, 0); i >= 0 && i < l; i++) {
	    let item = history[i];
            let data = {v: Format.vprintf('%d.%d', [item.major, item.minor]),
			state: item.state,
			timestamp: item.timestamp};
            entries.push(data);
            let metaPath = item.path.get_child('meta.json');
            if (metaPath.query_exists(cancellable)) {
		data['meta'] = JsonUtil.loadJson(metaPath, cancellable);
	    }
            data['diff'] = this._getBuildDiffForTask(item);
	}
	return entries;
    },

    _writeStatusFile: function() {
	let cancellable = null;
        let status = {'prefix': this.prefix};
        if (this._source_snapshot_path != null) {
            let version = this._src_db.parseVersionStr(this._source_snapshot_path.get_basename());
            status['version'] = version;
            status['version-path'] = this._snapshot_dir.get_relative_path(this._source_snapshot_path);
        } else {
            status['version'] = '';
	}

        status['build'] = this._buildHistoryToJson();

        if (this._build_proc != null) {
	    let buildHistory = this._build_taskset.getHistory();
            let activeBuild = buildHistory[buildHistory.length-1];
	    let buildStatus = status['build'];
	    let activeBuildJson = buildStatus[buildStatus.length-1];
            let statusPath = activeBuild.path.get_child('status.json');
            if (statusPath.query_exists(null)) {
                activeBuildJson['build-status'] = JsonUtil.loadJson(statusPath);
	    }
	}

	JsonUtil.writeJsonFileAtomic(this._status_path, status, cancellable);
    }
});
<commit_msg>autobuilder: Run build-disks too<commit_after>// Copyright (C) 2012,2013 Colin Walters <walters@verbum.org>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.

const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Format = imports.format;

const GSystem = imports.gi.GSystem;

const Builtin = imports.builtin;
const SubTask = imports.subtask;
const JsonDB = imports.jsondb;
const ProcUtil = imports.procutil;
const JsonUtil = imports.jsonutil;
const Snapshot = imports.snapshot;
const Config = imports.config;

const loop = GLib.MainLoop.new(null, true);

var AutoBuilderIface = <interface name="org.gnome.OSTreeBuild.AutoBuilder">
<method name="queueResolve">
    <arg type="as" direction="in" />
</method>
<property name="Status" type="s" access="read" />
</interface>;

const Autobuilder = new Lang.Class({
    Name: 'Autobuilder',
    Extends: Builtin.Builtin,

    DESCRIPTION: "Automatically fetch git repositories and build",

    _init: function() {
	this.parent();

        this.parser.addArgument('--prefix');
        this.parser.addArgument('--autoupdate-self', { action: 'storeTrue' });

	this._build_needed = true;
	this._full_resolve_needed = true;
	this._queued_force_resolve = [];
	this._resolve_timeout = 0;
	this._source_snapshot_path = null;
	this._prev_source_snapshot_path = null;
    },

    execute: function(args, loop, cancellable) {
	this._initSnapshot(args.prefix, null, cancellable);

	this._autoupdate_self = args.autoupdate_self;

	this._status_path = this.workdir.get_child('autobuilder-' + this.prefix + '.json');
	this._manifestPath = Gio.File.new_for_path('manifest.json');

	this._ownId = Gio.DBus.session.own_name('org.gnome.OSTreeBuild', Gio.BusNameOwnerFlags.NONE,
						function(name) {},
						function(name) { loop.quit(); });

	this._impl = Gio.DBusExportedObject.wrapJSObject(AutoBuilderIface, this);
	this._impl.export(Gio.DBus.session, '/org/gnome/OSTreeBuild/AutoBuilder');

	this._snapshot_dir = this.workdir.get_child('snapshots').get_child(this.prefix);
	this._src_db = new JsonDB.JsonDB(this._snapshot_dir);

	let taskdir = this.workdir.get_child('tasks');
	this._resolve_taskset = new SubTask.TaskSet(taskdir.get_child(this.prefix + '-resolve'));
	this._build_taskset = new SubTask.TaskSet(taskdir.get_child(this.prefix + '-build'));
	this._builddisks_taskset = new SubTask.TaskSet(taskdir.get_child(this.prefix + '-build-disks'));

	this._source_snapshot_path = this._src_db.getLatestPath();

	this._status_path = this.workdir.get_child('autobuilder-' + this.prefix + '.json');

	this._resolve_timeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT,
							 60 * 10, Lang.bind(this, this._fetchAll));
	this._fetchAll();
	if (this._source_snapshot_path != null)
	    this._run_build();

	this._updateStatus();

	loop.run();
    },

    _updateStatus: function() {
	let newStatus = "";
	if (this._resolve_taskset.isRunning())
	    newStatus += "[resolving] ";
	if (this._build_taskset.isRunning())
	    newStatus += " [building] ";
	if (this._builddisks_taskset.isRunning())
	    newStatus += " [disks] ";
	if (newStatus == "")
	    newStatus = "[idle]";
	if (newStatus != this._status) {
	    this._status = newStatus;
	    print(this._status);
	    this._impl.emit_property_changed('Status', new GLib.Variant("s", this._status));
	}

	this._writeStatusFile();
    },

    get Status() {
	return this._status;
    },

    queueResolve: function(srcUrls) {
	let matchingComponents = [];
	let snapshotData = this._src_db.loadFromPath(this._source_snapshot_path, null);
	let snapshot = new Snapshot.Snapshot(snapshotData, this._source_snapshot_path);
	for (let i = 0; i < srcUrls.length; i++) {
	    let matches = snapshot.getMatchingSrc(srcUrls[i]);
	    for (let j = 0; j < matches.length; j++)
		matchingComponents.push(matches[j]['name']);
	}
	if (matchingComponents.length > 0) {
	    this._queued_force_resolve.push.apply(this._queued_force_resolve, matchingComponents);
	    print("queued resolves: " + matchingComponents.join(' '));
	    if (!this._resolve_taskset.isRunning())
		this._fetch();
	} else {
	    print("Ignored fetch requests for unknown URLs: " + srcUrls.join(','));
	}
    },

    _fetchAll: function() {
	this._full_resolve_needed = true;
	if (!this._resolve_taskset.isRunning())
	    this._fetch();
	return true;
    },

    _fetch: function() {
	let cancellable = null;

	if (this._autoupdate_self)
	    ProcUtil.runSync(['git', 'pull', '-r'], cancellable)

	let args = ['ostbuild', 'resolve', '--manifest=manifest.json',
		    '--fetch', '--fetch-keep-going'];
	let isFull;
	if (this._full_resolve_needed) {
	    this._full_resolve_needed = false;
	    isFull = true;
	} else if (this._queued_force_resolve.length > 0) {
	    args.push.apply(args, this._queued_force_resolve);
	    isFull = false;
	} else {
	    throw new Error("_fetch() when not needed");
	}
	this._queued_force_resolve = [];
	let context = new GSystem.SubprocessContext({ argv: args });
	let workdir = this._resolve_taskset.prepare();
	let tmpManifest = workdir.get_child(this._manifestPath.get_basename());
	GSystem.file_linkcopy(this._manifestPath, tmpManifest, Gio.FileCopyFlags.OVERWRITE, cancellable);
	let t = this._resolve_taskset.start(context,
					    cancellable,
					    Lang.bind(this, this._onResolveExited));
	print(Format.vprintf("Resolve task %s.%s started (%s)", [t.major, t.minor,
								 isFull ? "full" : "incremental"]));

	this._updateStatus();

	return false;
    },

    _onResolveExited: function(resolveTask, success, msg) {
	print(Format.vprintf("resolve exited; success=%s msg=%s", [success, msg]))
	this._prev_source_snapshot_path = this._source_snapshot_path;
	this._source_snapshot_path = this._src_db.getLatestPath();
	let changed = (this._prev_source_snapshot_path == null ||
		       !this._prev_source_snapshot_path.equal(this._source_snapshot_path));
        if (changed)
            print(Format.vprintf("New version is %s", [this._source_snapshot_path.get_path()]))
	if (!this._build_needed)
	    this._build_needed = changed;
	if (this._build_needed && !this._build_taskset.isRunning())
	    this._run_build();

	if (this._full_resolve_needed || this._queued_force_resolve.length > 0) {
	    this._fetch();
	}

	this._updateStatus();
    },

    _run_build: function() {
	let cancellable = null;
	if (this._build_taskset.isRunning()) throw new Error();
	if (!this._build_needed) throw new Error();

	this._build_needed = false;

	let snapshotName = this._source_snapshot_path.get_basename();

	let workdir = this._build_taskset.prepare();
	let tmpSnapshotPath = workdir.get_child(snapshotName);
	GSystem.file_linkcopy(this._source_snapshot_path, tmpSnapshotPath,
			      Gio.FileCopyFlags.OVERWRITE, cancellable);

	let version = this._src_db.parseVersionStr(this._source_snapshot_path.get_basename());
	let meta = {'version': version,
		    'version-path': this._snapshot_dir.get_relative_path(this._source_snapshot_path)};
	let metaPath = workdir.get_child('meta.json');
	JsonUtil.writeJsonFileAtomic(metaPath, meta, cancellable);

	let args = ['ostbuild', 'build', '--snapshot=' + snapshotName];

	let context = new GSystem.SubprocessContext({ argv: args });
	let task = this._build_taskset.start(context,
					     cancellable,
					     Lang.bind(this, this._onBuildExited));
	print(Format.vprintf("Build task %s.%s started", [task.major, task.minor]));

	this._updateStatus();
    },

    _run_builddisks: function() {
	let cancellable = null;
	if (this._builddisks_taskset.isRunning()) throw new Error();

	let args = ['ostbuild', 'build-disks'];

	let context = new GSystem.SubprocessContext({ argv: args });
	let task = this._builddisks_taskset.start(context,
						  cancellable,
						  Lang.bind(this, this._onBuildDisksExited));
	print(Format.vprintf("Builddisks task %s.%s started", [task.major, task.minor]));

	this._updateStatus();
    },

    _onBuildExited: function(buildTaskset, success, msg) {
	print(Format.vprintf("build exited; success=%s msg=%s", [success, msg]))
	if (this._build_needed)
	    this._run_build()
	if (success && !this._builddisks_taskset.isRunning())
	    this._run_builddisks();

	this._updateStatus();
    },

    _onBuildDisksExited: function(buildTaskset, success, msg) {
	print(Format.vprintf("builddisks exited; success=%s msg=%s", [success, msg]))
	this._updateStatus();
    },

    _getBuildDiffForTask: function(task) {
	let cancellable = null;
        if (task.build_diff != undefined)
            return task.build_diff;
        let metaPath = task.path.get_child('meta.json');
	if (!metaPath.query_exists(null)) {
	    task.build_diff = null;
	    return task.build_diff;
	}
	let meta = JsonUtil.loadJson(metaPath, cancellable);
        let snapshotPath = this._snapshot_dir.get_child(meta['version-path']);
        let prevSnapshotPath = this._src_db.getPreviousPath(snapshotPath);
        if (prevSnapshotPath == null) {
            task.build_diff = null;
        } else {
            task.build_diff = Snapshot.snapshotDiff(this._src_db.loadFromPath(snapshotPath, cancellable),
                                                    this._src_db.loadFromPath(prevSnapshotPath, cancellable));
	}
	return task.build_diff;
    },

    _buildHistoryToJson: function() {
	let cancellable = null;
        let history = this._build_taskset.getHistory();
	let l = history.length;
        let MAXITEMS = 5;
        let entries = [];
	for (let i = Math.max(l - MAXITEMS, 0); i >= 0 && i < l; i++) {
	    let item = history[i];
            let data = {v: Format.vprintf('%d.%d', [item.major, item.minor]),
			state: item.state,
			timestamp: item.timestamp};
            entries.push(data);
            let metaPath = item.path.get_child('meta.json');
            if (metaPath.query_exists(cancellable)) {
		data['meta'] = JsonUtil.loadJson(metaPath, cancellable);
	    }
            data['diff'] = this._getBuildDiffForTask(item);
	}
	return entries;
    },

    _writeStatusFile: function() {
	let cancellable = null;
        let status = {'prefix': this.prefix};
        if (this._source_snapshot_path != null) {
            let version = this._src_db.parseVersionStr(this._source_snapshot_path.get_basename());
            status['version'] = version;
            status['version-path'] = this._snapshot_dir.get_relative_path(this._source_snapshot_path);
        } else {
            status['version'] = '';
	}

        status['build'] = this._buildHistoryToJson();

        if (this._build_proc != null) {
	    let buildHistory = this._build_taskset.getHistory();
            let activeBuild = buildHistory[buildHistory.length-1];
	    let buildStatus = status['build'];
	    let activeBuildJson = buildStatus[buildStatus.length-1];
            let statusPath = activeBuild.path.get_child('status.json');
            if (statusPath.query_exists(null)) {
                activeBuildJson['build-status'] = JsonUtil.loadJson(statusPath);
	    }
	}

	JsonUtil.writeJsonFileAtomic(this._status_path, status, cancellable);
    }
});
<|endoftext|>


<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Bodies = require('./lib/factory/Bodies');
var Body = require('./lib/body/Body');
var Class = require('../../utils/Class');
var Components = require('./components');
var GetFastValue = require('../../utils/object/GetFastValue');
var HasValue = require('../../utils/object/HasValue');
var Vertices = require('./lib/geometry/Vertices');

/**
 * @classdesc
 * A wrapper around a Tile that provides access to a corresponding Matter body. A tile can only
 * have one Matter body associated with it. You can either pass in an existing Matter body for
 * the tile or allow the constructor to create the corresponding body for you. If the Tile has a
 * collision group (defined in Tiled), those shapes will be used to create the body. If not, the
 * tile's rectangle bounding box will be used.
 *
 * The corresponding body will be accessible on the Tile itself via Tile.physics.matterBody.
 *
 * Note: not all Tiled collision shapes are supported. See
 * Phaser.Physics.Matter.TileBody#setFromTileCollision for more information.
 *
 * @class TileBody
 * @memberof Phaser.Physics.Matter
 * @constructor
 * @since 3.0.0
 *
 * @extends Phaser.Physics.Matter.Components.Bounce
 * @extends Phaser.Physics.Matter.Components.Collision
 * @extends Phaser.Physics.Matter.Components.Friction
 * @extends Phaser.Physics.Matter.Components.Gravity
 * @extends Phaser.Physics.Matter.Components.Mass
 * @extends Phaser.Physics.Matter.Components.Sensor
 * @extends Phaser.Physics.Matter.Components.Sleep
 * @extends Phaser.Physics.Matter.Components.Static
 *
 * @param {Phaser.Physics.Matter.World} world - The Matter world instance this body belongs to.
 * @param {Phaser.Tilemaps.Tile} tile - The target tile that should have a Matter body.
 * @param {Phaser.Types.Physics.Matter.MatterTileOptions} [options] - Options to be used when creating the Matter body.
 */
var MatterTileBody = new Class({

    Mixins: [
        Components.Bounce,
        Components.Collision,
        Components.Friction,
        Components.Gravity,
        Components.Mass,
        Components.Sensor,
        Components.Sleep,
        Components.Static
    ],

    initialize:

    function MatterTileBody (world, tile, options)
    {
        /**
         * The tile object the body is associated with.
         *
         * @name Phaser.Physics.Matter.TileBody#tile
         * @type {Phaser.Tilemaps.Tile}
         * @since 3.0.0
         */
        this.tile = tile;

        /**
         * The Matter world the body exists within.
         *
         * @name Phaser.Physics.Matter.TileBody#world
         * @type {Phaser.Physics.Matter.World}
         * @since 3.0.0
         */
        this.world = world;

        // Install a reference to 'this' on the tile and ensure there can only be one matter body
        // associated with the tile
        if (tile.physics.matterBody)
        {
            tile.physics.matterBody.destroy();
        }

        tile.physics.matterBody = this;

        // Set the body either from an existing body (if provided), the shapes in the tileset
        // collision layer (if it exists) or a rectangle matching the tile.
        var body = GetFastValue(options, 'body', null);

        var addToWorld = GetFastValue(options, 'addToWorld', true);

        if (!body)
        {
            var collisionGroup = tile.getCollisionGroup();
            var collisionObjects = GetFastValue(collisionGroup, 'objects', []);

            if (collisionObjects.length > 0)
            {
                this.setFromTileCollision(options);
            }
            else
            {
                this.setFromTileRectangle(options);
            }
        }
        else
        {
            this.setBody(body, addToWorld);
        }
    },

    /**
     * Sets the current body to a rectangle that matches the bounds of the tile.
     *
     * @method Phaser.Physics.Matter.TileBody#setFromTileRectangle
     * @since 3.0.0
     *
     * @param {Phaser.Types.Physics.Matter.MatterBodyTileOptions} [options] - Options to be used when creating the Matter body. See MatterJS.Body for a list of what Matter accepts.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setFromTileRectangle: function (options)
    {
        if (options === undefined) { options = {}; }
        if (!HasValue(options, 'isStatic')) { options.isStatic = true; }
        if (!HasValue(options, 'addToWorld')) { options.addToWorld = true; }

        var bounds = this.tile.getBounds();
        var cx = bounds.x + (bounds.width / 2);
        var cy = bounds.y + (bounds.height / 2);
        var body = Bodies.rectangle(cx, cy, bounds.width, bounds.height, options);

        this.setBody(body, options.addToWorld);

        return this;
    },

    /**
     * Sets the current body from the collision group associated with the Tile. This is typically
     * set up in Tiled's collision editor.
     *
     * Note: Matter doesn't support all shapes from Tiled. Rectangles and polygons are directly
     * supported. Ellipses are converted into circle bodies. Polylines are treated as if they are
     * closed polygons. If a tile has multiple shapes, a multi-part body will be created. Concave
     * shapes are supported if poly-decomp library is included. Decomposition is not guaranteed to
     * work for complex shapes (e.g. holes), so it's often best to manually decompose a concave
     * polygon into multiple convex polygons yourself.
     *
     * @method Phaser.Physics.Matter.TileBody#setFromTileCollision
     * @since 3.0.0
     *
     * @param {Phaser.Types.Physics.Matter.MatterBodyTileOptions} [options] - Options to be used when creating the Matter body. See MatterJS.Body for a list of what Matter accepts.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setFromTileCollision: function (options)
    {
        if (options === undefined) { options = {}; }
        if (!HasValue(options, 'isStatic')) { options.isStatic = true; }
        if (!HasValue(options, 'addToWorld')) { options.addToWorld = true; }

        var sx = this.tile.tilemapLayer.scaleX;
        var sy = this.tile.tilemapLayer.scaleY;
        var tileX = this.tile.getLeft();
        var tileY = this.tile.getTop();
        var collisionGroup = this.tile.getCollisionGroup();
        var collisionObjects = GetFastValue(collisionGroup, 'objects', []);

        var parts = [];

        for (var i = 0; i < collisionObjects.length; i++)
        {
            var object = collisionObjects[i];
            var ox = tileX + (object.x * sx);
            var oy = tileY + (object.y * sy);
            var ow = object.width * sx;
            var oh = object.height * sy;
            var body = null;

            if (object.rectangle)
            {
                body = Bodies.rectangle(ox + ow / 2, oy + oh / 2, ow, oh, options);
            }
            else if (object.ellipse)
            {
                body = Bodies.circle(ox + ow / 2, oy + oh / 2, ow / 2, options);
            }
            else if (object.polygon || object.polyline)
            {
                // Polygons and polylines are both treated as closed polygons
                var originalPoints = object.polygon ? object.polygon : object.polyline;

                var points = originalPoints.map(function (p)
                {
                    return { x: p.x * sx, y: p.y * sy };
                });

                var vertices = Vertices.create(points);

                // Points are relative to the object's origin (first point placed in Tiled), but
                // matter expects points to be relative to the center of mass. This only applies to
                // convex shapes. When a concave shape is decomposed, multiple parts are created and
                // the individual parts are positioned relative to (ox, oy).
                //
                //  Update: 8th January 2019 - the latest version of Matter needs the Vertices adjusted,
                //  regardless if convex or concave.

                var center = Vertices.centre(vertices);

                ox += center.x;
                oy += center.y;

                body = Bodies.fromVertices(ox, oy, vertices, options);
            }

            if (body)
            {
                parts.push(body);
            }
        }

        if (parts.length === 1)
        {
            this.setBody(parts[0], options.addToWorld);
        }
        else if (parts.length > 1)
        {
            options.parts = parts;
            this.setBody(Body.create(options), options.addToWorld);
        }

        return this;
    },

    /**
     * Sets the current body to the given body. This will remove the previous body, if one already
     * exists.
     *
     * @method Phaser.Physics.Matter.TileBody#setBody
     * @since 3.0.0
     *
     * @param {MatterJS.BodyType} body - The new Matter body to use.
     * @param {boolean} [addToWorld=true] - Whether or not to add the body to the Matter world.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setBody: function (body, addToWorld)
    {
        if (addToWorld === undefined) { addToWorld = true; }

        if (this.body)
        {
            this.removeBody();
        }

        this.body = body;
        this.body.gameObject = this;

        if (addToWorld)
        {
            this.world.add(this.body);
        }

        return this;
    },

    /**
     * Removes the current body from the TileBody and from the Matter world
     *
     * @method Phaser.Physics.Matter.TileBody#removeBody
     * @since 3.0.0
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    removeBody: function ()
    {
        if (this.body)
        {
            this.world.remove(this.body);
            this.body.gameObject = undefined;
            this.body = undefined;
        }

        return this;
    },

    /**
     * Removes the current body from the tile and the world.
     *
     * @method Phaser.Physics.Matter.TileBody#destroy
     * @since 3.0.0
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    destroy: function ()
    {
        this.removeBody();
        this.tile.physics.matterBody = undefined;
    }

});

module.exports = MatterTileBody;
<commit_msg>`MatterTileBody` now extends `EventEmitter`, meaning you can listen to collision events from Tiles directly and it will no longer throw errors about `gameObject.emit` not working. Fix #4967<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Bodies = require('./lib/factory/Bodies');
var Body = require('./lib/body/Body');
var Class = require('../../utils/Class');
var Components = require('./components');
var EventEmitter = require('eventemitter3');
var GetFastValue = require('../../utils/object/GetFastValue');
var HasValue = require('../../utils/object/HasValue');
var Vertices = require('./lib/geometry/Vertices');

/**
 * @classdesc
 * A wrapper around a Tile that provides access to a corresponding Matter body. A tile can only
 * have one Matter body associated with it. You can either pass in an existing Matter body for
 * the tile or allow the constructor to create the corresponding body for you. If the Tile has a
 * collision group (defined in Tiled), those shapes will be used to create the body. If not, the
 * tile's rectangle bounding box will be used.
 *
 * The corresponding body will be accessible on the Tile itself via Tile.physics.matterBody.
 *
 * Note: not all Tiled collision shapes are supported. See
 * Phaser.Physics.Matter.TileBody#setFromTileCollision for more information.
 *
 * @class TileBody
 * @memberof Phaser.Physics.Matter
 * @extends Phaser.Events.EventEmitter
 * @constructor
 * @since 3.0.0
 *
 * @extends Phaser.Physics.Matter.Components.Bounce
 * @extends Phaser.Physics.Matter.Components.Collision
 * @extends Phaser.Physics.Matter.Components.Friction
 * @extends Phaser.Physics.Matter.Components.Gravity
 * @extends Phaser.Physics.Matter.Components.Mass
 * @extends Phaser.Physics.Matter.Components.Sensor
 * @extends Phaser.Physics.Matter.Components.Sleep
 * @extends Phaser.Physics.Matter.Components.Static
 *
 * @param {Phaser.Physics.Matter.World} world - The Matter world instance this body belongs to.
 * @param {Phaser.Tilemaps.Tile} tile - The target tile that should have a Matter body.
 * @param {Phaser.Types.Physics.Matter.MatterTileOptions} [options] - Options to be used when creating the Matter body.
 */
var MatterTileBody = new Class({

    Extends: EventEmitter,

    Mixins: [
        Components.Bounce,
        Components.Collision,
        Components.Friction,
        Components.Gravity,
        Components.Mass,
        Components.Sensor,
        Components.Sleep,
        Components.Static
    ],

    initialize:

    function MatterTileBody (world, tile, options)
    {
        EventEmitter.call(this);

        /**
         * The tile object the body is associated with.
         *
         * @name Phaser.Physics.Matter.TileBody#tile
         * @type {Phaser.Tilemaps.Tile}
         * @since 3.0.0
         */
        this.tile = tile;

        /**
         * The Matter world the body exists within.
         *
         * @name Phaser.Physics.Matter.TileBody#world
         * @type {Phaser.Physics.Matter.World}
         * @since 3.0.0
         */
        this.world = world;

        // Install a reference to 'this' on the tile and ensure there can only be one matter body
        // associated with the tile
        if (tile.physics.matterBody)
        {
            tile.physics.matterBody.destroy();
        }

        tile.physics.matterBody = this;

        // Set the body either from an existing body (if provided), the shapes in the tileset
        // collision layer (if it exists) or a rectangle matching the tile.
        var body = GetFastValue(options, 'body', null);

        var addToWorld = GetFastValue(options, 'addToWorld', true);

        if (!body)
        {
            var collisionGroup = tile.getCollisionGroup();
            var collisionObjects = GetFastValue(collisionGroup, 'objects', []);

            if (collisionObjects.length > 0)
            {
                this.setFromTileCollision(options);
            }
            else
            {
                this.setFromTileRectangle(options);
            }
        }
        else
        {
            this.setBody(body, addToWorld);
        }
    },

    /**
     * Sets the current body to a rectangle that matches the bounds of the tile.
     *
     * @method Phaser.Physics.Matter.TileBody#setFromTileRectangle
     * @since 3.0.0
     *
     * @param {Phaser.Types.Physics.Matter.MatterBodyTileOptions} [options] - Options to be used when creating the Matter body. See MatterJS.Body for a list of what Matter accepts.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setFromTileRectangle: function (options)
    {
        if (options === undefined) { options = {}; }
        if (!HasValue(options, 'isStatic')) { options.isStatic = true; }
        if (!HasValue(options, 'addToWorld')) { options.addToWorld = true; }

        var bounds = this.tile.getBounds();
        var cx = bounds.x + (bounds.width / 2);
        var cy = bounds.y + (bounds.height / 2);
        var body = Bodies.rectangle(cx, cy, bounds.width, bounds.height, options);

        this.setBody(body, options.addToWorld);

        return this;
    },

    /**
     * Sets the current body from the collision group associated with the Tile. This is typically
     * set up in Tiled's collision editor.
     *
     * Note: Matter doesn't support all shapes from Tiled. Rectangles and polygons are directly
     * supported. Ellipses are converted into circle bodies. Polylines are treated as if they are
     * closed polygons. If a tile has multiple shapes, a multi-part body will be created. Concave
     * shapes are supported if poly-decomp library is included. Decomposition is not guaranteed to
     * work for complex shapes (e.g. holes), so it's often best to manually decompose a concave
     * polygon into multiple convex polygons yourself.
     *
     * @method Phaser.Physics.Matter.TileBody#setFromTileCollision
     * @since 3.0.0
     *
     * @param {Phaser.Types.Physics.Matter.MatterBodyTileOptions} [options] - Options to be used when creating the Matter body. See MatterJS.Body for a list of what Matter accepts.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setFromTileCollision: function (options)
    {
        if (options === undefined) { options = {}; }
        if (!HasValue(options, 'isStatic')) { options.isStatic = true; }
        if (!HasValue(options, 'addToWorld')) { options.addToWorld = true; }

        var sx = this.tile.tilemapLayer.scaleX;
        var sy = this.tile.tilemapLayer.scaleY;
        var tileX = this.tile.getLeft();
        var tileY = this.tile.getTop();
        var collisionGroup = this.tile.getCollisionGroup();
        var collisionObjects = GetFastValue(collisionGroup, 'objects', []);

        var parts = [];

        for (var i = 0; i < collisionObjects.length; i++)
        {
            var object = collisionObjects[i];
            var ox = tileX + (object.x * sx);
            var oy = tileY + (object.y * sy);
            var ow = object.width * sx;
            var oh = object.height * sy;
            var body = null;

            if (object.rectangle)
            {
                body = Bodies.rectangle(ox + ow / 2, oy + oh / 2, ow, oh, options);
            }
            else if (object.ellipse)
            {
                body = Bodies.circle(ox + ow / 2, oy + oh / 2, ow / 2, options);
            }
            else if (object.polygon || object.polyline)
            {
                // Polygons and polylines are both treated as closed polygons
                var originalPoints = object.polygon ? object.polygon : object.polyline;

                var points = originalPoints.map(function (p)
                {
                    return { x: p.x * sx, y: p.y * sy };
                });

                var vertices = Vertices.create(points);

                // Points are relative to the object's origin (first point placed in Tiled), but
                // matter expects points to be relative to the center of mass. This only applies to
                // convex shapes. When a concave shape is decomposed, multiple parts are created and
                // the individual parts are positioned relative to (ox, oy).
                //
                //  Update: 8th January 2019 - the latest version of Matter needs the Vertices adjusted,
                //  regardless if convex or concave.

                var center = Vertices.centre(vertices);

                ox += center.x;
                oy += center.y;

                body = Bodies.fromVertices(ox, oy, vertices, options);
            }

            if (body)
            {
                parts.push(body);
            }
        }

        if (parts.length === 1)
        {
            this.setBody(parts[0], options.addToWorld);
        }
        else if (parts.length > 1)
        {
            options.parts = parts;
            this.setBody(Body.create(options), options.addToWorld);
        }

        return this;
    },

    /**
     * Sets the current body to the given body. This will remove the previous body, if one already
     * exists.
     *
     * @method Phaser.Physics.Matter.TileBody#setBody
     * @since 3.0.0
     *
     * @param {MatterJS.BodyType} body - The new Matter body to use.
     * @param {boolean} [addToWorld=true] - Whether or not to add the body to the Matter world.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setBody: function (body, addToWorld)
    {
        if (addToWorld === undefined) { addToWorld = true; }

        if (this.body)
        {
            this.removeBody();
        }

        this.body = body;
        this.body.gameObject = this;

        if (addToWorld)
        {
            this.world.add(this.body);
        }

        return this;
    },

    /**
     * Removes the current body from the TileBody and from the Matter world
     *
     * @method Phaser.Physics.Matter.TileBody#removeBody
     * @since 3.0.0
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    removeBody: function ()
    {
        if (this.body)
        {
            this.world.remove(this.body);
            this.body.gameObject = undefined;
            this.body = undefined;
        }

        return this;
    },

    /**
     * Removes the current body from the tile and the world.
     *
     * @method Phaser.Physics.Matter.TileBody#destroy
     * @since 3.0.0
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    destroy: function ()
    {
        this.removeBody();
        this.tile.physics.matterBody = undefined;
    }

});

module.exports = MatterTileBody;
<|endoftext|>