<commit_before>'use strict';

var paginationVars = {
    'currentPage': 1,
    'numberPerPage': 30
};

function Pagination(dateTimeValues, paginationVars) {
    this.paginationVars = paginationVars;
    this.dateTimeValues = dateTimeValues;
    this.result = [];
    this.begin = null;
    this.end = null;
    this.numPages = null;
    this.calculate = function () {
        this.numPages = Math.ceil(this.dateTimeValues.length / paginationVars.numberPerPage);
        this.begin = ((this.paginationVars.currentPage - 1) * this.paginationVars.numberPerPage);
        this.end = this.begin + this.paginationVars.numberPerPage;
        this.result = this.dateTimeValues.slice(this.begin, this.end);
    }
}

function Column(format) {
    this.format = format;
    this.dateTimeValues = [];
    this.pagination = new Pagination(this.dateTimeValues, paginationVars);
    this.applied = false;
}

function DateTimeValue(value, timestamp, conflict) {
    this.value = value;
    this.timestamp = timestamp;
    this.conflict = conflict;
}

function DataModel(projectId, cellIndex) {
    this.projectId = projectId;
    this.cellIndex = cellIndex;
    this.originalColumn = null;
    this.resultColumn = new Column(null);
    this.formatColumns = [];
    this.predefinedFormats = ['dd-MM-yyyy', 'MM-dd-yyyy', 'dd/MM/yy', 'MM/dd/yy', 'dd/MM', 'dd.MM.yyyy hh:mm:ss', 'dd.MM.yyyy hh:mm'];
    this.inputFormatToAdd = null;

    this.init = function (dataModel, addInputFormat, scope) {
        this.originalColumn.dateTimeValues.forEach(function (item, index) {
            dataModel.resultColumn.dateTimeValues[index] = new DateTimeValue(null, null, true);
        });
        dataModel.predefinedFormats.forEach(function (item) {
            addInputFormat(dataModel, item, scope);
        });
        this.paginate();
        //});
    };

    this.findConflicts = function (reformatResultColumn) {
        var i;
        var valueCount;
        for (i = 0; i < this.originalColumn.dateTimeValues.length; i++) {
            valueCount = 0;
            this.formatColumns.forEach(function (item) {
                if (item.dateTimeValues[i].value) {
                    valueCount++;
                    if (item.applied) {
                        this.originalColumn.dateTimeValues[i].conflict = false;
                        this.resultColumn.dateTimeValues[i] = new DateTimeValue(item.dateTimeValues[i].value, item.dateTimeValues[i].timestamp, false);
                        this.formatColumns.forEach(function (item) {
                            item.dateTimeValues[i].conflict = false;
                        });
                    }
                }
            }.bind(this));

            if (valueCount == 1) {
                this.formatColumns.forEach(function (item) {
                    if (item.dateTimeValues[i].value && !item.applied) {
                        this.applyColumn(item, reformatResultColumn);
                    }
                }.bind(this));
            } else {
                this.formatColumns.forEach(function (item) {
                    if (item.dateTimeValues[i].value) {
                        item.dateTimeValues[i].conflict = true;
                    }
                    item.applied = false;
                }.bind(this));
            }
            //if (valueCount != 1) {
            //    this.originalColumn.dateTimeValues[i].conflict = true;
            //    this.resultColumn.dateTimeValues[i] = new DateTimeValue(null, null, true);
            //    this.formatColumns.forEach(function (item) {
            //        item.dateTimeValues[i].conflict = true;
            //    });
            //} else {
            //    this.originalColumn.dateTimeValues[i].conflict = false;
            //    this.resultColumn.dateTimeValues[i] = new DateTimeValue(selectedValue.value, selectedValue.timestamp, false);
            //    this.formatColumns.forEach(function (item) {
            //        item.dateTimeValues[i].conflict = false;
            //    });
            //}
        }
        //reformatResultColumn(this);
        this.paginate();
    };

    this.paginate = function () {
        this.formatColumns.forEach(function (item) {
            item.pagination.calculate();
        });
        this.originalColumn.pagination.calculate();
        this.resultColumn.pagination.calculate();
    };

    this.applyColumn = function (columnToApply, reformatResultColumn) {
        //if (!columnToApply.applied) {
        columnToApply.applied = true;
        var conflictColumns = [];
        this.formatColumns.forEach(function (columnItem, index) {
            var conflict = false;
            if (columnItem != columnToApply) {
                columnItem.dateTimeValues.forEach(function (item, index) {
                    if (columnToApply.dateTimeValues[index].value && item.value) {
                        conflict = true;
                    }
                });
            }
            if (conflict) {
                conflictColumns.push(columnItem);
                //conflictColumns.push(columnToApply);
            }
        });
        conflictColumns.forEach(function (conflictItem) {
            conflictItem.applied = false;
        });
        columnToApply.dateTimeValues.forEach(function (item, index) {
            if (item.value) {
                this.resultColumn.dateTimeValues[index] = new DateTimeValue(item.value, item.timestamp, false);
            }
        }.bind(this));
        reformatResultColumn(this);
    };

    //};
}

function ServiceMethods(http) {
    this.http = http;
    this.getColumn = function (dataModel, scope) {
        this.http.get('/command/timebench-extension/get-column?project=' + dataModel.projectId + '&cellIndex=' + dataModel.cellIndex).success(function (dateTimeValues) {
            console.log('/command/timebench-extension/get-column?project=' + dataModel.projectId + '&cellIndex=' + dataModel.cellIndex);
            var column = new Column(null);
            dateTimeValues.forEach(function (entry) {
                var dateTimeValue = new DateTimeValue(entry.v, null, false);
                column.dateTimeValues.push(dateTimeValue);
            });
            dataModel.originalColumn = column;
            dataModel.init(dataModel, this.addInputFormat.bind(this), scope);
            dataModel.paginate();
        }.bind(this))
    };

    this.addInputFormat = function (dataModel, format, scope) {
        var column;
        if (format) {
            column = new Column(format);
        } else {
            column = new Column(dataModel.inputFormatToAdd);
        }
        scope.wait=true;
        http.post('/command/timebench-extension/apply-input-format?cellIndex=' + dataModel.cellIndex + '&project=' + dataModel.projectId, column.format).success(function (dateTimeValueColumns) {
            console.log('/command/timebench-extension/apply-input-format?cellIndex=' + dataModel.cellIndex + '&project=' + dataModel.projectId);
            if (dateTimeValueColumns.length > 0) {
                dateTimeValueColumns[0].reformatedColumn.forEach(function (entry) {
                    column.dateTimeValues.push(new DateTimeValue(entry.v, entry.timestamp, false));
                });
                dataModel.formatColumns.push(column);
                dataModel.findConflicts(this.reformatResultColumn);
                this.reformatResultColumn(dataModel);
                dataModel.paginate();
            }
            scope.wait=false;
        }.bind(this));
        console.log(dataModel);

    };

    this.removeInputFormat = function (format, dataModel) {
        console.info('remove input format');
        dataModel.formatColumns.forEach(function (item, index, object) {
            if (item.format === format) {
                object.splice(index, 1);
            }
        });
        dataModel.findConflicts(this.reformatResultColumn);
        this.reformatResultColumn(dataModel);
        dataModel.paginate();
    };

    this.reformatResultColumn = function (dataModel) {
        http.post('/command/timebench-extension/apply-output-format', dataModel.resultColumn).success(function (resultColumn) {
            console.log('/command/timebench-extension/apply-output-format', dataModel.resultColumn);
            var column = new Column(resultColumn.format);
            resultColumn.dateTimeValues.forEach(function (item) {
                column.dateTimeValues.push(new DateTimeValue(item.value, item.timestamp, false));
            });
            dataModel.resultColumn = column;
            dataModel.paginate();
        });
    };

    this.applyToDataModel = function (dataModel) {
        http.post('/command/timebench-extension/apply-reformation-to-datamodel?project=' + dataModel.projectId + '&cellIndex=' + dataModel.cellIndex, dataModel.resultColumn).success(function (resultColumn) {
            console.log('/command/timebench-extension/apply-reformation-to-datamodel?project=' + dataModel.projectId);
            window.location = '/project?project=' + theProject.id;
        });
    }
}

var timeBenchExtensionApp = angular.module('timebenchExtension', ['ui.bootstrap']);

timeBenchExtensionApp.controller('timebenchExtensionCtrl', function ($scope, $http) {

    var dataModel = new DataModel(theProject.id, theProject.cellIndex);
    var serviceMethods = new ServiceMethods($http);
    serviceMethods.getColumn(dataModel, $scope);
    $scope.dataModel = dataModel;
    $scope.serviceMethods = serviceMethods;
});

<commit_msg>fix bug if input format is removed<commit_after>'use strict';

var paginationVars = {
    'currentPage': 1,
    'numberPerPage': 30
};

function Pagination(dateTimeValues, paginationVars) {
    this.paginationVars = paginationVars;
    this.dateTimeValues = dateTimeValues;
    this.result = [];
    this.begin = null;
    this.end = null;
    this.numPages = null;
    this.calculate = function () {
        this.numPages = Math.ceil(this.dateTimeValues.length / paginationVars.numberPerPage);
        this.begin = ((this.paginationVars.currentPage - 1) * this.paginationVars.numberPerPage);
        this.end = this.begin + this.paginationVars.numberPerPage;
        this.result = this.dateTimeValues.slice(this.begin, this.end);
    }
}

function Column(format) {
    this.format = format;
    this.dateTimeValues = [];
    this.pagination = new Pagination(this.dateTimeValues, paginationVars);
    this.applied = false;
}

function DateTimeValue(value, timestamp, conflict) {
    this.value = value;
    this.timestamp = timestamp;
    this.conflict = conflict;
}

function DataModel(projectId, cellIndex) {
    this.projectId = projectId;
    this.cellIndex = cellIndex;
    this.originalColumn = null;
    this.resultColumn = new Column(null);
    this.formatColumns = [];
    this.predefinedFormats = ['dd-MM-yyyy', 'MM-dd-yyyy', 'dd/MM/yy', 'MM/dd/yy', 'dd/MM', 'dd.MM.yyyy hh:mm:ss', 'dd.MM.yyyy hh:mm'];
    this.inputFormatToAdd = null;

    this.init = function (dataModel, addInputFormat, scope) {
        this.originalColumn.dateTimeValues.forEach(function (item, index) {
            dataModel.resultColumn.dateTimeValues[index] = new DateTimeValue(null, null, true);
        });
        dataModel.predefinedFormats.forEach(function (item) {
            addInputFormat(dataModel, item, scope);
        });
        this.paginate();
    };

    this.findConflicts = function (reformatResultColumn) {
        var i;
        var valueCountList = [];
        for (i = 0; i < this.originalColumn.dateTimeValues.length; i++) {
            var valueCount = 0;
            this.formatColumns.forEach(function (item) {
                if (item.dateTimeValues[i].value) {
                    valueCount++;
                    if (item.applied) {
                        this.originalColumn.dateTimeValues[i].conflict = false;
                        this.resultColumn.dateTimeValues[i] = new DateTimeValue(item.dateTimeValues[i].value, item.dateTimeValues[i].timestamp, false);
                        this.formatColumns.forEach(function (item) {
                            item.dateTimeValues[i].conflict = false;
                        });
                    }
                }
            }.bind(this));
            valueCountList.push(valueCount);

            if (valueCount > 1) {
                this.formatColumns.forEach(function (item) {
                    if (item.dateTimeValues[i].value) {
                        item.dateTimeValues[i].conflict = true;
                    }
                }.bind(this));
            }

        }
        reformatResultColumn(this);
        this.paginate();
    };

    this.paginate = function () {
        this.formatColumns.forEach(function (item) {
            item.pagination.calculate();
        });
        this.originalColumn.pagination.calculate();
        this.resultColumn.pagination.calculate();
    };

    this.applyColumn = function (columnToApply, reformatResultColumn) {
        //if (!columnToApply.applied) {
        columnToApply.applied = true;
        var conflictColumns = [];
        this.formatColumns.forEach(function (columnItem, index) {
            var conflict = false;
            if (columnItem != columnToApply) {
                columnItem.dateTimeValues.forEach(function (item, index) {
                    if (columnToApply.dateTimeValues[index].value && item.value) {
                        conflict = true;
                    }
                });
            }
            if (conflict) {
                conflictColumns.push(columnItem);
                //conflictColumns.push(columnToApply);
            }
        });
        conflictColumns.forEach(function (conflictItem) {
            conflictItem.applied = false;
        });
        columnToApply.dateTimeValues.forEach(function (item, index) {
            if (item.value) {
                this.resultColumn.dateTimeValues[index] = new DateTimeValue(item.value, item.timestamp, false);
            }
        }.bind(this));
        reformatResultColumn(this);
    };
}

