Browse Source

WIP

master
Sven Slootweg 1 year ago
commit
05cb1eafe8
12 changed files with 3275 additions and 0 deletions
  1. +3
    -0
      .gitignore
  2. +864
    -0
      blah.html
  3. +232
    -0
      get-barcode.js
  4. +82
    -0
      index.js
  5. +112
    -0
      notes.txt
  6. +18
    -0
      package.json
  7. +55
    -0
      parse-foodbook.js
  8. +12
    -0
      shorten-url.js
  9. +1362
    -0
      test-foodbook.js
  10. +34
    -0
      test-json.js
  11. +11
    -0
      test.js
  12. +490
    -0
      yarn.lock

+ 3
- 0
.gitignore View File

@ -0,0 +1,3 @@
keys.txt
config.json
node_modules

+ 864
- 0
blah.html View File

@ -0,0 +1,864 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Digital Catalog</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="/prod/favicon.ico">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="apple-touch-icon" href="/prod/launcer-icon.png">
<meta name="apple-mobile-web-app-title" content="PS FoodBook">
<link href="/prod/Content/bootstrap.css?v=3.2.0" rel="stylesheet"/>
<link href="/prod/Content/bootstrap-treeview.css?v=3.2.0" rel="stylesheet"/>
<link href="/prod/Content/flexslider.css?v=3.2.0" rel="stylesheet"/>
<link href="/prod/Content/flags/flag-icon.min.css?v=3.2.0" rel="stylesheet"/>
<link href="/prod/Content/jquery.fancybox.min.css?v=3.2.0" rel="stylesheet"/>
<link href="/prod/Content/site.css?v=3.2.0" rel="stylesheet"/>
<script src="/prod/Scripts/modernizr-2.8.3.js?v=3.2.0"></script>
<script src="/prod/Scripts/jquery-2.1.1.js?v=3.2.0"></script>
<script src="/prod/Scripts/bootstrap.placeholder.min.js?v=3.2.0"></script>
<script src="/prod/Scripts/jquery.lazyload.min.js?v=3.2.0"></script>
<script src="/prod/Scripts/bootstrap.ListShowMoreLess.js?v=3.2.0"></script>
<script src="/prod/Scripts/jquery.flexslider-min.js?v=3.2.0"></script>
<script src="/prod/Scripts/bootstrap-treeview.js?v=3.2.0"></script>
<script src="/prod/Scripts/bootstrap.listfilter.js?v=3.2.0"></script>
<script src="/prod/Scripts/jquery.fancybox.min.js?v=3.2.0"></script>
<script src="/prod/Scripts/bootstrap.js?v=3.2.0"></script>
<script src="/prod/Scripts/Site.js?v=3.2.0"></script>
<script src="https://cdn.psinfoodservice.com/kendo/2019.1.220/js/kendo.all.min.js"></script>
<script src="https://cdn.psinfoodservice.com/kendo/2019.1.220/js/kendo.aspnetmvc.min.js"></script>
<script src="https://cdn.psinfoodservice.com/kendo/2019.1.220/js/cultures/kendo.culture.nl-NL.min.js"></script>
<link href="https://cdn.psinfoodservice.com/kendo/2019.1.220/styles/kendo.rtl.min.css" rel="stylesheet" />
<link href="https://cdn.psinfoodservice.com/kendo/2019.1.220/styles/kendo.bootstrap-v4.min.css" rel="stylesheet" />
<link href="https://cdn.psinfoodservice.com/fontawesome/5.0.8/web-fonts-with-css/css/fontawesome-all.min.css" rel="stylesheet" />
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="~/Scripts/html5shiv.min.js"></script>
<script src="~/Scripts/respond.min.js"></script>
<![endif]-->
<!-- Exception in styling for a specific Digital Catalog The defaults are set in site.css-->
<style>
a.mainLg:hover {
text-decoration: none !important;
}
</style>
<!-- custom banner style -->
</head>
<body>
<nav class="navbar navbar-fixed-top navbar-default" role="navigation">
<div class="container">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand visible-xs-inline" href="/prod/dc">Home</a>
<a class="navbar-brand hidden-xs mainLg " href="/prod/dc">
<div class="divMn">
<img alt="logo" class="logo hidden-xs" style="height: 40px;" src="/prod/content/logo/ps_logo.png" />
<span>
FoodBook | PS in foodservice
<span> - </span>NL </span>
</div>
</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li class="dropdown language">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
<span class="flag-icon flag-icon-nl flag-icon-menu"></span><span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="/prod/Account/ChangeCulture?culture=en-US&amp;redirectUrl=%2Fprod%2Fdc"><span class="flag-icon flag-icon-en flag-icon-menu"> </span></a></li>
<li><a href="/prod/Account/ChangeCulture?culture=nl-NL&amp;redirectUrl=%2Fprod%2Fdc"><span class="flag-icon flag-icon-nl flag-icon-menu"> </span></a></li>
<li><a href="/prod/Account/ChangeCulture?culture=fr-FR&amp;redirectUrl=%2Fprod%2Fdc"><span class="flag-icon flag-icon-fr flag-icon-menu"> </span></a></li>
<li><a href="/prod/Account/ChangeCulture?culture=de-DE&amp;redirectUrl=%2Fprod%2Fdc"><span class="flag-icon flag-icon-de flag-icon-menu"> </span></a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right" >
<li><a href="/prod/Account/Login">Login</a></li>
<li><a href="/prod/Account/Register">Registreren</a></li>
</ul> <ul class="nav navbar-nav navbar-right">
<li><a href="https://www.psinfoodservice.nl/contact/contact" target="_blank">Contact</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
<span>Selecteer FoodBook</span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="/prod/dc/be">FoodBook - BE</a></li>
</ul>
</li>
</ul> </div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container-fluid -->
</nav>
<div class="banner hidden-xs">
&nbsp;
</div>
<div class="container body-content">
<!--[if lt IE 9]>
<div class="alert alert-warning">
Deze site wordt mogelijk niet goed weergegeven in IE 8 of lager.
</div>
<![endif]-->
<script type="text/javascript">
function ToggleMenu() {
if ($('#filtersContainer').hasClass('hidden-xs')) {
$('#filtersContainer').removeClass('hidden-xs');
} else {
$('#filtersContainer').addClass('hidden-xs');
}
}
</script>
<div class="row">
<div class="col-xs-12 hidden-sm hidden-md hidden-lg">
<button class="btn btn-default" onclick="ToggleMenu();return false;" style="margin: 10px 0 10px 0;" id="filterMobileButton">
<span class="glyphicon glyphicon-filter" aria-hidden="true"></span>
Filters
</button>
</div>
</div>
<div class="row">
<div class="col-sm-4 hidden-xs" id="filtersContainer">
<script src="/prod/Scripts/barcoder.min.js"></script>
<style>
.k-window-titlebar {
color: #FFFFFF !important;
font-size: 120% !important;
background-color: #009ee0 !important;
}
</style>
<form id="filterForm" role="form" method="POST">
<input id="hiddenCollapseFilterId" name="SelectedFilterOptions.collapseFilterId" type="hidden" value="filterkeywordPanelBody" />
<input id="SelectedFilterOptions_FreeText" name="SelectedFilterOptions.FreeText" type="hidden" value="8710654160037" />
<input id="SelectedFilterOptions_ProductGroupId" name="SelectedFilterOptions.ProductGroupId" type="hidden" value="" />
<input id="SelectedFilterOptions_BrandId" name="SelectedFilterOptions.BrandId" type="hidden" value="" />
<input id="SelectedFilterOptions_GrossierId" name="SelectedFilterOptions.GrossierId" type="hidden" value="" />
<select class="hidden" id="SelectedFilterOptions_FreeFromAllergenIds" multiple="multiple" name="SelectedFilterOptions.FreeFromAllergenIds"><option value="7">rogge</option>
<option value="27">gerst</option>
<option value="28">haver</option>
<option value="29">spelt</option>
<option value="12">schaaldieren</option>
<option value="3">ei</option>
<option value="11">vis</option>
<option value="17">pinda&#39;s</option>
<option value="32">hazelnoten</option>
<option value="33">walnoten</option>
<option value="34">cashewnoten</option>
<option value="35">pecannoten</option>
<option value="36">paranoten</option>
<option value="37">pistachenoten</option>
<option value="38">macadamianoten</option>
<option value="21">selderij</option>
<option value="24">mosterd</option>
<option value="22">sesam</option>
<option value="19">sulfiet (E220 - E228)</option>
<option value="23">lupine</option>
<option value="26">weekdieren</option>
</select>
<select class="hidden" id="SelectedFilterOptions_ContainsAllergenIds" multiple="multiple" name="SelectedFilterOptions.ContainsAllergenIds"><option value="5">glutenbevattende granen</option>
<option value="6">tarwe</option>
<option value="4">soja</option>
<option value="1">melk</option>
<option value="16">noten</option>
<option value="31">amandelen</option>
</select>
<select class="hidden" id="SelectedFilterOptions_QualityMarkIds" multiple="multiple" name="SelectedFilterOptions.QualityMarkIds"></select>
<select class="hidden" id="SelectedFilterOptions_CharacteristicIds" multiple="multiple" name="SelectedFilterOptions.CharacteristicIds"><option value="4">Vrij van glutamaat</option>
<option value="7">Vrij van kip en gevogelte</option>
<option value="14">Vrij van rundvlees</option>
<option value="20">Vrij van varkensvlees</option>
</select>
<div class="panel-group" id="filterAccordion" role="tablist" aria-multiselectable="false">
<!-- Filter op keywords -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="filterkeywordPanel">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#filterAccordion" href="#filterkeywordPanelBody"
aria-expanded="false" aria-controls="filterkeywordPanelBody">EAN of artikelnummer</a>
</h4>
</div>
<div id="filterkeywordPanelBody" class="panel-collapse collapse" role="tabpanel"
aria-labelledby="filterkeywordPanel">
<div class="panel-body">
<!--EAN-->
<div class="input-group">
<input class="form-control" id="SelectedFilterOptions_Ean" name="SelectedFilterOptions.Ean" placeholder="EAN-nummer" type="text" value="" />
<span class="input-group-btn">
<span id="btnSubmitGtinSearch" class="btn btn-default">Filter</span>
</span>
</div>
<!--Producentnummer-->
<div class="input-group">
<input class="form-control" id="SelectedFilterOptions_ProducerNumber" name="SelectedFilterOptions.ProducerNumber" placeholder="Artikelnummer producent" type="text" value="" />
<span class="input-group-btn">
<input type="submit" class="btn btn-default" value="Filter" />
</span>
</div>
<!-- Artikelnummer Grossier -->
<div class="input-group">
<input class="form-control" id="SelectedFilterOptions_GrossierNumber" name="SelectedFilterOptions.GrossierNumber" placeholder="Groothandelnummer" type="text" value="" />
<!-- todo: aanpassen naar naam van catalog!-->
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span>Groothandel</span>
<span class="caret"> </span>
</button>
<ul class="dropdown-menu" role="menu">
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 18978, true)">Advion BV</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 12006, true)">Bakeplus Holding b.v.</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 4189, true)">Bidfood (Deli XL)</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 13465, true)">Booijmans Foodimpuls</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 11960, true)">Boonstra Verswaren</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 7183, true)">Brugel groothandel</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 8997, true)">Bunzl</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 13458, true)">Chefs Culinar Nederland</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 7241, true)">De Kweker</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 13501, true)">Deelen Verswaren BV</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 10897, true)">DG Grootverbruik</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 10720, true)">Dijkgraaf Reinders</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 12865, true)">Food Focus</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 6581, true)">Gepu</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 7081, true)">Haarsma Foodimpuls</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 19008, true)">Holland Food Service | Lekker Verantwoord</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 6086, true)">Holland Food Service bv</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 6828, true)">Horesca At&#233;</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 6415, true)">Horesca Lieferink | Locatie Twello</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 11112, true)">Horesca Lieferink | Locatie Zeist</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 6390, true)">Horesca Meppel</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 12561, true)">Horesca Smulders Horeca Grootverbruik bv</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 9526, true)">Huuskes Versproducten</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 18825, true)">IJsExpress</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 14535, true)">Krijt Moester</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 11297, true)">Langerak de Jong</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 11280, true)">Makro</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 3983, true)">Sligro Food Group Nederland BV</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 18133, true)">Telesuper</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 8535, true)">Totaalvers</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 12121, true)">Van Altena groothandel</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 12197, true)">Van de Ven Groothandel</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 11432, true)">VAV groep</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 16327, true)">Verscentrum Nederland</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 4617, true)">VHC Actifood bv</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 6358, true)">Zegro Centrum Rotterdam bv</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 12276, true)">Zuivelgroothandel Kroon</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 12147, true)">Palveco | Palv&#233; versgroep</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 12460, true)">Versalert</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 9576, true)">Jelco BV | Palv&#233; versgroep</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 13251, true)">Volfood Versgroothandel</a>
</li>
<li>
<a href="#" onclick="addFilterPS('SelectedFilterOptions_GrossierId', 6356, true)">Veldboer Eenhoorn</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Filter op productgroepen -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="filterGroupPanel">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#filterAccordion" href="#filterGroupPanelBody"
aria-expanded="false" aria-controls="filterGroupPanelBody">
Productgroepen
</a>
</h4>
</div>
<div id="filterGroupPanelBody" class="panel-collapse collapse" role="tabpanel"
aria-labelledby="filterGroupPanel">
<div class="panel-body">
<div class="k-widget k-treeview k-reset" id="TreeViewTest"><ul class="k-group"><li class="k-item k-first k-last" data-expanded="true" data-id="64"><div class="k-top k-bot"><span class="k-icon k-i-collapse"></span><span class="k-in">Brood en banket</span></div><ul class="k-group"><li class="k-item k-last" data-expanded="true" data-id="131"><div class="k-top k-bot"><span class="k-icon k-i-collapse"></span><span class="k-in">Cake en koek</span></div><ul class="k-group"><li class="k-item k-last" data-id="317"><div class="k-top k-bot"><span class="k-in">Grote koeken</span></div></li></ul></li></ul></li></ul></div><script>
kendo.syncReady(function(){jQuery("#TreeViewTest").kendoTreeView({"select":onSelect});});
</script>
</div>
</div>
</div>
<!-- Filter op brands -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="filterBrandsPanel">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#filterAccordion" href="#filterBrandsPanelBody"
aria-expanded="false" aria-controls="filterBrandsPanelBody">Merken</a>
</h4>
</div>
<div id="filterBrandsPanelBody" class="panel-collapse collapse" role="tabpanel" aria-labelledby="filterBrandsPanel">
<div class="panel-body">
<div class="list-group-item">
<input id="PS_brands" name="PS_brands" style="width:100%;" type="text" /><script>
kendo.syncReady(function(){jQuery("#PS_brands").kendoComboBox({"change":selectPSbrand,"dataSource":{"transport":{"read":{"url":"/prod/dc/nl/12345/ps/FilterBrands","data":function() { return kendo.ui.ComboBox.requestData(jQuery("#PS_brands")); }},"prefix":""},"serverFiltering":true,"filter":[],"schema":{"errors":"Errors"}},"dataTextField":"Name","filter":"contains","noDataTemplate":"Geen resultaten","minLength":2,"autoBind":true,"dataValueField":"Id","placeholder":"Zoek op merknaam"});});
</script>
</div>
<div class="showmoreless list-group-item">
<span>1 merk</span>
</div>
</div>
</div>
</div>
<!-- Filter op allergens free from -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="filterAllergenFreeFromPanel">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#filterAccordion" href="#filterAllergenFreeFromPanelBody"
aria-expanded="false" aria-controls="filterAllergenFreeFromPanelBody">
Allergenen (bevat niet)
</a>
</h4>
</div>
<div id="filterAllergenFreeFromPanelBody" class="panel-collapse collapse" role="tabpanel"
aria-labelledby="filterAllergenFreeFromPanel">
<div class="panel-body">
<div class="list-group" id="allergenFreefromUnsortedList">
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 7)">
<span>rogge</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 27)">
<span>gerst</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 28)">
<span>haver</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 29)">
<span>spelt</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 12)">
<span>schaaldieren</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 3)">
<span>ei</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 11)">
<span>vis</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 17)">
<span>pinda&#39;s</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 32)">
<span>hazelnoten</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 33)">
<span>walnoten</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 34)">
<span>cashewnoten</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 35)">
<span>pecannoten</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 36)">
<span>paranoten</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 37)">
<span>pistachenoten</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 38)">
<span>macadamianoten</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 21)">
<span>selderij</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 24)">
<span>mosterd</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 22)">
<span>sesam</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 19)">
<span>sulfiet (E220 - E228)</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 23)">
<span>lupine</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_FreeFromAllergenIds', 26)">
<span>weekdieren</span>
</a>
</div>
</div>
</div>
</div>
<!-- Filter op allergens contains -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="filterAllergenContainPanel">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#filterAccordion" href="#filterAllergenContainPanelBody"
aria-expanded="false" aria-controls="filterAllergenContainPanelBody">
Allergenen (bevat)
</a>
</h4>
</div>
<div id="filterAllergenContainPanelBody" class="panel-collapse collapse" role="tabpanel" aria-labelledby="filterAllergenContainPanel">
<div class="panel-body">
<div class="list-group" id="allergenContainUnsortedList">
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_ContainsAllergenIds', 5)">
<span>glutenbevattende granen</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_ContainsAllergenIds', 6)">
<span>tarwe</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_ContainsAllergenIds', 4)">
<span>soja</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_ContainsAllergenIds', 1)">
<span>melk</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_ContainsAllergenIds', 16)">
<span>noten</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_ContainsAllergenIds', 31)">
<span>amandelen</span>
</a>
</div>
</div>
</div>
</div>
<!-- Filter op qualitymarks -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="filterQualityMarksPanel">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#filterAccordion" href="#filterQualityMarksPanelBody"
aria-expanded="false" aria-controls="filterQualityMarksPanelBody">Keurmerken</a>
</h4>
</div>
<div id="filterQualityMarksPanelBody" class="panel-collapse collapse" role="tabpanel"
aria-labelledby="filterQualityMarksPanel">
<div class="panel-body">
<div class="list-group" id="qualitymarkUnsortedList">
<span class="lblNoFilters"><i>Geen filters beschikbaar</i></span>
</div>
</div>
</div>
</div>
<!-- Filter op kenmerken-->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="filterCharacteristicsPanel">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#filterAccordion" href="#filterCharacteristicsPanelBody"
aria-expanded="false" aria-controls="filterCharacteristicsPanelBody">Kenmerken</a>
</h4>
</div>
<div id="filterCharacteristicsPanelBody" class="panel-collapse collapse" role="tabpanel"
aria-labelledby="filterCharacteristicsPanel">
<div class="panel-body">
<div class="list-group" id="characteristicUnsortedList">
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_CharacteristicIds', 4)">
<span>Vrij van glutamaat</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_CharacteristicIds', 7)">
<span>Vrij van kip en gevogelte</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_CharacteristicIds', 14)">
<span>Vrij van rundvlees</span>
</a>
<a class="list-group-item" href="#" onclick="addArrayFilter('SelectedFilterOptions_CharacteristicIds', 20)">
<span>Vrij van varkensvlees</span>
</a>
</div>
</div>
</div>
</div>
</div>
<!--end panel group-->
<!--
<button type="submit" class="btn btn-default">Filteren</button>
-->
</form>
<div data-role="dialog" id="dialog"></div><script>
kendo.syncReady(function(){jQuery("#dialog").kendoDialog({"title":"Zoekopdracht niet valide","visible":false,"width":460});});
</script>
<script language="javascript" type="text/javascript">
$(document).ready(function () {
var showMoreOptions = {
max: '5',
moreText: 'toon alle ([COUNT])',
lessText: 'toon minder'
};
$('#allergenFreefromUnsortedList').ListShowMoreLess(showMoreOptions);
$('#qualitymarkUnsortedList').ListShowMoreLess(showMoreOptions);
$('#allergenContainUnsortedList').ListShowMoreLess(showMoreOptions);
$('#characteristicUnsortedList').ListShowMoreLess(showMoreOptions);
});
function onSelect(e) {
addFilterPS('SelectedFilterOptions_ProductGroupId', this.dataItem(e.node).id);
}
function selectPSbrand(e) {
if (this.value()>0)
addFilterPS('SelectedFilterOptions_BrandId', this.value());
}
// GTIN VALIDATION
$('#btnSubmitGtinSearch').click(function () {
validateGtin($("#SelectedFilterOptions_Ean").val());
});
//$("#btnSubmitArtNrSearch").click(function () {
// validateGtin($("#SelectedFilterOptions_Ean").val());
//});
function centerKendoWindow(e) {
this.center();
}
function validateGtin(submittedEan) {
if (submittedEan == "") {
$('#filterForm').submit();
}
else {
validator = new Barcoder(); var validation1 = validator.validate(jQuery.trim(submittedEan));
//console.log('%s is valid: %s and has guessed type: %s', submittedEan, validation1.isValid, validation1.possibleType);
if (validation1.isValid) {
//console.log("VALID");
$('#filterForm').submit();
}
else {
//console.log("Je hebt een invalide EAN getoond");
var warning = "<p style='padding: 10px;'>Je hebt gezocht op EAN nummer <strong>" + submittedEan + "</strong>, dit is echter geen valide EAN</p>";
$('#dialog').data("kendoDialog").content(warning);
$('#dialog').data("kendoDialog").open();
}
}
}
</script>
</div>
<div class="col-sm-4 hidden-xs hidden" id="resultListLeft">
<ul class="media-list productresult">
<li class="media" onclick="loadDetailPage('/prod/dc/nl/12345/ps/ProductDetailPartial/6682'); return false;" title="Super appelkoeken">
<div class="media-left thumb text-center media-middle">
<div>
<img class="media-object assetimage" alt="media" src="https://permalink.psinfoodservice.com/prod/image/123492/05c5cd27-5def-41a1-808d-9a01eabc6c1e?w=60&amp;h=60" />
</div>
</div>
<div class="media-body media-middle">
<h4 class="media-heading">
Super appelkoeken
</h4>
<p class="list-group-item-text">
<small>de Molen&#39;s Banket</small><br />
<small>EAN:&nbsp;8710654160037, 8710654000586</small>
</p>
</div>
</li>
</ul>
<div>
</div>
</div>
<div class="col-sm-8 col-xs-12">
<div id="searchandfilterContainer">
<form id="searchForm" method="POST">
<div class="input-group">
<input id="searchInput" type="text" class="form-control" placeholder="Zoek op productnaam, merk, trefwoord of EAN. "
value="8710654160037">
<span class="input-group-btn">
<button class="btn btn-success" type="submit">Zoek</button>
<button class="btn btn-warning" onclick="location.href='/prod/dc';return false;">Reset alle filters</button>
</span>
</div>
</form>
<div style="margin-bottom: 10px;" id="filterSummaryContainer">
<div class="bs-callout bs-callout-warning" id="filterSummary">
<a href="#" class="label label-default" onclick="removeFilterPS('SelectedFilterOptions_FreeText'); removeFilterPS('SelectedFilterOptions_Ean')">
Zoeken op: 8710654160037
<span class="glyphicon glyphicon-remove-circle" />
</a>
</div>
</div>
</div>
<div id="resultList" class="hidden">
<ul class="media-list productresult">
<li class="media" onclick="loadDetailPage('/prod/dc/nl/12345/ps/ProductDetailPartial/6682'); return false;" title="Super appelkoeken">
<div class="media-left thumb text-center media-middle">
<div>
<img class="media-object assetimage" alt="media" src="https://permalink.psinfoodservice.com/prod/image/123492/05c5cd27-5def-41a1-808d-9a01eabc6c1e?w=250&amp;h=250" />
</div>
</div>
<div class="media-body media-middle">
<h4 class="media-heading">
Super appelkoeken
</h4>
<p class="list-group-item-text">
<small>de Molen&#39;s Banket</small><br />
<small>
EAN:&nbsp;8710654160037, 8710654000586
<span>(100 gram)</span>
</small>
</p>
</div>
</li>
</ul>
<div>
</div>
</div>
<div class="hidden" id="resultDetail">
<h2>Oeps! Je zoekopdracht leverde geen producten op.</h2>
<p>Het kan zijn dat het product dat je zoekt wel in de database van PS in foodservice zit, maar niet gevonden kan worden met deze zoekopdracht. Het product wel vinden? Controleer eerst dit:</p>
<ul>
<li>Gebruik <b>één</b> productnaam of merk in je zoekopdracht. Hoe korter hoe beter!</li>
<li>Staat er per ongeluk nog <b>een filter</b> aan die het zoeken beperkt?</li>
<li>Gebruik <b>geen komma’s</b> in je zoekopdracht.</li>
<li>Gebruik (een deel van) de <b>exacte</b> naam (zoals de producent hem communiceert) van het product/merk. Houd het zo <b>kort</b> mogelijk!</li>
<li>Wil je zoeken met <b>een nummer</b>? Bijvoorbeeld van een EAN, artikelnummer fabrikant of grossier? Gebruik het veld onder het filter links.</li>
</ul>
<p>
Nog steeds geen resultaat? Of zou je willen dat een producent ook zijn productinformatie in het FoodBook laat plaatsen? Dat horen we graag! Vul <a href="https://docs.google.com/forms/d/18zms043oZ_cSXtLG76UIJlAeqfZ4VlduCp14iW0zVEQ/viewform" target="_blank">dit formulier</a> in, dan gaan wij ermee aan de slag!
</p>
<button class="btn btn-default" type="button" onclick="showResultPage(); return false;">
Terug
</button>
</div>
</div>
</div>
<div class="modal fade" id="myModal" data-backdrop="static" data-keyboard="false">
<div style="width: 100%; background-color: transparent; text-align: center;margin-top:16%;">
<div style="background: #000000; opacity: 0.75; filter: alpha(opacity=75); width: 600px; border-radius: 5px; position: relative; float: left; top: 50%; left: 50%; transform: translate(-50%, -50%); ">
<br />
<span style="color: #FFFFFF; font-weight: normal; font-size: 160%; ">Even geduld a.u.b...</span><br /><br />
<img src="/prod/Content/images/ps_loading_eclipse_blue.gif" width="50" alt="loading_animation" />
<br /><br />
</div>
</div>
</div>
<hr />
<span id="showSurvey" class="fbBtn hidden-xs" style="display: none;" >
Feedback
</span>
<div data-role="dialog" id="FoodbookSurveyModal"></div><script>
kendo.syncReady(function(){jQuery("#FoodbookSurveyModal").kendoDialog({"title":"PS Foodbook - feedback","visible":false,"width":640,"content":"\u003ciframe class=\u0027gFormIframe\u0027 src=\u0027https://docs.google.com/forms/d/e/1FAIpQLSdw9av52vBurb-u4vcg5338NhXVMcjEv8mYF7ZiWcT2xW2Ozg/viewform?embedded=true\u0027 style=\u0027overflow-y: hidden;\u0027 width=\u0027600\u0027 height=\u00271080\u0027 frameborder=\u00270\u0027 marginheight=\u002720\u0027 marginwidth=\u00270\u0027\u003e\u003c/iframe\u003e"});});
</script>
<footer class="foott">
<p>
<span class="footer">
&copy; 2019 -
<a href="/prod/Account/ResetCurrentSession?returnUrl=https%3A%2F%2Ffoodbook.psinfoodservice.com%2Fprod%2Fdc">PS Foodbook</a>
powered by <a target="_blank" href="https://www.psinfoodservice.nl/">PS in foodservice</a>
- <a href="https://permalink.psinfoodservice.com/prod/documents/standards/Privacyverklaring.pdf" target="_blank">Privacy verklaring</a>
- <a href="https://permalink.psinfoodservice.com/prod/documents/standards/Disclaimer.pdf" target="_blank">Disclaimer</a>
- 3.2.0 - prod
</span>
</p>
</footer>
</div>
<script src="/prod/Scripts/Site.js?v=3.2.0"></script>
<script>
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date();
a = s.createElement(o),
m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
ga('create', 'UA-58848830-1', 'auto');
ga('send', 'pageview');
</script>
<!--
Start Cookie
https://cookieconsent.insites.com/
-->
<link rel="stylesheet" type="text/css" href="/prod/Content/cookieconsent.min.css" />
<script src="/prod/Scripts/cookieconsent.min.js"></script>
<script>
window.addEventListener("load", function(){
window.cookieconsent.initialise({
"palette": {
"popup": {
"background": "#009ee0",
"text": "#ffffff"
},
"button": {
"background": "#fff",
"text": "#009ee0"
}
},
"content": {
"message": "Deze website gebruikt cookies.",
"dismiss": "Begrepen",
"link": "Meer informatie",
"href": "https://www.psinfoodservice.nl/informatie/uitleg-cookies"
}
})});
</script>
<!--End cookie-->
</body>
</html>