function ServiceMethods(http) {
    this.http = http;
    this.getColumn = function (dataModel, scope) {
        this.http.get('/command/timebench-extension/get-column?project=' + dataModel.projectId + '&cellIndex=' + dataModel.cellIndex).success(function (dateTimeValues) {
            console.log('/command/timebench-extension/get-column?project=' + dataModel.projectId + '&cellIndex=' + dataModel.cellIndex);
            var column = new Column(null);
            dateTimeValues.forEach(function (entry) {
                var dateTimeValue = new DateTimeValue(entry.v, null, false);
                column.dateTimeValues.push(dateTimeValue);
            });
            dataModel.originalColumn = column;
            dataModel.init(dataModel, this.addInputFormat.bind(this), scope);
            dataModel.paginate();
        }.bind(this))
    };

    this.addInputFormat = function (dataModel, format, scope) {
        var column;
        if (format) {
            column = new Column(format);
        } else {
            column = new Column(dataModel.inputFormatToAdd);
        }
        http.post('/command/timebench-extension/apply-input-format?cellIndex=' + dataModel.cellIndex + '&project=' + dataModel.projectId, column.format).success(function (dateTimeValueColumns) {
            console.log('/command/timebench-extension/apply-input-format?cellIndex=' + dataModel.cellIndex + '&project=' + dataModel.projectId);
            if (dateTimeValueColumns.length > 0) {
                dateTimeValueColumns[0].reformatedColumn.forEach(function (entry) {
                    column.dateTimeValues.push(new DateTimeValue(entry.v, entry.timestamp, false));
                });
                dataModel.formatColumns.push(column);
                dataModel.findConflicts(this.reformatResultColumn);
                this.reformatResultColumn(dataModel);
                dataModel.paginate();
            }
        }.bind(this));
        console.log(dataModel);
    };

    this.removeInputFormat = function (format, dataModel) {
        console.info('remove input format');
        dataModel.formatColumns.forEach(function (item, index, object) {
            if (item.format === format) {
                object.splice(index, 1);
            }
        });
        dataModel.findConflicts(this.reformatResultColumn);
        this.reformatResultColumn(dataModel);
        for (var i = 0; i < dataModel.resultColumn.dateTimeValues.length; i++) {
            var valueCount = 0;
            dataModel.formatColumns.forEach(function (item) {
                if (item.dateTimeValues[i].value) {
                    valueCount++;
                }
            });
            if (valueCount < 1) {
                dataModel.resultColumn.dateTimeValues[i] = new DateTimeValue(null, null, false);
            }
        }
        dataModel.paginate();
    };

    this.reformatResultColumn = function (dataModel) {
        http.post('/command/timebench-extension/apply-output-format', dataModel.resultColumn).success(function (resultColumn) {
            console.log('/command/timebench-extension/apply-output-format', dataModel.resultColumn);
            var column = new Column(resultColumn.format);
            resultColumn.dateTimeValues.forEach(function (item) {
                column.dateTimeValues.push(new DateTimeValue(item.value, item.timestamp, false));
            });
            dataModel.resultColumn = column;
            dataModel.paginate();
        });
    };

    this.applyToDataModel = function (dataModel) {
        http.post('/command/timebench-extension/apply-reformation-to-datamodel?project=' + dataModel.projectId + '&cellIndex=' + dataModel.cellIndex, dataModel.resultColumn).success(function (resultColumn) {
            console.log('/command/timebench-extension/apply-reformation-to-datamodel?project=' + dataModel.projectId);
            window.location = '/project?project=' + theProject.id;
        });
    }
}

var timeBenchExtensionApp = angular.module('timebenchExtension', ['ui.bootstrap']);

timeBenchExtensionApp.controller('timebenchExtensionCtrl', function ($scope, $http) {

    var dataModel = new DataModel(theProject.id, theProject.cellIndex);
    var serviceMethods = new ServiceMethods($http);
    serviceMethods.getColumn(dataModel, $scope);
    $scope.dataModel = dataModel;
    $scope.serviceMethods = serviceMethods;
});

<|endoftext|>


<commit_before>var reddit=require("./reddit");

(new reddit({limit:5})).get("stories",function(e){
  for(var i=0;i<e.children.length;i++){
  	console.log(e.children[i].data);
  }
});<commit_msg>formating<commit_after>var reddit = require("./reddit");

(new reddit({limit:5})).get("stories", function(e) {
  for (var i = 0;i < e.children.length;i++) {
    console.log(e.children[i].data);
  }
});<|endoftext|>


<commit_before>
var assert = require('assert')
var graphDiff = require('./index')

var nodes = {
  1: [],
  2: [1],
  3: [2],
  4: [2],
  5: [4],
  6: [3, 5],
  7: [6],
  8: [5],
  9: [8],
  10: [7, 9],
  11: [10],
  12: [11],
  13: [10],
  14: [13],
  15: [12, 14],
  16: [15]
}

/* the graph:

    4-5-8-9    11-12
   /   \   \  /     \
1-2-3---6-7-10-13-14-15-16

*/

var readParents = function(id, cb) {
  cb(null, nodes[id])
}

var tests = [
  {from: 8, to: 9, expected: [9]},
  {from: 7, to: 9, expected: [9, 8]},
  {from: 6, to: 7, expected: [7]},
  {from: 5, to: 7, expected: [7, 6, 3]},
  {from: 4, to: 7, expected: [7, 6, 3, 5]},
  {from: 1, to: 7, expected: [7, 6, 3, 2, 5, 4]},
  {from: 1, to: 9, expected: [9, 8, 5, 4, 2]},
  {from: 9, to: 16, expected: [16,15,12,11,10,7,6,3,14,13]},
  {from: 1, to: 16, expected: [16,15,12,11,10,7,6,3,2,5,4,9,8,14,13]}
]

describe('graph-difference', function() {
  tests.forEach(function(each, i) {
    it('should run test ' + i, function(done) {
      graphDiff(each.from, each.to, readParents, function(err, res) {
        assert.deepEqual(res, each.expected)
        done()
      })
    })
  })
})
<commit_msg>improve tests<commit_after>
var assert = require('assert')
var graphDiff = require('./index')

var nodes = {
  1: [],
  2: [1],
  3: [2],
  4: [2],
  5: [4],
  6: [3, 5],
  7: [6],
  8: [5],
  9: [8],
  10: [7, 9],
  11: [10],
  12: [11],
  13: [10],
  14: [13],
  15: [12, 14],
  16: [15]
}

/* the graph:

    4-5-8-9    11-12
   /   \   \  /     \
1-2-3---6-7-10-13-14-15-16

*/

var readParents = function(id, cb) {
  cb(null, nodes[id])
}

var tests = [
  {from: 8, to: 9, expected: [9]},
  {from: 7, to: 9, expected: [9, 8]},
  {from: 6, to: 7, expected: [7]},
  {from: 5, to: 7, expected: [7, 6, 3]},
  {from: 4, to: 7, expected: [7, 6, 3, 5]},
  {from: 1, to: 7, expected: [7, 6, 3, 2, 5, 4]},
  {from: 1, to: 9, expected: [9, 8, 5, 4, 2]},
  {from: 9, to: 16, expected: [16,15,12,11,10,7,6,3,14,13]},
  {from: 1, to: 16, expected: [16,15,12,11,10,7,6,3,2,5,4,9,8,14,13]}
]

describe('graph-difference', function() {
  tests.forEach(function(each, i) {
    it('diff from ' + each.from + ' to ' + each.to + ' test(' + i + ')', function(done) {
      graphDiff(each.from, each.to, readParents, function(err, res) {
        assert.deepEqual(res, each.expected)
        done()
      })
    })
  })
})
<|endoftext|>


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

/* global glowroot, angular */

glowroot.controller('ConfigAlertCtrl', [
  '$scope',
  '$location',
  '$http',
  '$timeout',
  'confirmIfHasChanges',
  'httpErrors',
  function ($scope, $location, $http, $timeout, confirmIfHasChanges, httpErrors) {

    // initialize page binding object
    $scope.page = {};

    var version = $location.search().v;

    function onNewData(data) {
      $scope.config = data;
      $scope.originalConfig = angular.copy(data);

      if (data.emailAddresses.length) {
        $scope.page.timePeriodMinutes = data.timePeriodSeconds / 60;
        if (data.kind === 'transaction') {
          $scope.heading = data.transactionType + ' - ' + data.transactionPercentile
              + $scope.percentileSuffix(data.transactionPercentile) + ' percentile over a '
              + data.timePeriodSeconds + ' minute period';
        }
        if (data.kind === 'gauge') {
          $scope.heading = data.gaugeDisplay + ' - average over a ' + data.timePeriodSeconds / 60 + ' minute period';
        }
        $scope.emailAddresses = data.emailAddresses.join(', ');
      } else {
        $scope.heading = '<New>';
      }
    }

    $scope.unit = function () {
      if (!$scope.gauges) {
        // list of gauges hasn't loaded yet
        return '';
      }
      var i;
      for (i = 0; i < $scope.gauges.length; i++) {
        if ($scope.gauges[i].name === $scope.config.gaugeName) {
          return $scope.gauges[i].unit;
        }
      }
      return '';
    };

    var halfLoaded;

    $http.get('backend/jvm/all-gauges?agent-rollup=' + encodeURIComponent($scope.agentRollup))
        .success(function (data) {
          if (halfLoaded) {
            $scope.loaded = true;
          } else {
            halfLoaded = true;
          }
          $scope.gaugeNames = [];
          $scope.gauges = data;
        })
        .error(httpErrors.handler($scope));

    if (version) {
      $http.get('backend/config/alerts?agent-id=' + encodeURIComponent($scope.agentId) + '&version=' + version)
          .success(function (data) {
            if (halfLoaded) {
              $scope.loaded = true;
            } else {
              halfLoaded = true;
            }
            onNewData(data);
          })
          .error(httpErrors.handler($scope));
    } else {
      $scope.loaded = true;
      onNewData({
        kind: 'transaction',
        transactionType: $scope.defaultTransactionType(),
        timePeriodSeconds: NaN, // setting to NaN prevents "has changes" dialog on new alert with no changes
        emailAddresses: []
      });
    }

    $scope.$watch('config.kind', function (newValue) {
      if (!$scope.config) {
        return;
      }
      if (newValue === 'transaction') {
        $scope.config.gaugeName = undefined;
        $scope.config.gaugeThreshold = undefined;
      }
      if (newValue === 'gauge') {
        $scope.config.transactionType = undefined;
        $scope.config.transactionPercentile = undefined;
        $scope.config.transactionThresholdMillis = undefined;
        $scope.config.minTransactionCount = undefined;
      }
    });

    $scope.$watch('page.timePeriodMinutes', function (newValue) {
      if (!$scope.config) {
        return;
      }
      $scope.config.timePeriodSeconds = newValue * 60;
    });

    $scope.$watch('emailAddresses', function (newValue) {
      if (newValue) {
        var emailAddresses = [];
        angular.forEach(newValue.split(','), function (emailAddress) {
          emailAddress = emailAddress.trim();
          if (emailAddress.length) {
            emailAddresses.push(emailAddress);
          }
        });
        $scope.config.emailAddresses = emailAddresses;
      } else if ($scope.config) {
        $scope.config.emailAddresses = [];
      }
    });

    $scope.hasChanges = function () {
      return !angular.equals($scope.config, $scope.originalConfig);
    };
    var removeConfirmIfHasChangesListener = $scope.$on('$locationChangeStart', confirmIfHasChanges($scope));

    $scope.save = function (deferred) {
      var postData = angular.copy($scope.config);
      var url;
      if (version) {
        url = 'backend/config/alerts/update';
      } else {
        url = 'backend/config/alerts/add';
      }
      var agentId = $scope.agentId;
      $http.post(url + '?agent-id=' + agentId, postData)
          .success(function (data) {
            onNewData(data);
            deferred.resolve(version ? 'Saved' : 'Added');
            version = data.version;
            // fix current url (with updated version) before returning to list page in case back button is used later
            if (agentId) {
              $location.search({'agent-id': agentId, v: version}).replace();
            } else {
              $location.search({v: version}).replace();
            }
          })
          .error(function (data, status) {
            httpErrors.handler($scope, deferred)(data, status);
          });
    };

    $scope.delete = function (deferred) {
      var postData = {
        version: $scope.config.version
      };
      var agentId = $scope.agentId;
      $http.post('backend/config/alerts/remove?agent-id=' + encodeURIComponent(agentId), postData)
          .success(function () {
            removeConfirmIfHasChangesListener();
            if (postData) {
              $location.url('config/alert-list?agent-id=' + encodeURIComponent(agentId)).replace();
            } else {
              $location.url('config/alert-list').replace();
            }
          })
          .error(httpErrors.handler($scope, deferred));
    };
  }
]);
<commit_msg>Fix spinner on alert configuration<commit_after>/*
 * Copyright 2015-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* global glowroot, angular */

glowroot.controller('ConfigAlertCtrl', [
  '$scope',
  '$location',
  '$http',
  '$timeout',
  'confirmIfHasChanges',
  'httpErrors',
  function ($scope, $location, $http, $timeout, confirmIfHasChanges, httpErrors) {

    // initialize page binding object
    $scope.page = {};

    var version = $location.search().v;

    function onNewData(data) {
      $scope.config = data;
      $scope.originalConfig = angular.copy(data);

      if (data.emailAddresses.length) {
        $scope.page.timePeriodMinutes = data.timePeriodSeconds / 60;
        if (data.kind === 'transaction') {
          $scope.heading = data.transactionType + ' - ' + data.transactionPercentile
              + $scope.percentileSuffix(data.transactionPercentile) + ' percentile over a '
              + data.timePeriodSeconds + ' minute period';
        }
        if (data.kind === 'gauge') {
          $scope.heading = data.gaugeDisplay + ' - average over a ' + data.timePeriodSeconds / 60 + ' minute period';
        }
        $scope.emailAddresses = data.emailAddresses.join(', ');
      } else {
        $scope.heading = '<New>';
      }
    }

    $scope.unit = function () {
      if (!$scope.gauges) {
        // list of gauges hasn't loaded yet
        return '';
      }
      var i;
      for (i = 0; i < $scope.gauges.length; i++) {
        if ($scope.gauges[i].name === $scope.config.gaugeName) {
          return $scope.gauges[i].unit;
        }
      }
      return '';
    };

    var halfLoaded;
    function onHalfLoad() {
      if (halfLoaded) {
        $scope.loaded = true;
      } else {
        halfLoaded = true;
      }
    }

    $http.get('backend/jvm/all-gauges?agent-rollup=' + encodeURIComponent($scope.agentRollup))
        .success(function (data) {
          onHalfLoad();
          $scope.gaugeNames = [];
          $scope.gauges = data;
        })
        .error(httpErrors.handler($scope));

    if (version) {
      $http.get('backend/config/alerts?agent-id=' + encodeURIComponent($scope.agentId) + '&version=' + version)
          .success(function (data) {
            onHalfLoad();
            onNewData(data);
          })
          .error(httpErrors.handler($scope));
    } else {
      onHalfLoad();
      onNewData({
        kind: 'transaction',
        transactionType: $scope.defaultTransactionType(),
        timePeriodSeconds: NaN, // setting to NaN prevents "has changes" dialog on new alert with no changes
        emailAddresses: []
      });
    }

    $scope.$watch('config.kind', function (newValue) {
      if (!$scope.config) {
        return;
      }
      if (newValue === 'transaction') {
        $scope.config.gaugeName = undefined;
        $scope.config.gaugeThreshold = undefined;
      }
      if (newValue === 'gauge') {
        $scope.config.transactionType = undefined;
        $scope.config.transactionPercentile = undefined;
        $scope.config.transactionThresholdMillis = undefined;
        $scope.config.minTransactionCount = undefined;
      }
    });

    $scope.$watch('page.timePeriodMinutes', function (newValue) {
      if (!$scope.config) {
        return;
      }
      $scope.config.timePeriodSeconds = newValue * 60;
    });

    $scope.$watch('emailAddresses', function (newValue) {
      if (newValue) {
        var emailAddresses = [];
        angular.forEach(newValue.split(','), function (emailAddress) {
          emailAddress = emailAddress.trim();
          if (emailAddress.length) {
            emailAddresses.push(emailAddress);
          }
        });
        $scope.config.emailAddresses = emailAddresses;
      } else if ($scope.config) {
        $scope.config.emailAddresses = [];
      }
    });

    $scope.hasChanges = function () {
      return !angular.equals($scope.config, $scope.originalConfig);
    };
    var removeConfirmIfHasChangesListener = $scope.$on('$locationChangeStart', confirmIfHasChanges($scope));

    $scope.save = function (deferred) {
      var postData = angular.copy($scope.config);
      var url;
      if (version) {
        url = 'backend/config/alerts/update';
      } else {
        url = 'backend/config/alerts/add';
      }
      var agentId = $scope.agentId;
      $http.post(url + '?agent-id=' + agentId, postData)
          .success(function (data) {
            onNewData(data);
            deferred.resolve(version ? 'Saved' : 'Added');
            version = data.version;
            // fix current url (with updated version) before returning to list page in case back button is used later
            if (agentId) {
              $location.search({'agent-id': agentId, v: version}).replace();
            } else {
              $location.search({v: version}).replace();
            }
          })
          .error(function (data, status) {
            httpErrors.handler($scope, deferred)(data, status);
          });
    };

    $scope.delete = function (deferred) {
      var postData = {
        version: $scope.config.version
      };
      var agentId = $scope.agentId;
      $http.post('backend/config/alerts/remove?agent-id=' + encodeURIComponent(agentId), postData)
          .success(function () {
            removeConfirmIfHasChangesListener();
            if (postData) {
              $location.url('config/alert-list?agent-id=' + encodeURIComponent(agentId)).replace();
            } else {
              $location.url('config/alert-list').replace();
            }
          })
          .error(httpErrors.handler($scope, deferred));
    };
  }
]);
<|endoftext|>


<commit_before>var chimneypot = require('./index.js');

// Empty constructor
shouldThrowError(function() {
  new chimneypot();
});

// No port
shouldThrowError(function() {
  new chimneypot({
    path: '/',
    secret: 'spooky'
  });
});

// No path
shouldThrowError(function() {
  new chimneypot({
    port: 1996,
    secret: 'spooky'
  });
});

// No secret
shouldNotThrowError(function() {
  new chimneypot({
    port: 1996,
    path: '/'
  });
});

// Non numeric port
(function() {
  // String port
  shouldThrowError(function() {
    new chimneypot({
      port: 'wrong',
      path: '/',
      secret: 'spooky'
    });
  });

  // Boolean port (false)
  shouldThrowError(function() {
    new chimneypot({
      port: false,
      path: '/',
      secret: 'spooky'
    });
  });

  // Boolean port (true)
  shouldThrowError(function() {
    new chimneypot({
      port: true,
      path: '/',
      secret: 'spooky'
    });
  });

  // Array port
  shouldThrowError(function() {
    new chimneypot({
      port: [],
      path: '/',
      secret: 'spooky'
    });
  });

  // Object port
  shouldThrowError(function() {
    new chimneypot({
      port: {},
      path: '/',
      secret: 'spooky'
    });
  });

  // Non-integer port
  shouldThrowError(function() {
    new chimneypot({
      port: 50.5,
      path: '/',
      secret: 'spooky'
    });
  });
})();

// Call listen before routing
shouldThrowError(function() {
  var c = _setupValidChimneypot();

  c.listen();
});

console.log("all tests passed");

function shouldThrowError(func) {
  if (!_throws(func)) {
    throw new Error("Expected error to be thrown, but was not thrown");
  }
}

function shouldNotThrowError(func) {
  if (_throws(func)) {
    throw new Error("Expected error to not be thrown, but was thrown");
  }
}

function _setupValidChimneypot() {
  return new chimneypot({
    port: 1996,
    path: '/'
  });
}

function _throws(func) {
  try {
    func();
  } catch (err) {
    return true;
  }
  return false;
}
<commit_msg>Isolate listen method<commit_after>var chimneypot = require('./index.js');

// Empty constructor
shouldThrowError(function() {
  new chimneypot();
});

// No port
shouldThrowError(function() {
  new chimneypot({
    path: '/',
    secret: 'spooky'
  });
});

// No path
shouldThrowError(function() {
  new chimneypot({
    port: 1996,
    secret: 'spooky'
  });
});

// No secret
shouldNotThrowError(function() {
  new chimneypot({
    port: 1996,
    path: '/'
  });
});

// Non numeric port
(function() {
  // String port
  shouldThrowError(function() {
    new chimneypot({
      port: 'wrong',
      path: '/',
      secret: 'spooky'
    });
  });

  // Boolean port (false)
  shouldThrowError(function() {
    new chimneypot({
      port: false,
      path: '/',
      secret: 'spooky'
    });
  });

  // Boolean port (true)
  shouldThrowError(function() {
    new chimneypot({
      port: true,
      path: '/',
      secret: 'spooky'
    });
  });

  // Array port
  shouldThrowError(function() {
    new chimneypot({
      port: [],
      path: '/',
      secret: 'spooky'
    });
  });

  // Object port
  shouldThrowError(function() {
    new chimneypot({
      port: {},
      path: '/',
      secret: 'spooky'
    });
  });

  // Non-integer port
  shouldThrowError(function() {
    new chimneypot({
      port: 50.5,
      path: '/',
      secret: 'spooky'
    });
  });
})();

// Call listen before routing
(function() {
  var c = _setupValidChimneypot();

  shouldThrowError(function() {
    c.listen();
  });
})();

console.log("all tests passed");

function shouldThrowError(func) {
  if (!_throws(func)) {
    throw new Error("Expected error to be thrown, but was not thrown");
  }
}

function shouldNotThrowError(func) {
  if (_throws(func)) {
    throw new Error("Expected error to not be thrown, but was thrown");
  }
}

function _setupValidChimneypot() {
  return new chimneypot({
    port: 1996,
    path: '/'
  });
}

function _throws(func) {
  try {
    func();
  } catch (err) {
    return true;
  }
  return false;
}
<|endoftext|>


<commit_before>const test = require('ava');
const random = require('./helpers/random');
const all = require('./products');

test('all()', t => {
  t.true(all.length < 0);
});

test('random()', t => {
  t.truthy(random(all));
  t.not(random(all), random(all));
});
<commit_msg>🤖(fix): 2017-7-20 14:46:59<commit_after>const test = require('ava');
const random = require('./helpers/random');
const all = require('./products');

test('all()', t => {
  t.true(all.length > 0);
});

test('random()', t => {
  t.truthy(random(all));
  t.not(random(all), random(all));
});
<|endoftext|>


<commit_before>#!/usr/bin/env node

var mapnik = require('mapnik');
var assert = require('assert');
var path = require('path');
var fs = require('fs');

/* Datasource */

assert.throws(function() {mapnik.Datasource('foo')});
assert.throws(function() {mapnik.Datasource({'foo': 1})});
assert.throws(function() {mapnik.Datasource({'type': 'foo'})});
assert.throws(function() { mapnik.Datasource({'type': 'shape'}) });
assert.ok(new mapnik.Datasource({type: 'shape', file: './examples/data/world_merc.shp'}));

// datasource from shapefile
var options = { type: 'shape',
                file: './examples/data/world_merc.shp'
              };
var ds = new mapnik.Datasource(options);
var p = ds.parameters();
assert.equal(p.type, options.type);
assert.equal(p.file, options.file);
assert.ok(ds.features());
var desc = ds.describe();
assert.ok(desc);

// same datasource but from json file (originally converted with ogr2ogr)
var options2 = { type: 'ogr',
                 file: './examples/data/world_merc.json',
                 layer_by_index: 0
               };
var ds2 = new mapnik.Datasource(options);
var p2 = ds.parameters();
assert.equal(p2.type, options.type);
assert.equal(p2.file, options.file);
assert.ok(ds2.features());
// make sure json datasource description
// matches exactly the data read from shapefile
var desc2 = ds2.describe();
assert.equal(desc.length, desc2.length);
assert.equal(desc.type, desc2.type);
assert.equal(desc.encoding, desc2.encoding);
assert.deepEqual(desc.extent, desc2.extent);
assert.equal(desc.fields.length, desc2.fields.length);
assert.equal(desc.fields.FIPS, desc2.fields.FIPS);

/* TODO

 - expose adding a mapnik.Datasource to a mapnik.Layer
 - wrap Datasource contructor in factory for each 'type'

 e.g.
  Shapefile() // {type:'shape'}
  PostGIS()   // {type:'postgis'}
  OSM()       // {type:'osm'}

*/

/* settings */

assert.ok(mapnik.settings);
assert.ok(mapnik.settings.paths);
assert.ok(mapnik.settings.paths.fonts.length);
assert.ok(mapnik.settings.paths.input_plugins.length);

/* has version info */

assert.ok(mapnik.versions);
assert.ok(mapnik.versions.node);
assert.ok(mapnik.versions.v8);
assert.ok(mapnik.versions.mapnik);
assert.ok(mapnik.versions.mapnik_number);
assert.ok(mapnik.versions.boost);
assert.ok(mapnik.versions.boost_number);

/* datasource "input_plugins" */

// make sure we have some
assert.ok(mapnik.datasources().length > 1);