+ 232
- 0
get-barcode.js View File

@ -0,0 +1,232 @@
"use strict";
const Promise = require("bluebird");
const bhttp = require("bhttp").session({ headers: { "user-agent": "barcodebot (problems: admin@cryto.net)" } });
const dotty = require("dotty");
const cheerio = require("cheerio");
const url = require("url");
const chalk = require("chalk");
const config = require("./config.json");
const parseFoodbook = require("./parse-foodbook");
function log(barcode, message) {
console.log(`${chalk.cyan(`[${barcode}]`)} ${message}`);
}
function mapCurrency(abbreviation) {
if (abbreviation === "USD") {
return "$";
} else if (abbreviation === "EUR") {
return "€";
} else {
throw new Error(`Unrecognized currency: ${abbreviation}`);
}
}
function cleanValue(value) {
if(value != null) {
return value.replace(/\n/g, " ").replace(/\s+/, " ").trim();
}
}
let barcodeGetters = [
getBarcode_coop,
getBarcode_bolCom,
getBarcode_buycott,
getBarcode_foodBook,
getBarcode_eanData,
getBarcode_openFoodFacts,
];
module.exports = function getBarcode(barcode) {
return Promise.try(() => {
return Promise.reduce(barcodeGetters, (lastResult, getter) => {
if (lastResult == null) {
return getter(barcode);
} else {
return lastResult;
}
}, null);
}).then((result) => {
if (result != null) {
return {
... result,
image: (result.image !== "") ? result.image : null
};
} else {
return null;
}
})
};
function getBarcode_eanData(barcode) {
return Promise.try(() => {
log(barcode, "Trying EANData...");
return bhttp.get(`https://eandata.com/feed/?v=3&keycode=${config.apiKeys.eanData}&mode=json&find=${barcode}`);
}).then((response) => {
if (response.statusCode === 200) {
let productName = dotty.get(response.body, "product.attributes.product");
if (productName != null) {
let data = {
name: cleanValue(productName),
image: dotty.get(response.body, "product.image")
};
let priceCurrency = dotty.get(response.body, "product.attributes.price_new_extra");
let priceAmount = dotty.get(response.body, "product.attributes.price_new");
if (priceAmount != null) {
data.price = `${mapCurrency(priceCurrency)} ${priceAmount}`;
}
return data;
} else {
return null;
}
} else {
return null;
}
});
}
let coop404Regex = /We kunnen de pagina die je zoekt niet vinden/;
function getBarcode_coop(barcode) {
return Promise.try(() => {
log(barcode, "Trying Coop...");
return bhttp.get(`https://www.coop.nl/inventory/product/${barcode}`);
}).then((response) => {
if (response.statusCode === 200 && !coop404Regex.test(response.body.toString())) {
let $ = cheerio.load(response.body);
let $imageNoscript = cheerio.load($("picture noscript").html());
return {
name: cleanValue($(".detailHeader").text().trim().replace(/\n/g, " - ")),
// image: $('meta[property="og:image"]').attr("content"),
image: $imageNoscript('img').attr("src"),
price: `${$(".mainActions ins.price").text()}`
};
} else {
return null;
}
});
}
function getBarcode_foodBook(barcode) {
return Promise.try(() => {
log(barcode, "Trying FoodBook...");
return bhttp.post(`https://foodbook.psinfoodservice.com/prod/dc`, {
"SelectedFilterOptions.collapseFilterId": "filterkeywordPanelBody",
"SelectedFilterOptions.FreeText": "",
"SelectedFilterOptions.ProductGroupId": "",
"SelectedFilterOptions.BrandId": "",
"SelectedFilterOptions.GrossierId": "",
"SelectedFilterOptions.Ean": barcode,
"SelectedFilterOptions.ProducerNumber": "",
"SelectedFilterOptions.GrossierNumber": "",
"PS_brands_input": "",
"PS_brands": ""
});
}).then((response) => {
let products = parseFoodbook.parseSearch(response.body);
if (products.length > 0) {
return Promise.try(() => {
let productUrl = url.resolve("https://foodbook.psinfoodservice.com/prod/dc", products[0]);
return bhttp.get(productUrl);
}).then((response) => {
if (response.statusCode === 200) {
return parseFoodbook.parseProduct(response.body);
} else {
return null;
}
});
} else {
return null;
}
});
}
function getBarcode_openFoodFacts(barcode) {
return Promise.try(() => {
log(barcode, "Trying OpenFoodFacts...");
return bhttp.get(`https://world.openfoodfacts.org/api/v0/product/${encodeURIComponent(barcode)}.json`);
}).then((response) => {
if (response.statusCode === 200 && response.body.status === 1 && response.body.product.product_name != null) {
return {
name: cleanValue(response.body.product.product_name),
image: response.body.product.image_url
};
} else {
return null;
}
});
}
function getBarcode_buycott(barcode) {
return Promise.try(() => {
log(barcode, "Trying Buycott...");
return bhttp.get(`https://www.buycott.com/upc/${encodeURIComponent(barcode)}`);
}).then((response) => {
if (response.statusCode === 200) {
let $ = cheerio.load(response.body);
let product = cleanValue($("#container_header h2").text());
let tableData = $("table.product_info_table tr").get().map((row) => {
let $row = $(row);
return [
cleanValue($row.find("td:nth-child(1)").text()),
cleanValue($row.find("td:nth-child(2)").text()),
];
});
let brandRow = tableData.find(([key, value]) => (key === "GS1 Name"));
let productName;
if (brandRow != null) {
productName = `${brandRow[1]}, ${product}`;
} else {
productName = product;
}
return {
name: productName,
image: $(".header_image img").attr("src")
};
}
});
}
function getBarcode_bolCom(barcode) {
return Promise.try(() => {
log(barcode, "Trying bol.com...");
return bhttp.get(`https://www.bol.com/nl/s/algemeen/zoekresultaten/Ntt/${encodeURIComponent(barcode)}/N/0/Nty/1/search/true/searchType/qck/defaultSearchContext/media_all/sc/media_all/index.html`);
}).then((response) => {
if (response.statusCode === 200) {
let $ = cheerio.load(response.body);
let productName = cleanValue($(".pdp-header__title").text());
if (productName.length > 0) {
return {
name: productName,
image: $(".js_product_img").attr("src"),
price: `${cleanValue($(".promo-price").text())}`
};
}
}
});
}