// reloading the default plugins path should return false as no more plugins are registered
assert.ok(!mapnik.register_datasources(mapnik.settings.paths.input_plugins));

/* fonts */

function oc(a)
{
  var o = {};
  for (var i = 0; i < a.length; i++)
  {
    o[a[i]] = '';
  }
  return o;
}

// make sure we have default fonts
assert.ok('DejaVu Sans Bold' in oc(mapnik.fonts()));

// make sure system font was loaded
if (process.platform == 'darwin') {
    assert.ok('Times Regular' in oc(mapnik.fonts()));
    // it should already be loaded so trying to register more should return false
    assert.ok(!mapnik.register_fonts('/System/Library/Fonts/', {recurse: true}));
}

// will return true if new fonts are found
// but should return false as we now call at startup
assert.ok(!mapnik.register_system_fonts());

/* ABI compatibility */

// be strict/exact
// note process.versions == process.versions.node except the latter has the 'v' stripped
assert.ok(mapnik.versions.node === process.versions.node, 'The node version "' + process.versions.node + '" does not match the node version that node-mapnik was compiled against: "' + mapnik.versions.node + '"');

/* Map */

// Test mapnik.Map object creation

// no 'new' keyword
assert.throws(function() {mapnik.Map('foo')});
// invalid args
assert.throws(function() {new mapnik.Map()});
assert.throws(function() {new mapnik.Map(1)});
assert.throws(function() {new mapnik.Map('a', 'b', 'c')});
assert.throws(function() {new mapnik.Map(new mapnik.Map(1, 1))});

var Map = mapnik.Map;

var map1 = new Map(256, 256);

assert.ok(map1 instanceof Map);

var map = new mapnik.Map(600, 400);

assert.ok(map instanceof mapnik.Map);
assert.throws(function() {new mapnik.Map('foo')});

assert.throws(function() {mapnik.Map('foo')});

// test initial values
assert.deepEqual(map.extent(), [-1.0, -1.0, 0.0, 0.0]);

// Test rendering a blank image
map.render_to_file('/tmp/nodemap.png');
assert.ok(path.existsSync('/tmp/nodemap.png'));

// test async rendering
map.render(map.extent(), function(buffer) {
       fs.writeFile('/tmp/nodemap_async.png', buffer, function(err) {
           if (err) throw err;
           assert.ok(path.existsSync('/tmp/nodemap_async.png'));
           // todo - not working right...
           //assert.equal(fs.readFileSync('/tmp/nodemap.png').length,fs.readFileSync('/tmp/nodemap_async.png').length);
       });
  });

// Test loading a sample world map
map.load('./examples/stylesheet.xml');

// clear styles and layers from previous load to set up for another
// otherwise layers are duplicated
map.clear();

// Test loading a map from a string
var style_string = fs.readFileSync('./examples/stylesheet.xml').toLocaleString();
var base_url = './examples/'; // must end with trailing slash
map.from_string(style_string, base_url);

// test new extent based on active layers
map.zoom_all();
var expected = [-20037508.3428, -14996604.5082, 20037508.3428, 25078412.1774];
assert.notStrictEqual(map.extent(), expected);
//var expected_precise = [-20037508.342789248,-8317435.060598943,20037508.342789244,18399242.72978672];
//assert.deepEqual(map.extent(), expected_precise);

// Test rendering with actual data
map.render_to_file('/tmp/world.png');
assert.ok(path.existsSync('/tmp/world.png'));

assert.equal(map.width(), 600);
assert.equal(map.height(), 400);
map.resize(256, 256);
assert.equal(map.width(), 256);
assert.equal(map.height(), 256);

// layers
var layers = map.layers();
assert.equal(layers.length, 1, layers.length);
lyr = layers[0];
assert.equal(lyr.name, 'world');
assert.equal(lyr.srs, '+proj=merc +lon_0=0 +lat_ts=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs');
assert.equal(lyr.styles.length, 1);
assert.equal(lyr.styles[0], 'style');
assert.equal(lyr.name, 'world');
assert.equal(lyr.datasource.type, 'shape');
assert.equal(lyr.datasource.file, path.join(process.cwd(), 'examples/data/world_merc.shp'));

// features
var features = map.features(0); // for first and only layer
assert.equal(features.length, 245);
var last = features[244];
assert.equal(last.AREA, 1638094);
assert.equal(last.FIPS, 'RS');
assert.equal(last.ISO2, 'RU');
assert.equal(last.LAT, 61.988);
assert.equal(last.LON, 96.689);
assert.equal(last.NAME, 'Russia');
assert.equal(last.POP2005, 143953092);
assert.equal(last.REGION, 150);
assert.equal(last.SUBREGION, 151);
assert.equal(last.UN, 643);

// datasource meta data
var described = map.describe_data();
assert.deepEqual(described.world.extent, [-20037508.342789248, -8283343.693882697, 20037508.342789244, 18365151.363070473]);
assert.equal(described.world.type, 'vector');
assert.equal(described.world.encoding, 'utf-8');
assert.equal(described.world.fields.FIPS, 'String');

/* Layer */

// no 'new' keyword
assert.throws(function() {mapnik.Layer('foo')});
// invalid args
assert.throws(function() {new mapnik.Layer()});
assert.throws(function() {new mapnik.Layer(1)});
assert.throws(function() {new mapnik.Layer('a', 'b', 'c')});
assert.throws(function() {new mapnik.Layer(new mapnik.Layer('foo'))});

// new layer
var layer = new mapnik.Layer('foo', '+init=epsg:4326');
assert.equal(layer.name, 'foo');
assert.equal(layer.srs, '+init=epsg:4326');
assert.deepEqual(layer.styles, []);
// will be empty/undefined
assert.ok(!layer.datasource);
layer.datasource = new mapnik.Datasource(options);
assert.ok(layer.datasource instanceof mapnik.Datasource);

// json representation
var meta = layer.describe();
assert.equal(meta.name, 'foo');
assert.equal(meta.srs, '+init=epsg:4326');
assert.deepEqual(meta.styles, []);
assert.deepEqual(meta.datasource, options);

// actual map including layer with datasource
var map = new mapnik.Map(256, 256);
map.load('./examples/stylesheet.xml');

// pull a copy out of the map into v8 land
var layer = map.get_layer(0);
assert.equal(layer.name, 'world');
assert.equal(layer.srs, '+proj=merc +lon_0=0 +lat_ts=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs');
assert.equal(layer.styles.length, 1);
assert.equal(layer.styles[0], 'style');
assert.deepEqual(layer.datasource, {});

// json representation
var meta = layer.describe();
assert.equal(meta.name, 'world');
assert.equal(meta.srs, '+proj=merc +lon_0=0 +lat_ts=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs');
assert.equal(meta.styles.length, 1);
assert.deepEqual(meta.styles, ['style']);

// make a change to layer, ensure it sticks
layer.name = 'a';
layer.styles = ['a'];
layer.srs = '+init=epsg:4326';
layer.datasource = new mapnik.Datasource(options);

// check for change, after adding to map
// adding to map should release original layer
// as a copy is made when added (I think)
map.add_layer(layer);
var added = map.layers()[1];
// make sure the layer is an identical copy to what is on map
assert.equal(added.name, layer.name);
assert.equal(added.srs, layer.srs);
assert.deepEqual(added.styles, layer.styles);
assert.deepEqual(added.datasource, options);
assert.deepEqual(added.datasource, new mapnik.Datasource(options).parameters());

/* Projection */

// Test mapnik.Projection object creation

assert.throws(function() {new mapnik.Projection('+init=epsg:foo')});
assert.throws(function() {new mapnik.Projection('+proj +foo')});

try {
    var proj = new mapnik.Projection('+init=epsg:foo');
}
catch (err)
{
    assert.equal(err.message, 'failed to initialize projection with:+init=epsg:foo');
}

var wgs84 = new mapnik.Projection('+init=epsg:4326');

assert.ok(wgs84 instanceof mapnik.Projection);

var merc = null;

try {
    // perhaps we've got a savvy user?
    merc = new mapnik.Projection('+init=epsg:900913');
}
catch (err) {
    // newer versions of proj4 have this code which is == 900913
    merc = new mapnik.Projection('+init=epsg:3857');
}

if (!merc) {
    /* be friendly if this test fails */
    var msg = 'warning, could not create a spherical mercator projection';
    msg += '\nwith either epsg:900913 or epsg:3857, so they must be missing';
    msg += '\nfrom your proj4 epsg table (/usr/local/share/proj/epsg)';
    console.log(msg);
}
else
{
    assert.ok(merc instanceof mapnik.Projection);
    long_lat_coords = [-122.33517, 47.63752];
    merc_coords = merc.forward(long_lat_coords);

    assert.equal(merc_coords.length, 2);
    assert.notStrictEqual(merc_coords, [-13618288.8305, 6046761.54747]);
    assert.notStrictEqual(long_lat_coords, merc.inverse(merc.forward(long_lat_coords)));

    long_lat_bounds = [-122.420654, 47.605006, -122.2435, 47.67764];
    merc_bounds = merc.forward(long_lat_bounds);

    assert.equal(merc_bounds.length, 4);
    var expected = [-13627804.8659, 6041391.68077, -13608084.1728, 6053392.19471];
    assert.notStrictEqual(merc_bounds, expected);
    assert.notStrictEqual(long_lat_bounds, merc.inverse(merc.forward(long_lat_bounds)));
}

console.log('All tests pass...');
<commit_msg>improve test coverage of datasources<commit_after>#!/usr/bin/env node

var mapnik = require('mapnik');
var assert = require('assert');
var path = require('path');
var fs = require('fs');

/* settings */

assert.ok(mapnik.settings);
assert.ok(mapnik.settings.paths);
assert.ok(mapnik.settings.paths.fonts.length);
assert.ok(mapnik.settings.paths.input_plugins.length);

/* has version info */

assert.ok(mapnik.versions);
assert.ok(mapnik.versions.node);
assert.ok(mapnik.versions.v8);
assert.ok(mapnik.versions.mapnik);
assert.ok(mapnik.versions.mapnik_number);
assert.ok(mapnik.versions.boost);
assert.ok(mapnik.versions.boost_number);

/* datasource "input_plugins" */

// make sure we have some
assert.ok(mapnik.datasources().length > 1);

// reloading the default plugins path should return false as no more plugins are registered
assert.ok(!mapnik.register_datasources(mapnik.settings.paths.input_plugins));

/* fonts */

function oc(a)
{
  var o = {};
  for (var i = 0; i < a.length; i++)
  {
    o[a[i]] = '';
  }
  return o;
}

// make sure we have default fonts
assert.ok('DejaVu Sans Bold' in oc(mapnik.fonts()));

// make sure system font was loaded
if (process.platform == 'darwin') {
    assert.ok('Times Regular' in oc(mapnik.fonts()));
    // it should already be loaded so trying to register more should return false
    assert.ok(!mapnik.register_fonts('/System/Library/Fonts/', {recurse: true}));
}

// will return true if new fonts are found
// but should return false as we now call at startup
assert.ok(!mapnik.register_system_fonts());

/* ABI compatibility */

// be strict/exact
// note process.versions == process.versions.node except the latter has the 'v' stripped
assert.ok(mapnik.versions.node === process.versions.node, 'The node version "' + process.versions.node + '" does not match the node version that node-mapnik was compiled against: "' + mapnik.versions.node + '"');

/* Map */

// Test mapnik.Map object creation

// no 'new' keyword
assert.throws(function() {mapnik.Map('foo')});
// invalid args
assert.throws(function() {new mapnik.Map()});
assert.throws(function() {new mapnik.Map(1)});
assert.throws(function() {new mapnik.Map('a', 'b', 'c')});
assert.throws(function() {new mapnik.Map(new mapnik.Map(1, 1))});

var Map = mapnik.Map;

var map1 = new Map(256, 256);

assert.ok(map1 instanceof Map);

var map = new mapnik.Map(600, 400);

assert.ok(map instanceof mapnik.Map);
assert.throws(function() {new mapnik.Map('foo')});

assert.throws(function() {mapnik.Map('foo')});

// test initial values
assert.deepEqual(map.extent(), [-1.0, -1.0, 0.0, 0.0]);

// Test rendering a blank image
map.render_to_file('/tmp/nodemap.png');
assert.ok(path.existsSync('/tmp/nodemap.png'));

// test async rendering
map.render(map.extent(), function(buffer) {
       fs.writeFile('/tmp/nodemap_async.png', buffer, function(err) {
           if (err) throw err;
           assert.ok(path.existsSync('/tmp/nodemap_async.png'));
           // todo - not working right...
           //assert.equal(fs.readFileSync('/tmp/nodemap.png').length,fs.readFileSync('/tmp/nodemap_async.png').length);
       });
  });

// Test loading a sample world map
map.load('./examples/stylesheet.xml');

// clear styles and layers from previous load to set up for another
// otherwise layers are duplicated
map.clear();

// Test loading a map from a string
var style_string = fs.readFileSync('./examples/stylesheet.xml').toLocaleString();
var base_url = './examples/'; // must end with trailing slash
map.from_string(style_string, base_url);

// test new extent based on active layers
map.zoom_all();
var expected = [-20037508.3428, -14996604.5082, 20037508.3428, 25078412.1774];
assert.notStrictEqual(map.extent(), expected);
//var expected_precise = [-20037508.342789248,-8317435.060598943,20037508.342789244,18399242.72978672];
//assert.deepEqual(map.extent(), expected_precise);

// Test rendering with actual data
map.render_to_file('/tmp/world.png');
assert.ok(path.existsSync('/tmp/world.png'));

assert.equal(map.width(), 600);
assert.equal(map.height(), 400);
map.resize(256, 256);
assert.equal(map.width(), 256);
assert.equal(map.height(), 256);

// layers
var layers = map.layers();
assert.equal(layers.length, 1, layers.length);
lyr = layers[0];
assert.equal(lyr.name, 'world');
assert.equal(lyr.srs, '+proj=merc +lon_0=0 +lat_ts=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs');
assert.equal(lyr.styles.length, 1);
assert.equal(lyr.styles[0], 'style');
assert.equal(lyr.name, 'world');
assert.equal(lyr.datasource.type, 'shape');
assert.equal(lyr.datasource.file, path.join(process.cwd(), 'examples/data/world_merc.shp'));

// features
var features = map.features(0); // for first and only layer
assert.equal(features.length, 245);
var last = features[244];
assert.equal(last.AREA, 1638094);
assert.equal(last.FIPS, 'RS');
assert.equal(last.ISO2, 'RU');
assert.equal(last.LAT, 61.988);
assert.equal(last.LON, 96.689);
assert.equal(last.NAME, 'Russia');
assert.equal(last.POP2005, 143953092);
assert.equal(last.REGION, 150);
assert.equal(last.SUBREGION, 151);
assert.equal(last.UN, 643);

// datasource meta data
var described = map.describe_data();
assert.deepEqual(described.world.extent, [-20037508.342789248, -8283343.693882697, 20037508.342789244, 18365151.363070473]);
assert.equal(described.world.type, 'vector');
assert.equal(described.world.encoding, 'utf-8');
assert.equal(described.world.fields.FIPS, 'String');

/* Layer */

// no 'new' keyword
assert.throws(function() {mapnik.Layer('foo')});
// invalid args
assert.throws(function() {new mapnik.Layer()});
assert.throws(function() {new mapnik.Layer(1)});
assert.throws(function() {new mapnik.Layer('a', 'b', 'c')});
assert.throws(function() {new mapnik.Layer(new mapnik.Layer('foo'))});

// new layer
var layer = new mapnik.Layer('foo', '+init=epsg:4326');
assert.equal(layer.name, 'foo');
assert.equal(layer.srs, '+init=epsg:4326');
assert.deepEqual(layer.styles, []);
// will be empty/undefined
assert.ok(!layer.datasource);
var options = { type: 'shape',
                file: './examples/data/world_merc.shp'
              };
var ds = new mapnik.Datasource(options);
layer.datasource = ds;
assert.ok(layer.datasource instanceof mapnik.Datasource);

// json representation
var meta = layer.describe();
assert.equal(meta.name, 'foo');
assert.equal(meta.srs, '+init=epsg:4326');
assert.deepEqual(meta.styles, []);
assert.deepEqual(meta.datasource, options);

// actual map including layer with datasource
var mapnik = require('mapnik')
var map = new mapnik.Map(256, 256);
map.load('./examples/stylesheet.xml');

// pull a copy out of the map into v8 land
var layer = map.get_layer(0);
assert.equal(layer.name, 'world');
assert.equal(layer.srs, '+proj=merc +lon_0=0 +lat_ts=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs');
assert.equal(layer.styles.length, 1);
assert.equal(layer.styles[0], 'style');
// datasource has no properties atm...
assert.deepEqual(layer.datasource, {});
// but it does have functions
assert.deepEqual(layer.datasource.describe(), map.describe_data().world);

// json representation
var meta = layer.describe();
assert.equal(meta.name, 'world');
assert.equal(meta.srs, '+proj=merc +lon_0=0 +lat_ts=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs');
assert.equal(meta.styles.length, 1);
assert.deepEqual(meta.styles, ['style']);

// make a change to layer, ensure it sticks
layer.name = 'a';
layer.styles = ['a'];
layer.srs = '+init=epsg:4326';
layer.datasource = new mapnik.Datasource(options);

// check for change, after adding to map
// adding to map should release original layer
// as a copy is made when added (I think)
map.add_layer(layer);
var added = map.layers()[1];
// make sure the layer is an identical copy to what is on map
assert.equal(added.name, layer.name);
assert.equal(added.srs, layer.srs);
assert.deepEqual(added.styles, layer.styles);
assert.deepEqual(added.datasource, options);
assert.deepEqual(added.datasource, new mapnik.Datasource(options).parameters());

/* Datasource */

assert.throws(function() {mapnik.Datasource('foo')});
assert.throws(function() {mapnik.Datasource({'foo': 1})});
assert.throws(function() {mapnik.Datasource({'type': 'foo'})});
assert.throws(function() { mapnik.Datasource({'type': 'shape'}) });
assert.ok(new mapnik.Datasource({type: 'shape', file: './examples/data/world_merc.shp'}));

// datasource from shapefile
// options defined above
var ds = new mapnik.Datasource(options);
var p = ds.parameters();
assert.equal(p.type, options.type);
assert.equal(p.file, options.file);
assert.ok(ds.features());
var desc = ds.describe();
assert.ok(desc);

// same datasource but from json file (originally converted with ogr2ogr)
var options2 = { type: 'ogr',
                 file: './examples/data/world_merc.json',
                 layer_by_index: 0
               };
var ds2 = new mapnik.Datasource(options);
var p2 = ds.parameters();
assert.equal(p2.type, options.type);
assert.equal(p2.file, options.file);
assert.ok(ds2.features());
// make sure json datasource description
// matches exactly the data read from shapefile
var desc2 = ds2.describe();
assert.equal(desc.length, desc2.length);
assert.equal(desc.type, desc2.type);
assert.equal(desc.encoding, desc2.encoding);
assert.deepEqual(desc.extent, desc2.extent);
assert.equal(desc.fields.length, desc2.fields.length);
assert.equal(desc.fields.FIPS, desc2.fields.FIPS);

/* TODO

 - wrap Datasource contructor in factory for each 'type'

 e.g.
  Shapefile() // {type:'shape'}
  PostGIS()   // {type:'postgis'}
  OSM()       // {type:'osm'}

*/

/* Projection */

// Test mapnik.Projection object creation

assert.throws(function() {new mapnik.Projection('+init=epsg:foo')});
assert.throws(function() {new mapnik.Projection('+proj +foo')});

try {
    var proj = new mapnik.Projection('+init=epsg:foo');
}
catch (err)
{
    assert.equal(err.message, 'failed to initialize projection with:+init=epsg:foo');
}

var wgs84 = new mapnik.Projection('+init=epsg:4326');

assert.ok(wgs84 instanceof mapnik.Projection);

var merc = null;

try {
    // perhaps we've got a savvy user?
    merc = new mapnik.Projection('+init=epsg:900913');
}
catch (err) {
    // newer versions of proj4 have this code which is == 900913
    merc = new mapnik.Projection('+init=epsg:3857');
}

if (!merc) {
    /* be friendly if this test fails */
    var msg = 'warning, could not create a spherical mercator projection';
    msg += '\nwith either epsg:900913 or epsg:3857, so they must be missing';
    msg += '\nfrom your proj4 epsg table (/usr/local/share/proj/epsg)';
    console.log(msg);
}
else
{
    assert.ok(merc instanceof mapnik.Projection);
    long_lat_coords = [-122.33517, 47.63752];
    merc_coords = merc.forward(long_lat_coords);

    assert.equal(merc_coords.length, 2);
    assert.notStrictEqual(merc_coords, [-13618288.8305, 6046761.54747]);
    assert.notStrictEqual(long_lat_coords, merc.inverse(merc.forward(long_lat_coords)));

    long_lat_bounds = [-122.420654, 47.605006, -122.2435, 47.67764];
    merc_bounds = merc.forward(long_lat_bounds);

    assert.equal(merc_bounds.length, 4);
    var expected = [-13627804.8659, 6041391.68077, -13608084.1728, 6053392.19471];
    assert.notStrictEqual(merc_bounds, expected);
    assert.notStrictEqual(long_lat_bounds, merc.inverse(merc.forward(long_lat_bounds)));
}

console.log('All tests pass...');
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import styles from './Post.scss';
import htmlToText from 'app/common/parser';

export const Post = ({ title, photos, description, city, created }) => {
    const src = photos[0];
    const descriptionText = htmlToText(description);
    return (

{title}

{descriptionText}

€ 475,00

{created}

{city}

            <div className={styles.photo} style={{backgroundImage: `url(${src})`}} />

    );
};

Post.propTypes = {
    title: PropTypes.string,
    description: PropTypes.string,
    city: PropTypes.string,
    created: PropTypes.string,
    photos: PropTypes.array,
};
<commit_msg>Show price<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import styles from './Post.scss';
import htmlToText from 'app/common/parser';

export const Post = ({ title, photos, description, city, created, price }) => {
    const src = photos[0];
    const descriptionText = htmlToText(description);
    return (

{title}

{descriptionText}

€ {price}

{created}

{city}

            <div className={styles.photo} style={{backgroundImage: `url(${src})`}} />

    );
};

Post.propTypes = {
    title: PropTypes.string.isRequired,
    description: PropTypes.string.isRequired,
    city: PropTypes.string.isRequired,
    created: PropTypes.string.isRequired,
    photos: PropTypes.array.isRequired,
    price: PropTypes.number.isRequired,
};
<|endoftext|>


<commit_before>
const test     = require('tape')
const should   = require('should')
const wayfarer = require('./index.js')

test('wayfarer() should initialize with empty properties', function(t) {
  t.plan(1)
  const router = wayfarer()
  t.equal(router.defaultPath, '')
})

test('wayfarer should set accept a default path', function(t) {
  t.plan(1)
  const router = wayfarer({ default: '/404' })
  t.equal(router.defaultPath, '/404')
})

test('.on() should catch type errors', function(t) {
  t.plan(2)
  const router = wayfarer()
  t.throws(router.on.bind(router, 123), /string/)
  t.throws(router.on.bind(router, '/hi', 123), /function/)
})

test('.match() should match simple routes', function(t) {
  t.plan(1)
  const router = wayfarer()
  router.on('/hello', function() { t.ok(true, 'correct path called') })
  router.on('/hi', function() { t.ok(false, 'incorrect path called') })
  router.match('/hello')
})

test('.match() should match dynamic routes', function(t) {
  t.plan(1)
  const router = wayfarer()
  router.on('/hi', function() { t.ok(false, 'incorrect path called') })
  router.on('/:user', function() { t.ok(true, 'correct path called') })
  router.match('/tobi')
})

test('.match() should provide param object on dynamic routes', function(t) {
  t.plan(1)
  const router = wayfarer()
  router.on('/hi', function() { t.ok(false, 'incorrect path called') })
  router.on('/:user', function(uri, param) { t.equal(param.user, 'tobi') })
  router.match('/tobi')
})

test('.match() should match the default path if no matches found', function(t) {
  t.plan(1)
  const router = wayfarer({ default: '/404' })
  router.on('/hi', function() { t.ok(false, 'incorrect path called') })
  router.on('/howdy', function() { t.ok(false, 'incorrect path called') })
  router.on('/404', function() { t.ok(true, 'correct path called') })
  router.match('/derpderp')
})

test('.match() should not match queryStrings', function(t) {
  t.plan(1)
  const router = wayfarer()
  router.on('/hi', function() { t.ok(false, 'incorrect path called') })
  router.on('/howdy', function() { t.ok(false, 'incorrect path called') })
  router.on('/404', function() { t.ok(true, 'correct path called') })
  router.match('/404?derp=darp')
})

test('.match() should allow nested routers', function(t) {
  t.plan(1)
  const router = wayfarer()
  const sub = wayfarer()
  router.on('/home', sub.match.bind(sub))
  sub.on('/home', function(uri) { t.equal(uri, '/home') })
  router.match('/home')
})

test('aliases', function(t) {
  t.plan(2)
  const router = wayfarer()
  t.equal(router.route, router.on, '.route() == .on()')
  t.equal(router.path, router.on, '.path() == .on()')
})
<commit_msg>match: add nested routers<commit_after>
const test     = require('tape')
const should   = require('should')
const wayfarer = require('./index.js')