+ 82
- 0
index.js View File

@ -0,0 +1,82 @@
"use strict";
const unhandledError = require("unhandled-error");
const config = require("./config.json");
const shortenUrl = require("./shorten-url");
unhandledError((error) => {
console.error(error.stack);
});
const Promise = require("bluebird");
const IRC = require("irc-framework");
const getBarcode = require("./get-barcode");
function cleanBarcode(barcode) {
return barcode.replace(/[^0-9]+/g, "");
}
let eanRegex = /^([0-9]{8}|[0-9]{13})$/;
let bot = new IRC.Client();
bot.on("message", (event) => {
let match, barcode, explicit;
if (eanRegex.test(event.message)) {
barcode = event.message;
explicit = false;
} else if ((match = /^!barcode (.+)$/.exec(event.message)) != null) {
if (true || eanRegex.test(match[1])) {
barcode = match[1];
explicit = true;
} else {
event.reply("Ongeldige barcode!");
return;
}
}
if (barcode != null) {
return Promise.try(() => {
console.log(`Looking up: ${barcode}`);
return getBarcode(cleanBarcode(barcode));
}).then((result) => {
console.log(result);
if (result == null) {
if (explicit === true) {
event.reply("Barcode niet gevonden!")
}
} else {
let price = (result.price != null)
? result.price
: "onbekend";
return Promise.try(() => {
if (result.image != null) {
return shortenUrl(result.image);
} else {
return "niet beschikbaar";
}
}).then((image) => {
event.reply(`${result.name} -- Prijsindicatie: ${price} -- Productfoto: ${image}`);
});
}
});
}
})
bot.on("registered", () => {
console.log("Connected!");
bot.join(config.irc.channel);
});
bot.connect({
host: config.irc.host,
port: config.irc.port,
nick: config.irc.nick
});