test('wayfarer() should initialize with empty properties', function(t) {
  t.plan(1)
  const router = wayfarer()
  t.equal(router.defaultPath, '')
})

test('wayfarer should set accept a default path', function(t) {
  t.plan(1)
  const router = wayfarer({ default: '/404' })
  t.equal(router.defaultPath, '/404')
})

test('.on() should catch type errors', function(t) {
  t.plan(2)
  const router = wayfarer()
  t.throws(router.on.bind(router, 123), /string/)
  t.throws(router.on.bind(router, '/hi', 123), /function/)
})

test('.match() should match simple routes', function(t) {
  t.plan(1)
  const router = wayfarer()
  router.on('/hello', function() { t.ok(true, 'correct path called') })
  router.on('/hi', function() { t.ok(false, 'incorrect path called') })
  router.match('/hello')
})

test('.match() should match dynamic routes', function(t) {
  t.plan(1)
  const router = wayfarer()
  router.on('/hi', function() { t.ok(false, 'incorrect path called') })
  router.on('/:user', function() { t.ok(true, 'correct path called') })
  router.match('/tobi')
})

test('.match() should provide param object on dynamic routes', function(t) {
  t.plan(1)
  const router = wayfarer()
  router.on('/hi', function() { t.ok(false, 'incorrect path called') })
  router.on('/:user', function(uri, param) { t.equal(param.user, 'tobi') })
  router.match('/tobi')
})

test('.match() should match the default path if no matches found', function(t) {
  t.plan(1)
  const router = wayfarer({ default: '/404' })
  router.on('/hi', function() { t.ok(false, 'incorrect path called') })
  router.on('/howdy', function() { t.ok(false, 'incorrect path called') })
  router.on('/404', function() { t.ok(true, 'correct path called') })
  router.match('/derpderp')
})

test('.match() should not match queryStrings', function(t) {
  t.plan(1)
  const router = wayfarer()
  router.on('/hi', function() { t.ok(false, 'incorrect path called') })
  router.on('/howdy', function() { t.ok(false, 'incorrect path called') })
  router.on('/404', function() { t.ok(true, 'correct path called') })
  router.match('/404?derp=darp')
})

test('.match() should provide the uri string to callback', function(t) {
  t.plan(1)
  const router = wayfarer()
  const sub = wayfarer()
  router.on('/home', sub.match.bind(sub))
  sub.on('/home', function(uri) { t.equal(uri, '/home') })
  router.match('/home')
})

test('aliases', function(t) {
  t.plan(2)
  const router = wayfarer()
  t.equal(router.route, router.on, '.route() == .on()')
  t.equal(router.path, router.on, '.path() == .on()')
})
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import glamorous from 'glamorous';
import Swipeable from 'react-swipeable';
import chunk from 'lodash/chunk';
import { Link } from 'react-router';
import { List, fromJS } from 'immutable';
import { getCategoryName } from '../utils/utils';

import ProductBadge from './ProductBadge';
import FilterBar from './FilterBar';

export default class Catalogue2 extends Component {
  static propTypes = {
    categoryCode: PropTypes.string.isRequired,
    requestFetchCategory: PropTypes.func.isRequired,
    clearProductList: PropTypes.func.isRequired,
    setAnalyticsProductClick: PropTypes.func.isRequired,
    trackCatalogueProductsChunk: PropTypes.func.isRequired,
    products: ImmutablePropTypes.list,
    isDialogOpen: PropTypes.bool.isRequired,
    filterMap: ImmutablePropTypes.map.isRequired
  };

  static defaultProps = {
    products: List()
  };

  constructor(props) {
    super(props);
    this.flagOnSwipe = false;
    this.chunkSize = 4;
    this.productsChunk = List();
    this.timeout = 1000;
    this.renderProducts = this.renderProducts.bind(this);
    this.onBadgeClick = this.onBadgeClick.bind(this);
    this.onLeftSwipe = this.onLeftSwipe.bind(this);
    this.onRightSwipe = this.onRightSwipe.bind(this);
    this.chunkerizeProductList = this.chunkerizeProductList.bind(this);
    this.getChunks = this.getChunks.bind(this);
    this.trackCurrentChunk = this.trackCurrentChunk.bind(this);
    this.updateCurrentChunkInState = this.updateCurrentChunkInState.bind(this);
    this.setChunkPosition = this.setChunkPosition.bind(this);
    this.checkCategoryChange = this.checkCategoryChange.bind(this);
    this.checkAnalyticsConditionAndTrack = this.checkAnalyticsConditionAndTrack.bind(this);
    this.filtersHaveChanged = this.filtersHaveChanged.bind(this);
    this.state = initialState;
  }

  componentDidMount() {
    const { requestFetchCategory, categoryCode } = this.props;
    requestFetchCategory(categoryCode);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.products.size > 0) {
      this.chunkerizeProductList(nextProps.products);
      if (!this.state.currentChunk) {
        this.updateCurrentChunkInState();
        this.trackCurrentChunk();
      }
    }

    if (this.filtersHaveChanged(nextProps)) {
      this.setChunkPosition(0, 1);
      this.updateCurrentChunkInState();
    }
  }

  componentDidUpdate(nextProps, nextState) {
    this.checkCategoryChange({ nextProps, nextState });
    this.checkAnalyticsConditionAndTrack({ nextProps, nextState });
  }

  componentWillUnmount() {
    this.props.clearProductList();
  }

  onBadgeClick(product, index) {
    this.props.setAnalyticsProductClick({ product, index });
  }

  onLeftSwipe() {
    let { currentChunkIndex, appendChunkIndex } = this.state;
    if (appendChunkIndex <= this.productsChunk.size - 1 && !this.flagOnSwipe) {
      this.flagOnSwipe = true;
      this.setChunkPosition(currentChunkIndex += 1, appendChunkIndex += 1, 'left');
      setTimeout(() => this.flagOnSwipe = false, this.timeout); //eslint-disable-line
    }
  }

  onRightSwipe() {
    let { currentChunkIndex, appendChunkIndex } = this.state;
    if (currentChunkIndex > 0 && !this.flagOnSwipe) {
      this.flagOnSwipe = true;
      this.setChunkPosition(currentChunkIndex -= 1, appendChunkIndex -= 1, 'right');
      setTimeout(() => this.flagOnSwipe = false, this.timeout); //eslint-disable-line
    }
  }

  setChunkPosition(currentChunkIndex, appendChunkIndex, swipe = this.state.swipe) {
    this.setState({ currentChunkIndex, appendChunkIndex, swipe });
  }

  getChunks() {
    let chunks = List();

    if (this.productsChunk.size > 0) {
      const { currentChunkIndex, appendChunkIndex } = this.state;
      const currChunkIsPresent = this.productsChunk.has(currentChunkIndex);
      let currentChunk = null;
      let appendChunk = null;

      if (currChunkIsPresent) {
        currentChunk = this.productsChunk.getIn([currentChunkIndex]);
        appendChunk = this.productsChunk.getIn([appendChunkIndex]);
      } else {
        this.setState({ currentChunkIndex: 0, appendChunkIndex: 1 });
      }

      chunks = appendChunk ? currentChunk.concat(appendChunk) : currentChunk;
    }
    return chunks ? chunks : List(); // eslint-disable-line
  }

  getCurrentChunk() {
    const currentChunksSize = this.getChunks().size;
    return currentChunksSize > this.chunkSize
      ? this.getChunks().setSize(this.chunkSize)
      : this.getChunks().setSize(currentChunksSize);
  }

  filtersHaveChanged(nextProps) {
    return !(nextProps.filterMap.get('filters').equals(this.props.filterMap.get('filters')))
    || !(nextProps.filterMap.get('availability') === this.props.filterMap.get('availability'))
    || !(nextProps.filterMap.get('aid') === this.props.filterMap.get('aid'));
  }

  checkAnalyticsConditionAndTrack({ nextProps, nextState }) {
    const filtersHaveChanged = this.filtersHaveChanged(nextProps);
    const currentChunkChanged = nextState.currentChunkIndex !== this.state.currentChunkIndex;
    const swipe = Math.abs(nextState.currentChunkIndex - this.state.currentChunkIndex) === 1;

    if (filtersHaveChanged) {
      this.trackChunk();
    }

    if (currentChunkChanged && swipe && !filtersHaveChanged) {
      this.updateCurrentChunkInState();
      this.trackCurrentChunk();
    }
  }

  checkCategoryChange({ nextProps }) {
    if (nextProps.categoryCode !== this.props.categoryCode) {
      this.props.requestFetchCategory(this.props.categoryCode);
    }
  }

  updateCurrentChunkInState() {
    this.setState({ currentChunk: this.getCurrentChunk() });
  }

  chunkerizeProductList(productList) {
    this.productsChunk = fromJS(chunk(productList.toJS(), this.chunkSize));
  }

  trackCurrentChunk() {
    const positionIndex = this.chunkSize * this.state.currentChunkIndex;

    this.props.trackCatalogueProductsChunk({
      products: this.getCurrentChunk(),
      positionIndex
    });
  }

  trackChunk(index = 0) {
    const positionIndex = this.chunkSize * index;

    this.props.trackCatalogueProductsChunk({
      products: this.productsChunk.get(0),
      positionIndex
    });
  }

  renderProducts(products) {
    return products.map((p, index) =>
      <Link
        onClick={() => this.onBadgeClick(p, index)}
        to={`product/${p.get('code')}`} key={p.get('code')}
      >
        <ProductBadge productInfo={p} animated={index < 4} animatedDirection={this.state.swipe} />
      </Link>
    );
  }

  render() {
    const { products, categoryCode, isDialogOpen } = this.props;

    if (!products.size > 0) {
      return null;
    }

    const chunks = this.getChunks();

    return (

{getCategoryName(categoryCode)}

        <FilterBar categoryCode={categoryCode} />
        <Swipeable
          onSwipingLeft={this.onLeftSwipe}
          onSwipingRight={this.onRightSwipe}
          {...swipeableConfig}
        >
          <ProductSlider opacity={isDialogOpen}>
            {this.renderProducts(chunks)}
          </ProductSlider>
        </Swipeable>

    );
  }
}

const Header = glamorous.div({
  width: '100%',
  height: '184px',
  background: '#f7f7f7',
  display: 'flex',
  justifyContent: 'center',
  alignItems: 'center',
  '&>h1': {
    fontSize: '48px'
  }
});

const ProductSlider = glamorous.div(({ opacity = false }) => ({
  marginTop: '5%',
  display: 'flex',
  overflowX: 'hidden',
  flexFlow: 'column wrap',
  alignContent: 'flex-start',
  height: '1246px',
  opacity: opacity ? 0.17 : 1,
  '&>a': {
    width: '405px',
    height: '593px',
    marginRight: '20px',
    '&:nth-child(odd)': {
      marginBottom: '60px'
    }
  }
}));

const swipeableConfig = {
  flickThreshold: 0.3,
  preventDefaultTouchmoveEvent: true,
  delta: 40
};

const initialState = {
  currentChunkIndex: 0,
  appendChunkIndex: 1,
  swipe: 'left',
  currentChunk: null
};

<commit_msg>fix wrong position on product click<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import glamorous from 'glamorous';
import Swipeable from 'react-swipeable';
import chunk from 'lodash/chunk';
import { Link } from 'react-router';
import { List, fromJS } from 'immutable';
import { getCategoryName } from '../utils/utils';

import ProductBadge from './ProductBadge';
import FilterBar from './FilterBar';

export default class Catalogue2 extends Component {
  static propTypes = {
    categoryCode: PropTypes.string.isRequired,
    requestFetchCategory: PropTypes.func.isRequired,
    clearProductList: PropTypes.func.isRequired,
    setAnalyticsProductClick: PropTypes.func.isRequired,
    trackCatalogueProductsChunk: PropTypes.func.isRequired,
    products: ImmutablePropTypes.list,
    isDialogOpen: PropTypes.bool.isRequired,
    filterMap: ImmutablePropTypes.map.isRequired
  };

  static defaultProps = {
    products: List()
  };

  constructor(props) {
    super(props);
    this.flagOnSwipe = false;
    this.chunkSize = 4;
    this.productsChunk = List();
    this.timeout = 1000;
    this.renderProducts = this.renderProducts.bind(this);
    this.onBadgeClick = this.onBadgeClick.bind(this);
    this.onLeftSwipe = this.onLeftSwipe.bind(this);
    this.onRightSwipe = this.onRightSwipe.bind(this);
    this.chunkerizeProductList = this.chunkerizeProductList.bind(this);
    this.getChunks = this.getChunks.bind(this);
    this.trackCurrentChunk = this.trackCurrentChunk.bind(this);
    this.updateCurrentChunkInState = this.updateCurrentChunkInState.bind(this);
    this.setChunkPosition = this.setChunkPosition.bind(this);
    this.checkCategoryChange = this.checkCategoryChange.bind(this);
    this.checkAnalyticsConditionAndTrack = this.checkAnalyticsConditionAndTrack.bind(this);
    this.filtersHaveChanged = this.filtersHaveChanged.bind(this);
    this.state = initialState;
  }

  componentDidMount() {
    const { requestFetchCategory, categoryCode } = this.props;
    requestFetchCategory(categoryCode);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.products.size > 0) {
      this.chunkerizeProductList(nextProps.products);
      if (!this.state.currentChunk) {
        this.updateCurrentChunkInState();
        this.trackCurrentChunk();
      }
    }

    if (this.filtersHaveChanged(nextProps)) {
      this.setChunkPosition(0, 1);
      this.updateCurrentChunkInState();
    }
  }

  componentDidUpdate(nextProps, nextState) {
    this.checkCategoryChange({ nextProps, nextState });
    this.checkAnalyticsConditionAndTrack({ nextProps, nextState });
  }

  componentWillUnmount() {
    this.props.clearProductList();
  }

  onBadgeClick(product, index) {
    const position = (this.state.currentChunkIndex * this.chunkSize) + index + 1;
    this.props.setAnalyticsProductClick({ product, index: position });
  }

  onLeftSwipe() {
    let { currentChunkIndex, appendChunkIndex } = this.state;
    if (appendChunkIndex <= this.productsChunk.size - 1 && !this.flagOnSwipe) {
      this.flagOnSwipe = true;
      this.setChunkPosition(currentChunkIndex += 1, appendChunkIndex += 1, 'left');
      setTimeout(() => this.flagOnSwipe = false, this.timeout); //eslint-disable-line
    }
  }

  onRightSwipe() {
    let { currentChunkIndex, appendChunkIndex } = this.state;
    if (currentChunkIndex > 0 && !this.flagOnSwipe) {
      this.flagOnSwipe = true;
      this.setChunkPosition(currentChunkIndex -= 1, appendChunkIndex -= 1, 'right');
      setTimeout(() => this.flagOnSwipe = false, this.timeout); //eslint-disable-line
    }
  }

  setChunkPosition(currentChunkIndex, appendChunkIndex, swipe = this.state.swipe) {
    this.setState({ currentChunkIndex, appendChunkIndex, swipe });
  }

  getChunks() {
    let chunks = List();

    if (this.productsChunk.size > 0) {
      const { currentChunkIndex, appendChunkIndex } = this.state;
      const currChunkIsPresent = this.productsChunk.has(currentChunkIndex);
      let currentChunk = null;
      let appendChunk = null;

      if (currChunkIsPresent) {
        currentChunk = this.productsChunk.getIn([currentChunkIndex]);
        appendChunk = this.productsChunk.getIn([appendChunkIndex]);
      } else {
        this.setState({ currentChunkIndex: 0, appendChunkIndex: 1 });
      }

      chunks = appendChunk ? currentChunk.concat(appendChunk) : currentChunk;
    }
    return chunks ? chunks : List(); // eslint-disable-line
  }

  getCurrentChunk() {
    const currentChunksSize = this.getChunks().size;
    return currentChunksSize > this.chunkSize
      ? this.getChunks().setSize(this.chunkSize)
      : this.getChunks().setSize(currentChunksSize);
  }

  filtersHaveChanged(nextProps) {
    return !(nextProps.filterMap.get('filters').equals(this.props.filterMap.get('filters')))
    || !(nextProps.filterMap.get('availability') === this.props.filterMap.get('availability'))
    || !(nextProps.filterMap.get('aid') === this.props.filterMap.get('aid'));
  }

  checkAnalyticsConditionAndTrack({ nextProps, nextState }) {
    const filtersHaveChanged = this.filtersHaveChanged(nextProps);
    const currentChunkChanged = nextState.currentChunkIndex !== this.state.currentChunkIndex;
    const swipe = Math.abs(nextState.currentChunkIndex - this.state.currentChunkIndex) === 1;

    if (filtersHaveChanged) {
      this.trackChunk();
    }

    if (currentChunkChanged && swipe && !filtersHaveChanged) {
      this.updateCurrentChunkInState();
      this.trackCurrentChunk();
    }
  }

  checkCategoryChange({ nextProps }) {
    if (nextProps.categoryCode !== this.props.categoryCode) {
      this.props.requestFetchCategory(this.props.categoryCode);
    }
  }

  updateCurrentChunkInState() {
    this.setState({ currentChunk: this.getCurrentChunk() });
  }

  chunkerizeProductList(productList) {
    this.productsChunk = fromJS(chunk(productList.toJS(), this.chunkSize));
  }

  trackCurrentChunk() {
    const positionIndex = this.chunkSize * this.state.currentChunkIndex;

    this.props.trackCatalogueProductsChunk({
      products: this.getCurrentChunk(),
      positionIndex
    });
  }

  trackChunk(index = 0) {
    const positionIndex = this.chunkSize * index;

    this.props.trackCatalogueProductsChunk({
      products: this.productsChunk.get(0),
      positionIndex
    });
  }

  renderProducts(products) {
    return products.map((p, index) =>
      <Link
        onClick={() => this.onBadgeClick(p, index)}
        to={`product/${p.get('code')}`} key={p.get('code')}
      >
        <ProductBadge productInfo={p} animated={index < 4} animatedDirection={this.state.swipe} />
      </Link>
    );
  }

  render() {
    const { products, categoryCode, isDialogOpen } = this.props;

    if (!products.size > 0) {
      return null;
    }

    const chunks = this.getChunks();

    return (

{getCategoryName(categoryCode)}

        <FilterBar categoryCode={categoryCode} />
        <Swipeable
          onSwipingLeft={this.onLeftSwipe}
          onSwipingRight={this.onRightSwipe}
          {...swipeableConfig}
        >
          <ProductSlider opacity={isDialogOpen}>
            {this.renderProducts(chunks)}
          </ProductSlider>
        </Swipeable>

    );
  }
}

const Header = glamorous.div({
  width: '100%',
  height: '184px',
  background: '#f7f7f7',
  display: 'flex',
  justifyContent: 'center',
  alignItems: 'center',
  '&>h1': {
    fontSize: '48px'
  }
});

const ProductSlider = glamorous.div(({ opacity = false }) => ({
  marginTop: '5%',
  display: 'flex',
  overflowX: 'hidden',
  flexFlow: 'column wrap',
  alignContent: 'flex-start',
  height: '1246px',
  opacity: opacity ? 0.17 : 1,
  '&>a': {
    width: '405px',
    height: '593px',
    marginRight: '20px',
    '&:nth-child(odd)': {
      marginBottom: '60px'
    }
  }
}));

const swipeableConfig = {
  flickThreshold: 0.3,
  preventDefaultTouchmoveEvent: true,
  delta: 40
};

const initialState = {
  currentChunkIndex: 0,
  appendChunkIndex: 1,
  swipe: 'left',
  currentChunk: null
};

<|endoftext|>


<commit_before>'use strict';

const test = require('tape');
const CPU = require('./3502');
const Memory = require('./memory');
const assembler = require('./as');
const {decode_instruction, disasm1, disasm} = require('./instr_decode');
const {OP, ADDR_MODE, FLAGS, XOP} = require('./opcodes');
const {add, inc, dec} = require('./arithmetic');

test('halts', (t) => {
  const cpu = CPU();

  cpu.memory.write(0, -118); // iii0i HALT_Z
  cpu.run();
  t.end();
});

test('branch instruction decoding', (t) => {

  const machine_code = assembler(['BRSNZ #+121']);

  const di = decode_instruction(machine_code[0]);

  console.log(di);
  t.equal(di.flag, FLAGS.S);  // sign (S)
  t.equal(di.direction, 1);   // not equal (N)
  t.equal(di.compare, 0);     // zero (Z)
  t.end();
});

test('memory read/write', (t) => {
  const memory = Memory({tryteCount:9});

  t.equal(memory.read(0), 0); // 0 initialized

  memory.write(0, 42);
  t.equal(memory.read(0), 42);

  /* TODO
  t.equal(memory.read(-1), 0);
  memory.write(-1, 33);
  t.equal(memory.read(-1), 33);
  */

  t.end();
});

test('memory maps', (t) => {

  let trappedRead;
  let trappedWrite, trappedWriteValue;

  const memory = Memory({
    tryteCount:9, // ±4
    map: {
      trap1: {
        start: 3,
        end: 4,
        read: (address) => {
          console.log('trap1 read',address);
          trappedRead = address;
          return 42;
        },
        write: (address, value) => {
          console.log('trap1 write',address,value);
          trappedWrite = address;
          trappedWriteValue = value;
        },
      }
    }
  });

  t.equal(memory.read(0), 0);
  memory.write(0, 1);
  t.equal(memory.read(0), 1);
  t.equal(trappedRead === undefined, true);
  t.equal(trappedWrite === undefined, true);
  t.equal(trappedWriteValue === undefined, true);

  t.equal(memory.read(3), 42);
  t.equal(trappedRead, 3);
  t.equal(trappedWrite === undefined, true);
  t.equal(trappedWriteValue === undefined, true);

  t.equal(memory.read(4), 42);
  t.equal(trappedRead, 4);
  t.equal(trappedWrite === undefined, true);
  t.equal(trappedWriteValue === undefined, true);

  memory.write(4, 33);
  t.equal(trappedWrite, 4);
  t.equal(trappedWriteValue, 33);

  t.end();
});

test('arithmetic', (t) => {
  t.deepEqual(add(1, 2), {result:3, overflow:0});
  t.deepEqual(add(1, -2), {result:-1, overflow:0});
  t.deepEqual(add(1, -1), {result:0, overflow:0});

  //   11111
  // +     1
  // -------
  //  1iiiii
  // / ^^^^^
  //v   result
  t.deepEqual(add(121, 1), {result:-121, overflow:1}); // overflow wraps around

  t.deepEqual(add(121, 2), {result:-120, overflow:1});
  t.deepEqual(add(121, 121), {result:-1, overflow:1});

  t.deepEqual(add(0, 0), {result:0, overflow:0});
  t.deepEqual(add(0, -1), {result:-1, overflow:0});
  t.deepEqual(add(-1, -2), {result:-3, overflow:0});

  t.deepEqual(add(-121, -1), {result:121, overflow:-1}); // underflow
  t.deepEqual(add(-121, -121), {result:1, overflow:-1});

  t.deepEqual(add(-121, -121, 0), {result:1, overflow:-1});
  t.deepEqual(add(-121, -121, 1), {result:2, overflow:-1});
  t.deepEqual(add(-121, -121, -1), {result:0, overflow:-1});
  t.deepEqual(add(-121, 0, -1), {result:121, overflow:-1});

  t.end();
});

test('assemble/disassemble roundtrip', (t) => {
  // test assembly -> machine code -> assembly
  // note some instructions can be written multiple ways;
  // only test canonical forms
  let lines = [
      'LDA #%ii1i0',
      'NOP A',
      'NOP #%iiiii', // -121',
      'NOP 29524',
      'BRSNZ #-1', //'BNE -1',
      'BRSEZ #+2', //BEQ +2
      'HALT_N',
      'HALT_P',
      'LDA #%1iii0', // #42
      'STA 0',
      'PTI A',
      'TAX',
      'HALT_Z'];

  const machine_code = assembler(lines);

  const dis = disasm(machine_code);

  console.log(dis);

  for (let i = 0; i < lines.length; ++i) {
    const before = lines[i];
    const after = dis[i];

    t.equal(before, after);
  }
  t.end();
});

test('assembler directive .equ absolute', (t) => {
  const machine_code = assembler([
    '.equ -29524 foo',
    'STA foo',
    ]);

  t.equal(machine_code[0], 33);   // STA absolute
  t.equal(machine_code[1], -121); // absolute address
  t.equal(machine_code[2], -121); // absolute address

  t.end();
});

test('assembler directive .equ immediate', (t) => {
  const machine_code = assembler([
    '.equ -100 foo',
    'STA #foo'
    ]);

  t.equal(machine_code[0], 39);   // STA immediate
  t.equal(machine_code[1], -100); // immediate value

  t.end();
});