+ 112
- 0
notes.txt View File

@ -0,0 +1,112 @@
https://www.coop.nl/dr-oetker-big-americans-hawaii/product/4001724023876
https://eandata.com/feed/?v=3&keycode=<KEY>&mode=json&find=0025192251344
{
"status": {
"version": "3.1",
"code": "200",
"message": "free 0/15",
"find": "0025192251344",
"search": "normal",
"run": "0.2021",
"runtime": "0.2113"
},
"product": {
"attributes": {
"product": "Jaws",
"url": "http://www.jaws25.com/",
"model": "29466797",
"price_new": "6.9400",
"price_new_extra": "USD",
"price_new_extra_long": "US Dollars",
"price_new_extra_id": "537",
"price_used": "4.9900",
"price_used_extra": "USD",
"price_used_extra_long": "US Dollars",
"price_used_extra_id": "537",
"asin_com": "B00MA5KL9I",
"category": "55",
"category_text": "Movie",
"category_text_long": "Electronics / Photography: A/V Media: Movie",
"width": "5.3000",
"width_extra": "in",
"width_extra_long": "inches",
"width_extra_id": "501",
"height": "0.3500",
"height_extra": "in",
"height_extra_long": "inches",
"height_extra_id": "501",
"length": "6.7500",
"length_extra": "in",
"length_extra_long": "inches",
"length_extra_id": "501",
"weight": "15.0000",
"weight_extra": "lb/100",
"weight_extra_long": "hundredths pounds",
"weight_extra_id": "765",
"long_desc": "*** only visible with paid data feed ***",
"features": "Shrink-wrapped",
"binding": "Blu-ray",
"published": "2016-01-26",
"similar": "0012569828476, 0025192092824, 0025192279218, 0025192347627, 0025192354595, 0025192354601, 0025192354618, 0032429134967, 0043396374003, 0043396496941, 0097361467641, 0191329029053, 0191329047200, 0709112696308, 5051368208930, 5051892007344, 5053083090555, 9780783282336",
"release_year": "1975.0000",
"movie_cast": "*** only visible with paid data feed ***",
"movie_aspect": "2.35:1",
"format": "Multiple Formats, Blu-ray, Ultraviolet, Color, Widescreen",
"movie_genre": "Action/Adventure",
"movie_rating": "743",
"movie_rating_text": "PG",
"movie_rating_text_long": "PG - Parental Guidance Suggested",
"runtime": "124.0000",
"runtime_extra": "minutes",
"runtime_extra_long": "minutes",
"runtime_extra_id": "753",
"movie_imdb": "*** only visible with paid data feed ***",
"movie_runtime": "124.0000",
"movie_runtime_extra": "minutes",
"movie_runtime_extra_long": "minutes",
"movie_runtime_extra_id": "753",
"music_runtime": "124.0000",
"music_runtime_extra": "minutes",
"music_runtime_extra_long": "minutes",
"music_runtime_extra_id": "753",
"movie_director": "Steven Spielberg",
"movie_imdb_trailer": "*** only visible with paid data feed ***"
},
"EAN13": "0025192251344",
"UPCA": "025192251344",
"barcode": {
"EAN13": "https://eandata.com/image/0025192251344.png",
"UPCA": "https://eandata.com/image/025192251344.png"
},
"locked": "0",
"modified": "2019-04-25 22:23:52",
"image": "https://eandata.com/image/products/002/519/225/0025192251344.jpg"
}
}
POST https://foodbook.psinfoodservice.com/prod/dc
"SelectedFilterOptions.collapseFilterId": "filterkeywordPanelBody",
"SelectedFilterOptions.FreeText": "",
"SelectedFilterOptions.ProductGroupId": "",
"SelectedFilterOptions.BrandId": "",
"SelectedFilterOptions.GrossierId": "",
"SelectedFilterOptions.Ean": "8710654160037",
"SelectedFilterOptions.ProducerNumber": "",
"SelectedFilterOptions.GrossierNumber": "",
"PS_brands_input": "",
"PS_brands": ""
GET https://foodbook.psinfoodservice.com/prod/dc/nl/12345/ps/FilterBrands?text=&filter%5Blogic%5D=and
curl 'https://foodbook.psinfoodservice.com/prod/dc#' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: https://foodbook.psinfoodservice.com/prod/dc' -H 'Content-Type: application/x-www-form-urlencoded' -H 'DNT: 1' -H 'Connection: keep-alive' -H 'Cookie: ASP.NET_SessionId=tqcozq0zsc21wafsx5i4vgpk' -H 'Upgrade-Insecure-Requests: 1' --data 'SelectedFilterOptions.collapseFilterId=filterkeywordPanelBody&SelectedFilterOptions.FreeText=8710654160037&SelectedFilterOptions.ProductGroupId=&SelectedFilterOptions.BrandId=&SelectedFilterOptions.GrossierId=&SelectedFilterOptions.Ean=&SelectedFilterOptions.ProducerNumber=&SelectedFilterOptions.GrossierNumber=&PS_brands_input=&PS_brands='
blah.html
curl 'https://foodbook.psinfoodservice.com/prod/dc/nl/12345/ps/FilterBrands?text=&filter%5Blogic%5D=and' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: https://foodbook.psinfoodservice.com/prod/dc' -H 'X-Requested-With: XMLHttpRequest' -H 'DNT: 1' -H 'Connection: keep-alive' -H 'Cookie: ASP.NET_SessionId=tqcozq0zsc21wafsx5i4vgpk'
[{"Id":81,"Name":"de Molen\u0027s Banket","LogoUrl":null,"IsPubliclyVisible":true}]
curl 'https://foodbook.psinfoodservice.com/prod/dc/nl/12345/ps/ProductDetailPartial/6682?_=1556280486040' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0' -H 'Accept: text/html, */*; q=0.01' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: https://foodbook.psinfoodservice.com/prod/dc' -H 'X-Requested-With: XMLHttpRequest' -H 'DNT: 1' -H 'Connection: keep-alive' -H 'Cookie: ASP.NET_SessionId=tqcozq0zsc21wafsx5i4vgpk'

+ 18
- 0
package.json View File

@ -0,0 +1,18 @@
{
"name": "barcodebot",
"description": "An IRC bot that looks up UPC barcodes",
"version": "1.0.0",
"main": "index.js",
"repository": "http://git.cryto.net/joepie91/barcodebot.git",
"author": "Sven Slootweg <admin@cryto.net>",
"license": "WTFPL OR CC0-1.0",
"dependencies": {
"bhttp": "^1.2.4",
"bluebird": "^3.5.4",
"chalk": "^2.4.2",
"cheerio": "^1.0.0-rc.3",
"dotty": "^0.1.0",
"irc-framework": "^4.2.0",
"unhandled-error": "^1.0.0"
}
}

+ 55
- 0
parse-foodbook.js View File

@ -0,0 +1,55 @@
"use strict";
const cheerio = require("cheerio");
let onclickRegex = /^loadDetailPage\('([^']+)'\);/;
let urlRegex = /url\('([^']+)'\)/;
function getOnclickUrl(onclickText) {
let match = onclickRegex.exec(onclickText);
if (match == null) {
throw new Error("Expected URL not found in onclick handler");
} else {
return match[1];
}
}
function getUrl(cssText) {
let match = urlRegex.exec(cssText);
if (match == null) {
return null; /* Sometimes there is no picture */
} else {
return match[1];
}
}
module.exports = {
parseSearch: function (html) {
let $ = cheerio.load(html);
let products = $(".productresult .media").get().map((row) => {
let $row = $(row);
let onclickValue = $row.attr("onclick");
return getOnclickUrl(onclickValue);
});
return products;
},
parseProduct: function (html) {
let $ = cheerio.load(html);
let brandTextElement = $(".section-data h1 a#brandlink");
let brand = brandTextElement.text().trim();
brandTextElement.remove();
let product = $(".section-data h1").text().trim();
return {
name: `${brand}, ${product}`,
image: getUrl($(".assetImageDefault").attr("style"))
}
}
};