test('assembly labels', (t) => {
  let machine_code = assembler([
    'foo1:',
    'BNE foo1'
    ]);
  t.equal(machine_code[0], 10); // BNE
  t.equal(machine_code[1], -2); // relative address

  // immediate, relative address
  machine_code = assembler([
    'BNE #-1'
    ]);
  t.equal(machine_code[0], 10); // BNE
  t.equal(machine_code[1], -1);

  machine_code = assembler([
    'BNE #1'
    ]);
  t.equal(machine_code[0], 10); // BNE
  t.equal(machine_code[1], 1);

  // absolute
  machine_code = assembler([
    'BNE 0'
    ]);
  t.equal(machine_code[0], 10); // BNE
  t.equal(machine_code[1], -2); // 2(end of this instruction) - -2(relative offset) = 0(absolute jump destination)

  machine_code = assembler([
    'NOP A',
    'NOP A',
    'foo1:',
    'BNE foo1'
    ]);

  t.equal(machine_code[0], 0);  // 0: NOP A
  t.equal(machine_code[1], 0);  // 0: NOP A
  t.equal(machine_code[2], 10); // 1: BNE
  t.equal(machine_code[3], -2); // 2: relative label

  t.end();
});

test('assembly branch out-of-range', (t) => {
  t.doesNotThrow(() => {
    let machine_code = assembler(['BNE 123']); // from 2 to 123 absolute, maximum range 121
    t.equal(machine_code[1], 121);

    machine_code = assembler(['BNE -119']); // from 2 to -119, minimum range -121
    t.equal(machine_code[1], -121);
  });

  t.throws(() => {
    const machine_code = assembler(['BNE 124']); // from 2 to 123, a branch too far (122)
    console.log(machine_code);
  });

  t.throws(() => {
    const machine_code = assembler(['BNE -120']); // from 2 to -120, a branch too far (-122)
    console.log(machine_code);
  });

  t.end();
});

test('execute', (t) => {
  const cpu = CPU();
  var lines = [
      'LDA #$ijk',
      'LDA #%ii1i0',
      'LDA #&QF',
      'NOP A',
      'NOP #-121',
      'NOP 29524',
      'LDA #0',
      'BNE #-1',  // not taken
      'BEQ #+2',  // taken
      'HALT_N',   // (skipped by above branch)
      'HALT_P',   // (also skipped)
      'LDA #42',
      'STA 0',    // [0] = 42

      'LDA #%00i01',
      'PTI A',

      'TAX',      // X = A
      'INX',
      'DEX',
      'DEX',

      'HALT_Z'];

  const machine_code = assembler(lines);
  cpu.memory.writeArray(0, machine_code);
  cpu.run();

  t.equal(cpu.memory.read(0), 42);

  t.equal(cpu.accum, 119);
  t.equal(cpu.index, 118);
  t.equal(cpu.yindex, 0);
  t.equal(cpu.pc, machine_code.length - 1);

  t.equal(cpu.get_flag(FLAGS.R), 0);
  t.equal(cpu.get_flag(FLAGS.H), 0);

  t.end();
});

test('adc overflow flag', (t) => {
  const cpu = CPU();
  var lines = [
    'LDA #121',
    'ADC #121',
    'HALT_Z'
  ];

  const machine_code = assembler(lines);
  cpu.memory.writeArray(0, machine_code);
  cpu.run();

  //  11111
  //  11111
  // 10000i
  // Vaccum
  t.equal(cpu.accum, -1);
  t.equal(cpu.get_flag(FLAGS.V), 1);

  t.end();
});

test('clear overflow flag', (t) => {
  const cpu = CPU();
  var lines = [
    'LDA #121',
    'ADC #121', // V = 1
    'CLV',      // V = 0
    'SECN',     // C = -1
    'HALT_Z'
  ];

  const machine_code = assembler(lines);
  cpu.memory.writeArray(0, machine_code);
  cpu.run();

  t.equal(cpu.get_flag(FLAGS.V), 0);
  t.equal(cpu.get_flag(FLAGS.C), -1);

  t.end();
});
<commit_msg>Test ADC with set carry flag<commit_after>'use strict';

const test = require('tape');
const CPU = require('./3502');
const Memory = require('./memory');
const assembler = require('./as');
const {decode_instruction, disasm1, disasm} = require('./instr_decode');
const {OP, ADDR_MODE, FLAGS, XOP} = require('./opcodes');
const {add, inc, dec} = require('./arithmetic');

test('halts', (t) => {
  const cpu = CPU();

  cpu.memory.write(0, -118); // iii0i HALT_Z
  cpu.run();
  t.end();
});

test('branch instruction decoding', (t) => {

  const machine_code = assembler(['BRSNZ #+121']);

  const di = decode_instruction(machine_code[0]);

  console.log(di);
  t.equal(di.flag, FLAGS.S);  // sign (S)
  t.equal(di.direction, 1);   // not equal (N)
  t.equal(di.compare, 0);     // zero (Z)
  t.end();
});

test('memory read/write', (t) => {
  const memory = Memory({tryteCount:9});

  t.equal(memory.read(0), 0); // 0 initialized

  memory.write(0, 42);
  t.equal(memory.read(0), 42);

  /* TODO
  t.equal(memory.read(-1), 0);
  memory.write(-1, 33);
  t.equal(memory.read(-1), 33);
  */

  t.end();
});

test('memory maps', (t) => {

  let trappedRead;
  let trappedWrite, trappedWriteValue;

  const memory = Memory({
    tryteCount:9, // ±4
    map: {
      trap1: {
        start: 3,
        end: 4,
        read: (address) => {
          console.log('trap1 read',address);
          trappedRead = address;
          return 42;
        },
        write: (address, value) => {
          console.log('trap1 write',address,value);
          trappedWrite = address;
          trappedWriteValue = value;
        },
      }
    }
  });

  t.equal(memory.read(0), 0);
  memory.write(0, 1);
  t.equal(memory.read(0), 1);
  t.equal(trappedRead === undefined, true);
  t.equal(trappedWrite === undefined, true);
  t.equal(trappedWriteValue === undefined, true);

  t.equal(memory.read(3), 42);
  t.equal(trappedRead, 3);
  t.equal(trappedWrite === undefined, true);
  t.equal(trappedWriteValue === undefined, true);

  t.equal(memory.read(4), 42);
  t.equal(trappedRead, 4);
  t.equal(trappedWrite === undefined, true);
  t.equal(trappedWriteValue === undefined, true);

  memory.write(4, 33);
  t.equal(trappedWrite, 4);
  t.equal(trappedWriteValue, 33);

  t.end();
});

test('arithmetic', (t) => {
  t.deepEqual(add(1, 2), {result:3, overflow:0});
  t.deepEqual(add(1, -2), {result:-1, overflow:0});
  t.deepEqual(add(1, -1), {result:0, overflow:0});

  //   11111
  // +     1
  // -------
  //  1iiiii
  // / ^^^^^
  //v   result
  t.deepEqual(add(121, 1), {result:-121, overflow:1}); // overflow wraps around

  t.deepEqual(add(121, 2), {result:-120, overflow:1});
  t.deepEqual(add(121, 121), {result:-1, overflow:1});

  t.deepEqual(add(0, 0), {result:0, overflow:0});
  t.deepEqual(add(0, -1), {result:-1, overflow:0});
  t.deepEqual(add(-1, -2), {result:-3, overflow:0});

  t.deepEqual(add(-121, -1), {result:121, overflow:-1}); // underflow
  t.deepEqual(add(-121, -121), {result:1, overflow:-1});

  t.deepEqual(add(-121, -121, 0), {result:1, overflow:-1});
  t.deepEqual(add(-121, -121, 1), {result:2, overflow:-1});
  t.deepEqual(add(-121, -121, -1), {result:0, overflow:-1});
  t.deepEqual(add(-121, 0, -1), {result:121, overflow:-1});

  t.end();
});

test('assemble/disassemble roundtrip', (t) => {
  // test assembly -> machine code -> assembly
  // note some instructions can be written multiple ways;
  // only test canonical forms
  let lines = [
      'LDA #%ii1i0',
      'NOP A',
      'NOP #%iiiii', // -121',
      'NOP 29524',
      'BRSNZ #-1', //'BNE -1',
      'BRSEZ #+2', //BEQ +2
      'HALT_N',
      'HALT_P',
      'LDA #%1iii0', // #42
      'STA 0',
      'PTI A',
      'TAX',
      'HALT_Z'];

  const machine_code = assembler(lines);

  const dis = disasm(machine_code);

  console.log(dis);

  for (let i = 0; i < lines.length; ++i) {
    const before = lines[i];
    const after = dis[i];

    t.equal(before, after);
  }
  t.end();
});

test('assembler directive .equ absolute', (t) => {
  const machine_code = assembler([
    '.equ -29524 foo',
    'STA foo',
    ]);

  t.equal(machine_code[0], 33);   // STA absolute
  t.equal(machine_code[1], -121); // absolute address
  t.equal(machine_code[2], -121); // absolute address

  t.end();
});

test('assembler directive .equ immediate', (t) => {
  const machine_code = assembler([
    '.equ -100 foo',
    'STA #foo'
    ]);

  t.equal(machine_code[0], 39);   // STA immediate
  t.equal(machine_code[1], -100); // immediate value

  t.end();
});

test('assembly labels', (t) => {
  let machine_code = assembler([
    'foo1:',
    'BNE foo1'
    ]);
  t.equal(machine_code[0], 10); // BNE
  t.equal(machine_code[1], -2); // relative address

  // immediate, relative address
  machine_code = assembler([
    'BNE #-1'
    ]);
  t.equal(machine_code[0], 10); // BNE
  t.equal(machine_code[1], -1);

  machine_code = assembler([
    'BNE #1'
    ]);
  t.equal(machine_code[0], 10); // BNE
  t.equal(machine_code[1], 1);

  // absolute
  machine_code = assembler([
    'BNE 0'
    ]);
  t.equal(machine_code[0], 10); // BNE
  t.equal(machine_code[1], -2); // 2(end of this instruction) - -2(relative offset) = 0(absolute jump destination)

  machine_code = assembler([
    'NOP A',
    'NOP A',
    'foo1:',
    'BNE foo1'
    ]);

  t.equal(machine_code[0], 0);  // 0: NOP A
  t.equal(machine_code[1], 0);  // 0: NOP A
  t.equal(machine_code[2], 10); // 1: BNE
  t.equal(machine_code[3], -2); // 2: relative label

  t.end();
});

test('assembly branch out-of-range', (t) => {
  t.doesNotThrow(() => {
    let machine_code = assembler(['BNE 123']); // from 2 to 123 absolute, maximum range 121
    t.equal(machine_code[1], 121);

    machine_code = assembler(['BNE -119']); // from 2 to -119, minimum range -121
    t.equal(machine_code[1], -121);
  });

  t.throws(() => {
    const machine_code = assembler(['BNE 124']); // from 2 to 123, a branch too far (122)
    console.log(machine_code);
  });

  t.throws(() => {
    const machine_code = assembler(['BNE -120']); // from 2 to -120, a branch too far (-122)
    console.log(machine_code);
  });

  t.end();
});

test('execute', (t) => {
  const cpu = CPU();
  var lines = [
      'LDA #$ijk',
      'LDA #%ii1i0',
      'LDA #&QF',
      'NOP A',
      'NOP #-121',
      'NOP 29524',
      'LDA #0',
      'BNE #-1',  // not taken
      'BEQ #+2',  // taken
      'HALT_N',   // (skipped by above branch)
      'HALT_P',   // (also skipped)
      'LDA #42',
      'STA 0',    // [0] = 42

      'LDA #%00i01',
      'PTI A',

      'TAX',      // X = A
      'INX',
      'DEX',
      'DEX',

      'HALT_Z'];

  const machine_code = assembler(lines);
  cpu.memory.writeArray(0, machine_code);
  cpu.run();

  t.equal(cpu.memory.read(0), 42);

  t.equal(cpu.accum, 119);
  t.equal(cpu.index, 118);
  t.equal(cpu.yindex, 0);
  t.equal(cpu.pc, machine_code.length - 1);

  t.equal(cpu.get_flag(FLAGS.R), 0);
  t.equal(cpu.get_flag(FLAGS.H), 0);

  t.end();
});

test('adc overflow flag', (t) => {
  const cpu = CPU();
  var lines = [
    'LDA #121',
    'SECN',
    'ADC #121',
    'HALT_Z'
  ];

  const machine_code = assembler(lines);
  cpu.memory.writeArray(0, machine_code);
  cpu.run();

  //  11111
  //  11111
  //      i
  // 1000i1
  // Vaccum
  t.equal(cpu.accum, -2);
  t.equal(cpu.get_flag(FLAGS.V), 1);

  t.end();
});

test('clear overflow flag', (t) => {
  const cpu = CPU();
  var lines = [
    'LDA #121',
    'ADC #121', // V = 1
    'CLV',      // V = 0
    'SECN',     // C = -1
    'HALT_Z'
  ];

  const machine_code = assembler(lines);
  cpu.memory.writeArray(0, machine_code);
  cpu.run();

  t.equal(cpu.get_flag(FLAGS.V), 0);
  t.equal(cpu.get_flag(FLAGS.C), -1);

  t.end();
});
<|endoftext|>