+ 12
- 0
shorten-url.js View File

@ -0,0 +1,12 @@
"use strict";
const Promise = require("bluebird");
const bhttp = require("bhttp");
module.exports = function shortenUrl(targetUrl) {
return Promise.try(() => {
return bhttp.get(`https://is.gd/create.php?format=simple&url=${encodeURIComponent(targetUrl)}`);
}).then((response) => {
return response.body.toString();
});
};

+ 1362
- 0
test-foodbook.js
File diff suppressed because it is too large
View File


+ 34
- 0
test-json.js View File

@ -0,0 +1,34 @@
"use strict";
const dotty = require("dotty");
let json = `{"status":{"version":"3.1","code":"200","message":"free 0\/15","find":"0025192251344","search":"normal","run":"0.2021","runtime":"0.2113"},"product":{"attributes":{"product":"Jaws","url":"http:\/\/www.jaws25.com\/","model":"29466797","price_new":"6.9400","price_new_extra":"USD","price_new_extra_long":"US Dollars","price_new_extra_id":"537","price_used":"4.9900","price_used_extra":"USD","price_used_extra_long":"US Dollars","price_used_extra_id":"537","asin_com":"B00MA5KL9I","category":"55","category_text":"Movie","category_text_long":"Electronics \/ Photography: A\/V Media: Movie","width":"5.3000","width_extra":"in","width_extra_long":"inches","width_extra_id":"501","height":"0.3500","height_extra":"in","height_extra_long":"inches","height_extra_id":"501","length":"6.7500","length_extra":"in","length_extra_long":"inches","length_extra_id":"501","weight":"15.0000","weight_extra":"lb\/100","weight_extra_long":"hundredths pounds","weight_extra_id":"765","long_desc":"*** only visible with paid data feed ***","features":"Shrink-wrapped","binding":"Blu-ray","published":"2016-01-26","similar":"0012569828476, 0025192092824, 0025192279218, 0025192347627, 0025192354595, 0025192354601, 0025192354618, 0032429134967, 0043396374003, 0043396496941, 0097361467641, 0191329029053, 0191329047200, 0709112696308, 5051368208930, 5051892007344, 5053083090555, 9780783282336","release_year":"1975.0000","movie_cast":"*** only visible with paid data feed ***","movie_aspect":"2.35:1","format":"Multiple Formats, Blu-ray, Ultraviolet, Color, Widescreen","movie_genre":"Action\/Adventure","movie_rating":"743","movie_rating_text":"PG","movie_rating_text_long":"PG - Parental Guidance Suggested","runtime":"124.0000","runtime_extra":"minutes","runtime_extra_long":"minutes","runtime_extra_id":"753","movie_imdb":"*** only visible with paid data feed ***","movie_runtime":"124.0000","movie_runtime_extra":"minutes","movie_runtime_extra_long":"minutes","movie_runtime_extra_id":"753","music_runtime":"124.0000","music_runtime_extra":"minutes","music_runtime_extra_long":"minutes","music_runtime_extra_id":"753","movie_director":"Steven Spielberg","movie_imdb_trailer":"*** only visible with paid data feed ***"},"EAN13":"0025192251344","UPCA":"025192251344","barcode":{"EAN13":"https:\/\/eandata.com\/image\/0025192251344.png","UPCA":"https:\/\/eandata.com\/image\/025192251344.png"},"locked":"0","modified":"2019-04-25 22:23:52","image":"https:\/\/eandata.com\/image\/products\/002\/519\/225\/0025192251344.jpg"}}
`;
let source = JSON.parse(json);
function parse() {
let productName = dotty.get(source, "product.attributes.product");
if (productName != null) {
let data = {
name: productName,
image: dotty.get(source, "product.image")
};
let priceCurrency = dotty.get(source, "product.attributes.price_new_extra");
let priceAmount = dotty.get(source, "product.attributes.price_new");
if (priceAmount != null) {
data.price = `$ ${priceAmount}`;
}
return data;
} else {
return null;
}
}
console.log(parse());

+ 11
- 0
test.js View File

@ -0,0 +1,11 @@
"use strict";
const Promise = require("bluebird");
const getBarcode = require("./get-barcode");
Promise.try(() => {
return getBarcode("4002846034450");
}).then((result) => {
console.log(result);
})

+ 490
- 0
yarn.lock View File

@ -0,0 +1,490 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/node@*":
version "11.13.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.13.7.tgz#85dbb71c510442d00c0631f99dae957ce44fd104"
integrity sha512-suFHr6hcA9mp8vFrZTgrmqW2ZU3mbWsryQtQlY/QvwTISCw7nw/j+bCQPPohqmskhmqa5wLNuMHTTsc+xf1MQg==
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
bhttp@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/bhttp/-/bhttp-1.2.4.tgz#fed0c24f765b35afc4940b08ab3214813e38f38f"
integrity sha1-/tDCT3ZbNa/ElAsIqzIUgT44848=
dependencies:
bluebird "^2.8.2"
concat-stream "^1.4.7"
debug "^2.1.1"
dev-null "^0.1.1"
errors "^0.2.0"
extend "^2.0.0"
form-data2 "^1.0.0"
form-fix-array "^1.0.0"
lodash "^2.4.1"
stream-length "^1.0.2"
string "^3.0.0"
through2-sink "^1.0.0"
through2-spy "^1.2.0"
tough-cookie "^2.3.1"
bluebird@^2.6.2, bluebird@^2.8.1, bluebird@^2.8.2:
version "2.11.0"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
integrity sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=
bluebird@^3.5.4:
version "3.5.4"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.4.tgz#d6cc661595de30d5b3af5fcedd3c0b3ef6ec5714"
integrity sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==
boolbase@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
cheerio@^1.0.0-rc.3:
version "1.0.0-rc.3"
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6"
integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==
dependencies:
css-select "~1.2.0"
dom-serializer "~0.1.1"
entities "~1.1.1"
htmlparser2 "^3.9.1"
lodash "^4.15.0"
parse5 "^3.0.1"
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
combined-stream2@^1.0.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/combined-stream2/-/combined-stream2-1.1.2.tgz#f6e14b7a015666f8c7b0a1fac506240164ac3570"
integrity sha1-9uFLegFWZvjHsKH6xQYkAWSsNXA=
dependencies:
bluebird "^2.8.1"
debug "^2.1.1"
stream-length "^1.0.1"
concat-stream@^1.4.7:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
css-select@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
dependencies:
boolbase "~1.0.0"
css-what "2.1"
domutils "1.5.1"
nth-check "~1.0.1"
css-what@2.1:
version "2.1.3"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
debug@^2.1.1, debug@^2.2.0:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
dev-null@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/dev-null/-/dev-null-0.1.1.tgz#5a205ce3c2b2ef77b6238d6ba179eb74c6a0e818"
integrity sha1-WiBc48Ky73e2I41roXnrdMag6Bg=
dom-serializer@0, dom-serializer@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==
dependencies:
domelementtype "^1.3.0"
entities "^1.1.1"
domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
domhandler@^2.3.0:
version "2.4.2"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==
dependencies:
domelementtype "1"
domutils@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
dependencies:
dom-serializer "0"
domelementtype "1"
domutils@^1.5.1: