Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

first commit

  • Loading branch information...
commit 0a9825712bad81ee457b1741f8deb5a8ef0bb3d4 1 parent 04063b3
@eibbors authored
Showing with 14,578 additions and 0 deletions.
  1. +305 −0 lib/fba.js
  2. +109 −0 lib/feeds.js
  3. +388 −0 lib/mws.js
  4. +154 −0 lib/orders.js
  5. +162 −0 lib/products.js
  6. +166 −0 lib/reports.js
  7. +67 −0 lib/sellers.js
  8. +2 −0  node_modules/xml2js/.npmignore
  9. +12 −0 node_modules/xml2js/Cakefile
  10. +19 −0 node_modules/xml2js/LICENSE
  11. +139 −0 node_modules/xml2js/README.md
  12. +152 −0 node_modules/xml2js/lib/xml2js.js
  13. +9 −0 node_modules/xml2js/node_modules/sax/AUTHORS
  14. +23 −0 node_modules/xml2js/node_modules/sax/LICENSE
  15. +215 −0 node_modules/xml2js/node_modules/sax/README.md
  16. +8,002 −0 node_modules/xml2js/node_modules/sax/examples/big-not-pretty.xml
  17. +41 −0 node_modules/xml2js/node_modules/sax/examples/example.js
  18. +58 −0 node_modules/xml2js/node_modules/sax/examples/get-products.js
  19. +4 −0 node_modules/xml2js/node_modules/sax/examples/hello-world.js
  20. +8 −0 node_modules/xml2js/node_modules/sax/examples/not-pretty.xml
  21. +74 −0 node_modules/xml2js/node_modules/sax/examples/pretty-print.js
  22. +2 −0  node_modules/xml2js/node_modules/sax/examples/shopping.xml
  23. +870 −0 node_modules/xml2js/node_modules/sax/examples/strict.dtd
  24. +45 −0 node_modules/xml2js/node_modules/sax/examples/switch-bench.js
  25. +15 −0 node_modules/xml2js/node_modules/sax/examples/test.html
  26. +1,254 −0 node_modules/xml2js/node_modules/sax/examples/test.xml
  27. +1,007 −0 node_modules/xml2js/node_modules/sax/lib/sax.js
  28. +10 −0 node_modules/xml2js/node_modules/sax/package.json
  29. +25 −0 node_modules/xml2js/node_modules/sax/test/buffer-overrun.js
  30. +47 −0 node_modules/xml2js/node_modules/sax/test/case.js
  31. +11 −0 node_modules/xml2js/node_modules/sax/test/cdata-chunked.js
  32. +15 −0 node_modules/xml2js/node_modules/sax/test/cdata-end-split.js
  33. +28 −0 node_modules/xml2js/node_modules/sax/test/cdata-fake-end.js
  34. +15 −0 node_modules/xml2js/node_modules/sax/test/cdata-multiple.js
  35. +10 −0 node_modules/xml2js/node_modules/sax/test/cdata.js
  36. +86 −0 node_modules/xml2js/node_modules/sax/test/index.js
  37. +43 −0 node_modules/xml2js/node_modules/sax/test/issue-23.js
  38. +24 −0 node_modules/xml2js/node_modules/sax/test/issue-30.js
  39. +15 −0 node_modules/xml2js/node_modules/sax/test/issue-35.js
  40. +13 −0 node_modules/xml2js/node_modules/sax/test/issue-47.js
  41. +31 −0 node_modules/xml2js/node_modules/sax/test/issue-49.js
  42. +28 −0 node_modules/xml2js/node_modules/sax/test/parser-position.js
  43. +12 −0 node_modules/xml2js/node_modules/sax/test/script.js
  44. +40 −0 node_modules/xml2js/node_modules/sax/test/self-closing-child-strict.js
  45. +40 −0 node_modules/xml2js/node_modules/sax/test/self-closing-child.js
  46. +25 −0 node_modules/xml2js/node_modules/sax/test/self-closing-tag.js
  47. +17 −0 node_modules/xml2js/node_modules/sax/test/stray-ending.js
  48. +17 −0 node_modules/xml2js/node_modules/sax/test/trailing-non-whitespace.js
  49. +17 −0 node_modules/xml2js/node_modules/sax/test/unquoted.js
  50. +67 −0 node_modules/xml2js/node_modules/sax/test/xmlns-issue-41.js
  51. +59 −0 node_modules/xml2js/node_modules/sax/test/xmlns-rebinding.js
  52. +71 −0 node_modules/xml2js/node_modules/sax/test/xmlns-strict.js
  53. +15 −0 node_modules/xml2js/node_modules/sax/test/xmlns-unbound.js
  54. +35 −0 node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-prefix-attribute.js
  55. +20 −0 node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-prefix.js
  56. +40 −0 node_modules/xml2js/node_modules/sax/test/xmlns-xml-default-redefine.js
  57. +37 −0 node_modules/xml2js/package.json
  58. +142 −0 node_modules/xml2js/src/xml2js.coffee
  59. +34 −0 node_modules/xml2js/test/fixtures/sample.xml
  60. +154 −0 node_modules/xml2js/test/xml2js.test.coffee
  61. +33 −0 package.json
View
305 lib/fba.js
@@ -0,0 +1,305 @@
+ /**
+ * Fulfillment API requests and definitions for Amazon's MWS web services.
+ * Currently untested, for the most part because I don't have an account
+ * with Fulfillment By Amazon services.
+ *
+ * @author Robert Saunders
+ */
+var mws = require('./mws');
+
+/**
+ * Construct a mws fulfillment api request for mws.Client.invoke()
+ * @param {String} group Group name (category) of request
+ * @param {String} path Path of associated group
+ * @param {String} action Action request will be calling
+ * @param {Object} params Schema of possible request parameters
+ */
+function FulfillmentRequest(group, path, action, params) {
+ var opts = {
+ name: 'Fulfillment',
+ group: group,
+ path: path,
+ version: '2010-10-01',
+ legacy: false,
+ action: action,
+ params: params
+ };
+ return new mws.Request(opts);
+}
+
+function FbaInboundRequest(action, params) {
+ return FulfillmentRequest('Inbound Shipments', '/FulfillmentInboundShipment/2010-10-01', action, params);
+}
+
+function FbaInventoryRequest(action, params) {
+ return FulfillmentRequest('Inventory', '/FulfillmentInventory/2010-10-01', action, params);
+}
+
+function FbaOutboundRequest(action, params) {
+ return FulfillmentRequest('Outbound Shipments', '/FulfillmentOutboundShipment/2010-10-01', action, params);
+}
+
+/**
+ * Ojects to represent enum collections used by some request(s)
+ * @type {Object}
+ */
+var enums = exports.enums = {
+
+ ResponseGroups: function() {
+ return new mws.Enum(['Basic', 'Detailed']);
+ },
+
+ ShippingSpeedCategories: function() {
+ return new mws.Enum(['Standard', 'Expedited', 'Priority']);
+ },
+
+ FulfillmentPolicies: function() {
+ return new mws.Enum(['FillOrKill', 'FillAll', 'FillAllAvailable']);
+ }
+
+};
+
+/**
+ * A collection of currently supported request constructors. Once created and
+ * configured, the returned requests can be passed to an mws client `invoke` call
+ * @type {Object}
+ */
+var calls = exports.requests = {
+
+ // Inbound Shipments
+ inbound: {
+
+ GetServiceStatus: function() {
+ return new FbaInboundRequest('GetServiceStatus', {});
+ },
+
+ CreateInboundShipment: function() {
+ return new FbaInboundRequest('CreateInboundShipment', {
+ ShipmentId: { name: 'ShipmentId', required: true},
+ Shipmentname: { name: 'InboundShipmentHeader.ShipmentName', required: true },
+ ShipFromname: { name: 'InboundShipmentHeader.ShipFromAddress.Name', required: true },
+ ShipFromAddressLine1: { name: 'InboundShipmentHeader.ShipFromAddress.AddressLine1', required: true },
+ ShipFromAddressLine2: { name: 'InboundShipmentHeader.ShipFromAddress.AddressLine2', required: false },
+ ShipFromAddressCity: { name: 'InboundShipmentHeader.ShipFromAddress.City', required: true },
+ ShipFromDistrictOrCounty: { name: 'InboundShipmentHeader.ShipFromAddress.DistrictOrCounty', required: false },
+ ShipFromStateOrProvince: { name: 'InboundShipmentHeader.ShipFromAddress.StateOrProvinceCode', required: true },
+ ShipFromPostalCode: { name: 'InboundShipmentHeader.ShipFromAddress.PostalCode', required: true },
+ ShipFromCountryCode: { name: 'InboundShipmentHeader.ShipFromAddress.CountryCode', required: true },
+ DestinationFulfillmentCenterId: { name: 'InboundShipmentHeader.DestinationFulfillmentCenterId', required: true },
+ ShipmentStatus: { name: 'InboundShipmentHeader.ShipmentStatus' },
+ LabelPrepPreference: { name: 'InboundShipmentHeader.LabelPrepPreference' },
+ InboundShipmentItems: { name: 'InboundShipmentItems', type: 'Complex', list: true, required: true, sub: [
+ { name: 'InboundShipmentItems.member.-.QuantityShipped' },
+ { name: 'InboundShipmentItems.member.-.SellerSKU' }
+ ]}
+ });
+ },
+
+ CreateInboundShipmentPlan: function() {
+ return new FbaInboundRequest('CreateInboundShipmentPlan', {
+ LabelPrepPreference: { name: 'LabelPrepPreference', required: true },
+ { name: 'ShipFromAddress.Name' },
+ { name: 'ShipFromAddress.AddressLine1' },
+ { name: 'ShipFromAddress.City' },
+ { name: 'ShipFromAddress.StateOrProvinceCode' },
+ { name: 'ShipFromAddress.PostalCode' },
+ { name: 'ShipFromAddress.CountryCode' },
+ { name: 'ShipFromAddress.AddressLine2' },
+ { name: 'ShipFromAddress.DistrictOrCounty' }
+ { name: 'InboundShipmentPlanRequestItems', required: true, list: true, type: 'Complex',
+ { name: 'InboundShipmentPlanRequestItems.member.-.SellerSKU' },
+ { name: 'InboundShipmentPlanRequestItems.member.-.ASIN' },
+ { name: 'InboundShipmentPlanRequestItems.member.-.Quantity' },
+ { name: 'InboundShipmentPlanRequestItems.member.-.Condition' }
+ },
+ });
+ },
+
+ ListInboundShipmentItems: function() {
+ return new FbaInboundRequest('ListInboundShipmentItems', {
+ { name: 'ShipmentId', required: true },
+ { name: 'LastUpdatedAfter', type: 'Timestamp' },
+ { name: 'LastUpdatedBefore', type: 'Timestamp' }
+ });
+ },
+
+ ListInboundShipmentItemsByNextToken: function() {
+ return new FbaInboundRequest('ListInboundShipmentItemsByNextToken', {
+ NextToken: { name: 'NextToken', required: true }
+ });
+ },
+
+ ListInboundShipments: function() {
+ return new FbaInboundRequest('ListInboundShipments', {
+ ShipmentStatuses: { name: 'ShipmentStatusList.member', list: true, required: false },
+ ShipmentIds: { name: 'ShipmentIdList.member', list: true, required: false },
+ LastUpdatedAfter: { name: 'LastUpdatedAfter', type: 'Timestamp' },
+ LastUpdatedBefore: { name: 'LastUpdatedBefore', type: 'Timestamp' }
+ });
+ },
+
+ ListInboundShipmentsByNextToken: function() {
+ return new FbaInboundRequest('ListInboundShipmentsByNextToken', {
+ NextToken: { name: 'NextToken', required: true }
+ });
+ },
+
+ UpdateInboundShipment: function() {
+ return new FbaInboundRequest('UpdateInboundShipment', {
+ { name: 'ShipmentId', required: true },
+ { name: 'Inbound Shipment Header', type: 'Complex', required: true,
+
+ { name: 'InboundShipmentHeader.ShipmentName', required: true },
+ { name: 'InboundShipmentHeader.ShipFromAddress.Name', required: true },
+ { name: 'InboundShipmentHeader.ShipFromAddress.AddressLine1', required: true },
+ { name: 'InboundShipmentHeader.ShipFromAddress.AddressLine2', required: true },
+ { name: 'InboundShipmentHeader.ShipFromAddress.City', required: true },
+ { name: 'InboundShipmentHeader.ShipFromAddress.StateOrProvinceCode', required: true },
+ { name: 'InboundShipmentHeader.ShipFromAddress.PostalCode', required: true },
+ { name: 'InboundShipmentHeader.ShipFromAddress.CountryCode', required: true },
+ { name: 'InboundShipmentHeader.ShipFromAddress.DistrictOrCounty', required: true },
+ { name: 'InboundShipmentHeader.DestinationFulfillmentCenterId', required: true },
+ { name: 'InboundShipmentHeader.ShipmentStatus' },
+ { name: 'InboundShipmentHeader.LabelPrepPreference' }
+ ]
+ },
+ { name: 'InboundShipmentItems', type: 'Complex', list: true, required: true,
+
+ { name: 'InboundShipmentItems.member.-.QuantityShipped' },
+ { name: 'InboundShipmentItems.member.-.SellerSKU' }
+ ]
+ }
+ });
+ }
+
+ },
+
+ // Inventory
+ inventory: {
+
+ GetServiceStatus: function() {
+ return new FbaInventoryRequest('GetServiceStatus', {});
+ },
+
+ ListInventorySupply: function() {
+ return new FbaInventoryRequest('ListInventorySupply', {
+ SellerSkus: { name: 'SellerSkus.member', list: true },
+ QueryStartDateTime: { name: 'QueryStartDateTime', type: 'Timestamp' },
+ ResponseGroup: { name: 'ResponseGroup' }
+ });
+ },
+
+ ListInventorySupplyByNextToken: function() {
+ return new FbaInventoryRequest('ListInventorySupplyByNextToken', {
+ NextToken: { name: 'NextToken', required: true }
+ });
+ }
+
+ },
+
+ // Outbound Shipments
+ outbound: {
+
+ GetServiceStatus: function() {
+ return new FbaOutboundRequest('GetServiceStatus', {});
+ },
+
+ CancelFulfillmentOrder: function() {
+ return new FbaOutboundRequest('CancelFulfillmentOrder', {
+ SellerFulfillmentOrderId: { name: 'SellerFulfillmentOrderId', required: true }
+ });
+ },
+
+ CreateFulfillmentOrder: function() {
+ return new FbaOutboundRequest('CreateFulfillmentOrder', {
+ { name: 'SellerFulfillmentOrderId', required: true },
+ { name: 'ShippingSpeedCategory', required: true, type: 'fba.ShippingSpeedCategory' },
+ { name: 'DisplayableOrder', type: 'Complex', required: true,
+ Parameters: [
+ { name: 'DisplayableOrderId', required: true },
+ { name: 'DisplayableOrderDateTime', type: 'Timestamp' },
+ { name: 'DisplayableOrderComment' }
+ ]
+ },
+ { name: 'FulfillmentPolicy', required: false, type: 'fba.FulfillmentPolicy' },
+ { name: 'FulfillmentMethod', required: false },
+ { name: 'NotificationEmailList.member', required: false, list: true },
+ { name: 'DestinationAddress', required: true, type: 'Complex',
+ Parameters: [
+ { name: 'DestinationAddress.Name' },
+ { name: 'DestinationAddress.Line1' },
+ { name: 'DestinationAddress.Line2' },
+ { name: 'DestinationAddress.Line3' },
+ { name: 'DestinationAddress.City' },
+ { name: 'DestinationAddress.StateOrProvinceCode', Displayname: "State/Province" },
+ { name: 'DestinationAddress.PostalCode' },
+ { name: 'DestinationAddress.CountryCode' },
+ { name: 'DestinationAddress.DistrictOrCounty' },
+ { name: 'DestinationAddress.PhoneNumber' }
+ ]
+ },
+ { name: 'LineItems', type: 'Complex', required: true, list: true,
+ Parameters: [
+ { name: 'Items.member.-.DisplayableComment' },
+ { name: 'Items.member.-.GiftMessage' },
+ { name: 'Items.member.-.PerUnitDeclaredValue.Value' },
+ { name: 'Items.member.-.PerUnitDeclaredValue.CurrencyCode' },
+ { name: 'Items.member.-.Quantity' },
+ { name: 'Items.member.-.SellerFulfillmentOrderItemId' },
+ { name: 'Items.member.-.SellerSKU' }
+ ]
+ }
+ });
+ },
+
+ GetFulfillmentOrder: function() {
+ return new FbaOutboundRequest('GetFulfillmentOrder', {
+ SellerFulfillmentOrderId: { name: 'SellerFulfillmentOrderId', required: true }
+ });
+ },
+
+ GetFulfillmentPreview: function() {
+ return new FbaOutboundRequest('GetFulfillmentPreview', {
+ { name: 'Address', required: true, type: 'Complex',
+ Parameters: [
+ { name: 'Address.Name' },
+ { name: 'Address.Line1' },
+ { name: 'Address.Line2' },
+ { name: 'Address.Line3' },
+ { name: 'Address.City' },
+ { name: 'Address.StateOrProvinceCode' },
+ { name: 'Address.PostalCode' },
+ { name: 'Address.CountryCode' },
+ { name: 'Address.DistrictOrCounty' },
+ { name: 'Address.PhoneNumber' }
+ ]
+ },
+ { name: 'LineItems', type: 'Complex', required: true, list: true,
+ Parameters: [
+ { name: 'Items.member.-.Quantity' },
+ { name: 'Items.member.-.SellerFulfillmentOrderItemId' },
+ { name: 'Items.member.-.SellerSKU' },
+ { name: 'Items.member.-.EstimatedShippingWeight' },
+ { name: 'Items.member.-.ShippingWeightCalculationMethod' }
+ ]
+ },
+ { name: 'ShippingSpeedCategories.member', list: true, type: 'fba.ShippingSpeedCategory' }
+ });
+ },
+
+ ListAllFulfillmentOrders: function() {
+ return new FbaOutboundRequest('ListAllFulfillmentOrders', {
+ QueryStartDateTime: { name: 'QueryStartDateTime', required: true, type: 'Timestamp' },
+ FulfillentMethods: { name: 'FulfillmentMethod.member', list: true }
+ });
+ },
+
+ ListAllFulfillmentOrdersByNextToken: function() {
+ return new FbaOutboundRequest('ListAllFulfillmentOrdersByNextToken', {
+ NextToken: { name: 'NextToken', required: true }
+ });
+ }
+
+ }
+
+};
View
109 lib/feeds.js
@@ -0,0 +1,109 @@
+/**
+ * Feeds API requests and definitions for Amazon's MWS web services.
+ * For information on using, please see examples folder.
+ *
+ * @author Robert Saunders
+ */
+var mws = require('./mws');
+
+/**
+ * Construct a Feeds API request for mws.Client.invoke()
+ *
+ * @param {String} action Action parameter of request
+ * @param {Object} params Schemas for all supported parameters
+ */
+function FeedsRequest(action, params) {
+ var opts = {
+ name: 'Feeds',
+ group: 'Feeds',
+ path: '/',
+ version: '2009-01-01',
+ legacy: true,
+ action: action,
+ params: params
+ };
+ return new mws.Request(opts);
+}
+
+/**
+ * Ojects to represent enum collections used by some request(s)
+ * @type {Object}
+ */
+var enums = exports.enums = {
+
+ FeedProcessingStatuses: function() {
+ return new mws.Enum(['_SUBMITTED_', '_IN_PROGRESS_', '_CANCELLED_', '_DONE_']);
+ },
+
+ FeedTypes: function() {
+ return new mws.Enum([
+ '_POST_PRODUCT_DATA_', '_POST_PRODUCT_RELATIONSHIP_DATA_', '_POST_ITEM_DATA_', '_POST_PRODUCT_OVERRIDES_DATA_', '_POST_PRODUCT_IMAGE_DATA_',
+ '_POST_PRODUCT_PRICING_DATA_', '_POST_INVENTORY_AVAILABILITY_DATA_', '_POST_ORDER_ACKNOWLEDGEMENT_DATA_', '_POST_ORDER_FULFILLMENT_DATA_',
+ '_POST_FULFILLMENT_ORDER_REQUEST_DATA_', '_POST_FULFILLMENT_ORDER_CANCELLATION', '_POST_PAYMENT_ADJUSTMENT_DATA_', '_POST_INVOICE_CONFIRMATION_DATA_',
+ '_POST_FLAT_FILE_LISTINGS_DATA_', '_POST_FLAT_FILE_ORDER_ACKNOWLEDGEMENT_DATA_', '_POST_FLAT_FILE_FULFILLMENT_DATA_',
+ '_POST_FLAT_FILE_FBA_CREATE_INBOUND_SHIPMENT_', '_POST_FLAT_FILE_FBA_UPDATE_INBOUND_SHIPMENT_', '_POST_FLAT_FILE_PAYMENT_ADJUSTMENT_DATA_',
+ '_POST_FLAT_FILE_INVOICE_CONFIRMATION_DATA_', '_POST_FLAT_FILE_INVLOADER_DATA_', '_POST_FLAT_FILE_CONVERGENCE_LISTINGS_DATA_',
+ '_POST_FLAT_FILE_BOOKLOADER_DATA_', '_POST_FLAT_FILE_LISTINGS_DATA_', '_POST_FLAT_FILE_PRICEANDQUANTITYONLY', '_POST_UIEE_BOOKLOADER_DATA_'
+ ]);
+ }
+
+};
+
+/**
+ * A collection of currently supported request constructors. Once created and
+ * configured, the returned requests can be passed to an mws client `invoke` call
+ * @type {Object}
+ */
+var calls = exports.requests = {
+
+ CancelFeedSubmissions: function() {
+ return new FeedsRequest('CancelFeedSubmissions', {
+ FeedSubmissionIds: { name: 'FeedSubmissionIdList.Id', list: true, required: false },
+ FeedTypes: { name: 'FeedTypeList.Type', list: true},
+ SubmittdFrom: { name: 'SubmittedFromDate', type: 'Timestamp' },
+ SubmittedTo: { name: 'SubmittedToDate', type: 'Timestamp' }
+ });
+ },
+
+ GetFeedSubmissionList: function() {
+ return new FeedsRequest('GetFeedSubmissionList', {
+ FeedSubmissionIds: { name: 'FeedSubmissionIdList.Id', list: true, required: false },
+ MaxCount: { name: 'MaxCount' },
+ FeedTypes: { name: 'FeedTypeList.Type', list: true},
+ FeedProcessingStatuses: { name: 'FeedProcessingStatusList.Status', list: true, type: 'bde.FeedProcessingStatuses' },
+ SubmittedFrom: { name: 'SubmittedFromDate', type: 'Timestamp' },
+ SubmittedTo: { name: 'SubmittedToDate', type: 'Timestamp' }
+ });
+ },
+
+ GetFeedSubmissionListByNextToken: function() {
+ return new FeedsRequest('GetFeedSubmissionListByNextToken', {
+ NextToken: { name: 'NextToken', required: true }
+ });
+ },
+
+ GetFeedSubmissionCount: function() {
+ return new FeedsRequest('GetFeedSubmissionCount', {
+ FeedTypes: { name: 'FeedTypeList.Type', list: true},
+ FeedProcessingStatuses: { name: 'FeedProcessingStatusList.Status', list: true, type: 'bde.FeedProcessingStatuses' },
+ SubmittedFrom: { name: 'SubmittedFromDate', type: 'Timestamp' },
+ SubmittedTo: { name: 'SubmittedToDate', type: 'Timestamp' }
+ });
+ },
+
+ GetFeedSubmissionResult: function() {
+ return new FeedsRequest('GetFeedSubmissionResult', {
+ FeedSubmissionId: { name: 'FeedSubmissionId', required: true }
+ });
+ },
+
+ SubmitFeed: function() {
+ return new FeedsRequest('SubmitFeed', {
+ FeedContents: { name: '_BODY_', required: true },
+ FeedType: { name: 'FeedType', required: true},
+ MarketplaceIds: { name: 'MarketplaceIdList.Id', list: true, required: false },
+ PurgeAndReplace: { name: 'PurgeAndReplace', required: false, type: 'Boolean' }
+ });
+ }
+
+};
View
388 lib/mws.js
@@ -0,0 +1,388 @@
+var https = require('https'),
+ qs = require("querystring"),
+ crypto = require('crypto'),
+ xml2js = require('xml2js');
+
+
+var MARKETPLACE_IDS = {
+
+};
+
+/**
+ * Constructor for the main MWS client interface used to make api calls and
+ * various data structures to encapsulate MWS requests, definitions, etc.
+ *
+ * @param {String} accessKeyId Id for your secret Access Key (required)
+ * @param {String} secretAccessKey Secret Access Key provided by Amazon (required)
+ * @param {String} merchantId Aka SellerId, provided by Amazon (required)
+ * @param {Object} options Additional configuration options for this instance
+ */
+function AmazonMwsClient(accessKeyId, secretAccessKey, merchantId, options) {
+ this.host = options.host || 'mws.amazonservices.com';
+ this.port = options.port || 443;
+ this.conn = options.conn || https;
+ this.creds = crypto.createCredentials(options.creds || {});
+ this.appName = options.appName || 'mws.js';
+ this.appVersion = options.appVersion || '0.0.1';
+ this.appLanguage = options.appLanguage || 'JavaScript';
+ this.accessKeyId = accessKeyId || null;
+ this.secretAccessKey = secretAccessKey || null;
+ this.merchantId = merchantId || null;
+}
+
+/**
+ * The method used to invoke calls against MWS Endpoints. Recommended usage is
+ * through the invoke wrapper method when the api call you're invoking has a
+ * request defined in one of the submodules. However, you can use call() manually
+ * when a lower level of control is necessary (custom or new requests, for example).
+ *
+ * @param {Object} api Settings object unique to each API submodule
+ * @param {String} action Api `Action`, such as GetServiceStatus or GetOrder
+ * @param {Object} query Any parameters belonging to the current action
+ * @param {Function} callback Callback function to send any results recieved
+ */
+AmazonMwsClient.prototype.call = function(api, action, query, callback) {
+ if (this.secretAccessKey == null || this.accessKeyId == null || this.merchantId == null) {
+ throw("accessKeyId, secretAccessKey, and merchantId must be set");
+ }
+
+ // Check if we're dealing with a file (such as a feed) upload
+ if (api.upload) {
+ var body = query._BODY_,
+ bformat = query._FORMAT_;
+ delete query._BODY_;
+ delete query._FORMAT_;
+ }
+
+ // Add required parameters and sign the query
+ query['Action'] = action;
+ query['Version'] = api.version;
+ query["Timestamp"] = (new Date()).toISOString();
+ query["AWSAccessKeyId"] = this.accessKeyId;
+ if (api.legacy) { query['Merchant'] = this.merchantId; }
+ else { query['SellerId'] = this.merchantId; }
+ query = this.sign(api.path, query);
+
+ if (!api.upload) {
+ var body = qs.stringify(query);
+ }
+
+ // Setup our HTTP headers and connection options
+ var headers = {
+ 'Host': this.host,
+ 'User-Agent': this.appName + '/' + this.appVersion + ' (Language=' + this.appLanguage + ')',
+ 'Content-Type': bformat || 'application/x-www-form-urlencoded; charset=utf-8',
+ 'Content-Length': body.length
+ };
+ if (api.upload) {
+ headers['Content-MD5'] = cryto.createHash('md5').update(body).digest("base64");
+ }
+ var options = {
+ host: this.host,
+ port: this.port,
+ path: api.path + (api.upload ? '?' + qs.stringify(query) : ''),
+ method: "POST",
+ headers: headers
+ };
+
+ // Make the initial request and define callbacks
+ var req = this.conn.request(options, function (res) {
+ var data = '';
+ // Append each incoming chunk to data variable
+ res.addListener('data', function (chunk) {
+ data += chunk.toString();
+ });
+ // When response is complete, parse the XML and pass it to callback
+ res.addListener('end', function() {
+ var parser = new xml2js.Parser();
+ parser.addListener('end', function (result) {
+ // Throw an error if there was a problem reported
+ if (result.Error != null)
+ throw(result.Error.Code + ": " + result.Error.Message);
+ callback(result);
+ });
+ if (data.slice(0, 5) == '<?xml')
+ parser.parseString(data);
+ else
+ callback(data);
+ });
+ });
+ req.write(body);
+ req.end();
+};
+
+/**
+ * Calculates the HmacSHA256 signature and appends it with additional signature
+ * parameters to the provided query object.
+ *
+ * @param {String} path Path of API call (used to build the string to sign)
+ * @param {Object} query Any non-signature parameters that will be sent
+ * @return {Object} Finalized object used to build query string of request
+ */
+AmazonMwsClient.prototype.sign = function(path, query) {
+ var keys = [],
+ sorted = {},
+ hash = crypto.createHmac("sha256", this.secretAccessKey);
+
+ // Configure the query signature method/version
+ query["SignatureMethod"] = "HmacSHA256";
+ query["SignatureVersion"] = "2";
+
+ // Copy query keys, sort them, then copy over the values
+ for(var key in query)
+ keys.push(key);
+ keys = keys.sort();
+ for(n in keys) {
+ var key = keys[n];
+ sorted[key] = query[key];
+ }
+
+ var stringToSign = ["POST", this.host, path, qs.stringify(sorted)].join("\n");
+
+ // An RFC (cannot remember which one) requires these characters also be changed:
+ stringToSign = stringToSign.replace(/'/g,"%27");
+ stringToSign = stringToSign.replace(/\*/g,"%2A");
+ stringToSign = stringToSign.replace(/\(/g,"%28");
+ stringToSign = stringToSign.replace(/\)/g,"%29");
+
+ query['Signature'] = hash.update(stringToSign).digest("base64");
+
+ return query;
+};
+
+/**
+ * Suggested method for invoking a pre-defined mws request object.
+ *
+ * @param {Object} request An instance of AmazonMwsRequest with params, etc.
+ * @param {Function} callback Callback function used to process results/errors
+ */
+AmazonMwsClient.prototype.invoke = function(request, callback) {
+ this.call(request.api, request.action, request.query(), callback);
+};
+
+
+/**
+ * Constructor for general MWS request objects, wrapped by api submodules to keep
+ * things DRY, yet familiar despite whichever api is being implemented.
+ *
+ * @param {Object} options Settings to apply to new request instance.
+ */
+function AmazonMwsRequest(options) {
+ this.api = {
+ path: options.path || '/',
+ version: options.version || '2009-01-01',
+ legacy: options.legacy || false,
+ };
+ this.action = options.action || 'GetServiceStatus';
+ this.params = options.params || {};
+}
+
+/**
+ * Handles the casting, renaming, and setting of individual request params.
+ *
+ * @param {String} param Key of parameter (not ALWAYS the same as the param name!)
+ * @param {Mixed} value Value to assign to parameter
+ * @return {Object} Current instance to allow function chaining
+ */
+AmazonMwsRequest.prototype.set = function(param, value) {
+ var p = this.params[param],
+ v = p.value = {};
+
+ // Handles the actual setting based on type
+ var setValue = function(name, val) {
+ if (p.type == 'Timestamp') {
+ v[name] = val.toISOString();
+ } else if (p.type == 'Boolean') {
+ v[name] = val ? 'true' : 'false';
+ } else {
+ v[name] = val;
+ }
+ }
+
+ // Lists need to be sequentially numbered and we take care of that here
+ if (p.list) {
+ var i = 0;
+ if ((typeof(value) == "string") || (typeof(value) == "number")) {
+ setValue(p.name + '.1', value);
+ }
+ if (typeof(value) == "object") {
+ if (Array.isArray(value)) {
+ for (i = value.length - 1; i >= 0; i--) {
+ setValue(p.name + '.' + (i+1), value[i]);
+ }
+ } else {
+ for (var key in value) {
+ setValue(p.name + '.' + (++i), value[key]);
+ }
+ }
+ }
+ } else {
+ setValue(p.name, value)
+ }
+
+ return this;
+};
+
+/**
+ * Builds a query object and checks for required parameters.
+ *
+ * @return {Object} KvP's of all provided parameters (used by invoke())
+ */
+AmazonMwsRequest.prototype.query = function() {
+ var q = {};
+ for (var param in this.params) {
+ var value = this.params[param].value,
+ name = this.params[param].name,
+ complex = (this.params[param].type === 'Complex');
+ required = this.params[param].required;
+ console.log("v " + value + "\nn " + name + "\nr " + required);
+ if ((value !== undefined) && (value !== null)) {
+ if (complex) {
+ value.appendTo(q);
+ } else {
+ for (var val in value) {
+ q[val] = value[val];
+ }
+ }
+ } else {
+ if (param.required === true) {
+ throw("ERROR: Missing required parameter, " + name + "!")
+ }
+ }
+ };
+ return q
+};
+
+
+/**
+ * Contructor for objects used to represent enumeration states. Useful
+ * when you need to make programmatic updates to an enumerated data type or
+ * wish to encapsulate enum states in a handy, re-usable variable.
+ *
+ * @param {Array} choices An array of any possible values (choices)
+ */
+function EnumType(choices) {
+ for (var choice in choices) {
+ this[choices[choice]] = false;
+ }
+ this._choices = choices;
+}
+
+/**
+ * Enable one or more choices (accepts a variable number of arguments)
+ * @return {Object} Current instance of EnumType for chaining
+ */
+EnumType.prototype.enable = function() {
+ for (var arg in arguments) {
+ this[arguments[arg]] = true;
+ }
+ return this;
+};
+
+/**
+ * Disable one or more choices (accepts a variable number of arguments)
+ * @return {Object} Current instance of EnumType for chaining
+ */
+EnumType.prototype.disable = function() {
+ for (var arg in arguments) {
+ this[arguments[arg]] = false;
+ }
+ return this;
+};
+
+/**
+ * Toggles one or more choices (accepts a variable number of arguments)
+ * @return {Object} Current instance of EnumType for chaining
+ */
+EnumType.prototype.toggle = function() {
+ for (var arg in arguments) {
+ this[arguments[arg]] = ! this[arguments[arg]];
+ }
+ return this;
+};
+
+/**
+ * Return all possible values without regard to current state
+ * @return {Array} Choices passed to EnumType constructor
+ */
+EnumType.prototype.all = function() {
+ return this._choices;
+};
+
+/**
+ * Return all enabled choices as an array (used to set list params, usually)
+ * @return {Array} Choice values for each choice set to true
+ */
+EnumType.prototype.values = function() {
+ var value = [];
+ for (var choice in this._choices) {
+ if (this[this._choices[choice]] === true) {
+ value.push(this._choices[choice]);
+ }
+ }
+ return value;
+};
+
+
+/**
+ * Takes an object and adds an appendTo function that will add
+ * each kvp of object to a query. Used when dealing with complex
+ * parameters that need to be built in an abnormal or unique way.
+ *
+ * @param {String} name Name of parameter, prefixed to each key
+ * @param {Object} obj Parameters belonging to the complex type
+ */
+function ComplexType(name, obj) {
+ var _obj = obj;
+ obj.appendTo = function(query) {
+ for (var k in _obj) {
+ query[name + '.' k] = _obj[k];
+ }
+ return query;
+ }
+ return obj;
+}
+
+
+exports.Client = AmazonMwsClient;
+exports.Request = AmazonMwsRequest;
+exports.Enum = EnumType;
+exports.Complex = ComplexType;
+
+var orders = exports.orders = require('./orders');
+var sellers = exports.sellers = require('./sellers');
+
+
+
+
+// console.log(orders.enums.OrderStatuses().not('Pending', 'Canceled'));
+
+var accessKeyId = 'AKIAIBXXMKBLSEDGAKUQ',
+secretAccessKey = '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',
+merchantId = 'A8MV19LRXBZP0',
+marketplaceId = 'ATVPDKIKX0DER';
+
+var client = new AmazonMwsClient(accessKeyId, secretAccessKey, merchantId, {});
+
+
+
+client.invoke(new sellers.requests.GetServiceStatus(), function (res) {
+ console.log(res);
+});
+
+// var listOrders = new orders.calls.ListOrders();
+// listOrders.set('MarketplaceId', marketplaceId)
+// .set('CreatedAfter', new Date(2,14,2012));
+// client.invoke(listOrders, function(result) {
+// console.log(result);
+// });
+// var bubble = new AmazonMwsRequest({ path: "/Orders/2011-01-01", version: "2011-01-01", action: "GetServiceStatus", legacy: false });
+
+// client.invoke(bubble, function(result) {
+// console.log(result);
+// });
+
+
+// var dumptruck = new AmazonMwsRequest({ params: { 'AmazonOrderId': { name: 'AmazonOrderId', type: 'String', required: true }}});
+// dumptruck.set('AmazonOrderId', 'Poopsex');
+// console.log(dumptruck.query());
View
154 lib/orders.js
@@ -0,0 +1,154 @@
+/**
+ * Orders API requests and definitions for Amazon's MWS web services.
+ * For information on using, please see examples folder.
+ *
+ * @author Robert Saunders
+ */
+var mws = require('./mws');
+
+/**
+ * Construct an Orders API request for mws.Client.invoke()
+ *
+ * @param {String} action Action parameter of request
+ * @param {Object} params Schemas for all supported parameters
+ */
+function OrdersRequest(action, params) {
+ var opts = {
+ name: 'Orders',
+ group: 'Order Retrieval',
+ path: '/Orders/2011-01-01',
+ version: '2011-01-01',
+ legacy: false,
+ action: action,
+ params: params
+ };
+ return new mws.Request(opts);
+}
+
+/**
+ * Ojects to represent enum collections used by some request(s)
+ * @type {Object}
+ */
+var enums = exports.enums = {
+
+ FulfillmentChannels: function() {
+ return new mws.Enum(['AFN', 'MFN']);
+ },
+
+ OrderStatuses: function() {
+ return new mws.Enum(['Pending', 'Unshipped', 'PartiallyShipped', 'Shipped', 'Canceled', 'Unfulfillable']);
+ },
+
+ PaymentMethods: function() {
+ return new mws.Enum(['COD', 'CVS', 'Other']);
+ }
+
+};
+
+/**
+ * Contains brief definitions for unique data type values.
+ * Can be used to explain input/output to users via tooltips, for example
+ * @type {Object}
+ */
+var types = exports.types = {
+
+ FulfillmentChannel: {
+ 'AFN':'Amazon Fulfillment Network',
+ 'MFN':'Merchant\'s Fulfillment Network' },
+
+ OrderStatus: {
+ 'Pending':'Order placed but payment not yet authorized. Not ready for shipment.',
+ 'Unshipped':'Payment has been authorized. Order ready for shipment, but no items shipped yet. Implies PartiallyShipped.',
+ 'PartiallyShipped':'One or more (but not all) items have been shipped. Implies Unshipped.',
+ 'Shipped':'All items in the order have been shipped.',
+ 'Canceled':'The order was canceled.',
+ 'Unfulfillable':'The order cannot be fulfilled. Applies only to Amazon-fulfilled orders not placed on Amazon.' },
+
+ PaymentMethod: {
+ 'COD':'Cash on delivery',
+ 'CVS':'Convenience store payment',
+ 'Other':'Any payment method other than COD or CVS' },
+
+ ServiceStatus: {
+ 'GREEN':'The service is operating normally.',
+ 'GREEN_I':'The service is operating normally + additional info provided',
+ 'YELLOW':'The service is experiencing higher than normal error rates or degraded performance.',
+ 'RED':'The service is unabailable or experiencing extremely high error rates.' },
+
+ ShipServiceLevelCategory: {
+ 'Expedited':'Expedited shipping',
+ 'NextDay':'Overnight shipping',
+ 'SecondDay':'Second-day shipping',
+ 'Standard':'Standard shipping' }
+
+};
+
+/**
+ * A collection of currently supported request constructors. Once created and
+ * configured, the returned requests can be passed to an mws client `invoke` call
+ * @type {Object}
+ */
+var calls = exports.requests = {
+
+ /**
+ * Requests the operational status of the Orders API section.
+ */
+ GetServiceStatus: function() {
+ return new OrdersRequest('GetServiceStatus', {});
+ },
+
+ /**
+ * Returns orders created or updated during a time frame you specify.
+ */
+ ListOrders: function() {
+ return new OrdersRequest('ListOrders', {
+ CreatedAfter: { name: 'CreatedAfter', type: 'Timestamp' },
+ CreatedBefore: { name: 'CreatedBefore', type: 'Timestamp' },
+ LastUpdatedAfter: { name: 'LastUpdatedAfter', type: 'Timestamp' },
+ MarketplaceId: { name: 'MarketplaceId.Id', list: true, required: true },
+ LastUpdatedBefore: { name: 'LastUpdatedBefore', type: 'Timestamp' },
+ OrderStatus: { name: 'OrderStatus.Status', type: 'orders.OrderStatuses', list: true },
+ FulfillmentChannel: { name: 'FulfillmentChannel.Channel', type: 'orders.FulfillmentChannels', list: true },
+ PaymentMethod: { name: 'PaymentMethod.Method', type: 'orders.PaymentMethods', list: true },
+ BuyerEmail: { name: 'BuyerEmail' },
+ SellerOrderId: { name: 'SellerOrderId' },
+ MaxResultsPerPage: { name: 'MaxResultsPerPage' }
+ });
+ },
+
+ /**
+ * Returns the next page of orders using the NextToken parameter.
+ */
+ ListOrdersByNextToken: function() {
+ return new OrdersRequest('ListOrdersByNextToken', {
+ NextToken: { name: 'NextToken', required: true }
+ });
+ },
+
+ /**
+ * Returns orders based on the AmazonOrderId values that you specify.
+ */
+ GetOrder: function() {
+ return new OrdersRequest('GetOrder', {
+ AmazonOrderId: { name: 'AmazonOrderId.Id', required: true, list: true }
+ });
+ },
+
+ /**
+ * Returns order items based on the AmazonOrderId that you specify.
+ */
+ ListOrderItems: function() {
+ return new OrdersRequest('ListOrderItems', {
+ AmazonOrderId: { name: 'AmazonOrderId', required: true } });
+ },
+
+ /**
+ * Returns the next page of order items using the NextToken parameter.
+ */
+ ListOrderItemsByNextToken: function() {
+ return new OrdersRequest('ListOrderItemsByNextToken', {
+ NextToken: { name: 'NextToken', required: true }
+ });
+ }
+
+};
View
162 lib/products.js
@@ -0,0 +1,162 @@
+/**
+ * Products API requests and definitions for Amazon's MWS web services.
+ * For information on using, please see examples folder.
+ *
+ * @author Robert Saunders
+ */
+var mws = require('./mws');
+
+/**
+ * Construct a Products API request for using with mws.Client.invoke()
+ *
+ * @param {String} action Action parameter of request
+ * @param {Object} params Schemas for all supported parameters
+ */
+function ProductsRequest(action, params) {
+ var opts = {
+ name: 'Products',
+ group: 'Products',
+ path: '/Products/2011-10-01',
+ version: '2011-10-01',
+ legacy: false,
+ action: action,
+ params: params
+ };
+ return new mws.Request(opts);
+}
+
+/**
+ * Ojects to represent enum collections used by some request(s)
+ * @type {Object}
+ */
+var enums = exports.enums = {
+
+ ItemConditions: function() {
+ return new mws.Enum([ 'New', 'Used', 'Collectible', 'Refurbished', 'Club' ]);
+ }
+
+};
+
+/**
+ * Contains brief definitions for unique data type values.
+ * Can be used to explain input/output to users via tooltips, for example
+ * @type {Object}
+ */
+var types = exports.types = {
+
+ CompetitivePriceId: {
+ '1':'New Buy Box Price',
+ '2':'Used Buy Box Price' },
+
+ ServiceStatus: {
+ 'GREEN':'The service is operating normally.',
+ 'GREEN_I':'The service is operating normally + additional info provided',
+ 'YELLOW':'The service is experiencing higher than normal error rates or degraded performance.',
+ 'RED':'The service is unabailable or experiencing extremely high error rates.' },
+
+};
+
+/**
+ * A collection of currently supported request constructors. Once created and
+ * configured, the returned requests can be passed to an mws client `invoke` call
+ * @type {Object}
+ */
+var calls = exports.requests = {
+
+ /**
+ * Requests the operational status of the Products API section.
+ */
+ GetServiceStatus: function() {
+ return new ProductsRequest('GetServiceStatus', {});
+ },
+
+ /**
+ * Returns a list of products and their attributes, ordered by relevancy,
+ * based on a search query that you specify
+ */
+ ListMatchingProducts: function() {
+ return new ProductsRequest('ListMatchingProducts', {
+ MarketplaceId: { name: 'MarketplaceId', required: true},
+ Query: { name: 'Query', required: true},
+ QueryContextId: { name: 'QueryContextId'}
+ });
+ },
+
+ /**
+ * Returns a list of products and their attributes,
+ * based on a list of ASIN values that you specify
+ */
+ GetMatchingProduct: function() {
+ return new ProductsRequest('GetMatchingProduct', {
+ MarketplaceId: { name: 'MarketplaceId', required: true},
+ ASINList: { name: 'ASINList.ASIN', list: true, required: true}
+ });
+ },
+
+ /**
+ * Returns the current competitive pricing of a product,
+ * based on the SellerSKU and MarketplaceId that you specify
+ */
+ GetCompetitivePricingForSKU: function() {
+ return new ProductsRequest('GetCompetitivePricingForSKU', {
+ MarketplaceId: { name: 'MarketplaceId', required: true},
+ SellerSKUList: { name: 'SellerSKUList.SellerSKU', list: true, required: true}
+ });
+ },
+
+ /**
+ * Same as above, except that it uses a MarketplaceId and an ASIN to uniquely
+ * identify a product, and it does not return the SKUIdentifier element
+ */
+ GetCompetitivePricingForASIN: function() {
+ return new ProductsRequest('GetCompetitivePricingForASIN', {
+ MarketplaceId: { name: 'MarketplaceId', required: true},
+ ASINList: { name: 'ASINList.ASIN', list: true, required: true}
+ });
+ },
+
+ /**
+ * Returns the lowest price offer listings for a specific product by item condition.
+ */
+ GetLowestOfferListingsForSKU: function() {
+ return new ProductsRequest('GetLowestOfferListingsForSKU', {
+ MarketplaceId: { name: 'MarketplaceId', required: true},
+ ItemCondition: { name: 'ItemCondition'},
+ SellerSKUList: { name: 'SellerSKUList.SellerSKU', list: true, required: true }
+ });
+ },
+
+ /**
+ * Same as above but by a list of ASIN's you provide
+ */
+ GetLowestOfferListingsForASIN: function() {
+ return new ProductsRequest('GetLowestOfferListingsForASIN', {
+ MarketplaceId: { name: 'MarketplaceId', required: true},
+ ItemCondition: { name: 'ItemCondition'},
+ ASINList: { name: 'ASINList.ASIN', list: true, required: true}
+ });
+ },
+
+ /**
+ * Returns the product categories that a product belongs to,
+ * including parent categories back to the root for the marketplace
+ */
+ GetProductCategoriesForSKU: function() {
+ return new ProductsRequest('GetProductCategoriesForSKU', {
+ MarketplaceId: { name: 'MarketplaceId', required: true},
+ SellerSKU: { name: 'SellerSKU', required: true}
+ });
+ },
+
+ /**
+ * Same as above, except that it uses a MarketplaceId and an ASIN to
+ *uniquely identify a product.
+ */
+ GetProductCategoriesForASIN: function() {
+ return new ProductsRequest('GetProductCategoriesForASIN', {
+ MarketplaceId: { name: 'MarketplaceId', required: true},
+ ASIN: { name: 'ASIN', required: true}
+ });
+ }
+
+};
View
166 lib/reports.js
@@ -0,0 +1,166 @@
+/**
+ * Reports API requests and definitions for Amazon's MWS web services.
+ * For information on using, please see examples folder.
+ *
+ * @author Robert Saunders
+ */
+var mws = require('./mws');
+
+/**
+ * Construct a Reports API request for mws.Client.invoke()
+ *
+ * @param {String} action Action parameter of request
+ * @param {Object} params Schemas for all supported parameters
+ */
+function ReportsRequest(action, params) {
+ var opts = {
+ name: 'Reports',
+ group: 'Reports & Report Scheduling',
+ path: '/',
+ version: '2009-01-01',
+ legacy: true,
+ action: action,
+ params: params
+ };
+ return new mws.Request(opts);
+}
+
+/**
+ * Ojects to represent enum collections used by some request(s)
+ * @type {Object}
+ */
+var enums = exports.enums = {
+
+ Schedules: function() {
+ return new mws.Enum(['_15_MINUTES_', '_30_MINUTES_', '_1_HOUR_', '_2_HOURS_', '_4_HOURS_', '_8_HOURS_', '_12_HOURS_', '_72_HOURS_', '_1_DAY_', '_2_DAYS_', '_7_DAYS_', '_14_DAYS_', '_15_DAYS_', '_30_DAYS_', '_NEVER_']);
+ },
+
+ ReportProcessingStatuses: function() {
+ return new mws.Enum(['_SUBMITTED_', '_IN_PROGRESS_', '_CANCELLED_', '_DONE_', '_DONE_NO_DATA_']);
+ },
+
+ ReportOptions: function() {
+ return new mws.Enum(['ShowSalesChannel=true']);
+ }
+
+};
+
+/**
+ * A collection of currently supported request constructors. Once created and
+ * configured, the returned requests can be passed to an mws client `invoke` call
+ * @type {Object}
+ */
+var calls = exports.requests = {
+
+ GetReport: function() {
+ return new ReportsRequest('GetReport', {
+ ReportId: { name: 'ReportId', required: true }
+ });
+ },
+
+ GetReportCount: function() {
+ return new ReportsRequest('GetReportCount', {
+ ReportTypes: { name: 'ReportTypeList.Type', list: true},
+ Acknowledged: { name: 'Acknowledged', type: 'Boolean' },
+ AvailableFrom: { name: 'AvailableFromDate', type: 'Timestamp' },
+ AvailableTo: { name: 'AvailableToDate', type: 'Timestamp' }
+ });
+ },
+
+ GetReportList: function() {
+ return new ReportsRequest('GetReportList', {
+ MaxCount: { name: 'MaxCount' },
+ ReportTypes: { name: 'ReportTypeList.Type', list: true},
+ Acknowledged: { name: 'Acknowledged', type: 'Boolean' },
+ AvailableFrom: { name: 'AvailableFromDate', type: 'Timestamp' },
+ AvailableTo: { name: 'AvailableToDate', type: 'Timestamp' },
+ ReportRequestIds: { name: 'ReportRequestIdList.Id', list: true }
+ });
+ },
+
+ GetReportListByNextToken: function() {
+ return new ReportsRequest('GetReportListByNextToken', {
+ NextToken: { name: 'NextToken', required: true }
+ });
+ },
+
+ GetReportRequestCount: function() {
+ return new ReportsRequest('GetReportRequestCount', {
+ RequestedFrom: { name: 'RequestedFromDate', type: 'Timestamp' },
+ RequestedTo: { name: 'RequestedToDate', type: 'Timestamp' },
+ ReportTypes: { name: 'ReportTypeList.Type', list: true },
+ ReportProcessingStatuses: { name: 'ReportProcessingStatusList.Status', list: true, type: 'reports.ReportProcessingStatuses' }
+ });
+ },
+
+ GetReportRequestList: function() {
+ return new ReportsRequest('GetReportRequestList', {
+ MaxCount: { name: 'MaxCount' },
+ RequestedFrom: { name: 'RequestedFromDate', type: 'Timestamp' },
+ RequestedTo: { name: 'RequestedToDate', type: 'Timestamp' },
+ ReportRequestIds: { name: 'ReportRequestIdList.Id', list: true },
+ ReportTypes: { name: 'ReportTypeList.Type', list: true },
+ ReportProcessingStatuses: { name: 'ReportProcessingStatusList.Status', list: true, type: 'reports.ReportProcessingStatuses' }
+ });
+ },
+
+ GetReportRequestListByNextToken: function() {
+ return new ReportsRequest('GetReportRequestListByNextToken', {
+ NextToken: { name: 'NextToken', required: true }
+ });
+ },
+
+ CancelReportRequests: function() {
+ return new ReportsRequest('CancelReportRequests', {
+ RequestedFrom: { name: 'RequestedFromDate', type: 'Timestamp' },
+ RequestedTo: { name: 'RequestedToDate', type: 'Timestamp' },
+ ReportRequestIds: { name: 'ReportRequestIdList.Id', list: true },
+ ReportTypes: { name: 'ReportTypeList.Type', list: true },
+ ReportProcessingStatuses: { name: 'ReportProcessingStatusList.Status', list: true, type: 'reports.ReportProcessingStatuses' }
+ });
+ },
+
+ RequestReport: function() {
+ return new ReportsRequest('RequestReport', {
+ ReportType: { name: 'ReportType', required: true },
+ MarketplaceIds: { name: 'MarketplaceIdList.Id', list: true, required: false },
+ StartDate: { name: 'StartDate', type: 'Timestamp' },
+ EndDate: { name: 'EndDate', type: 'Timestamp' },
+ ReportOptions: { name: 'ReportOptions', type: 'reports.ReportOptions' }
+ });
+ },
+
+ ManageReportSchedule: function() {
+ return new ReportsRequest('ManageReportSchedule', {
+ ReportType: { name: 'ReportType', required: true },
+ Schedule: { name: 'Schedule', type: 'reports.Schedules', required: true },
+ ScheduleDate: { name: 'ScheduleDate', type: 'Timestamp' }
+ });
+ },
+
+ GetReportScheduleList: function() {
+ return new ReportsRequest('GetReportScheduleList', {
+ ReportTypes: { name: 'ReportTypeList.Type', list: true }
+ });
+ },
+
+ GetReportScheduleListByNextToken: function() {
+ return new ReportsRequest('GetReportScheduleListByNextToken', {
+ NextToken: { name: 'NextToken', required: true }
+ });
+ },
+
+ GetReportScheduleCount: function() {
+ return new ReportsRequest('GetReportScheduleCount', {
+ ReportTypes: { name: 'ReportTypeList.Type', list: true }
+ });
+ },
+
+ UpdateReportAcknowledgements: function() {
+ return new ReportsRequest('UpdateReportAcknowledgements', {
+ ReportIds: { name: 'ReportIdList.Id', list: true, required: true },
+ Acknowledged: { name: 'Acknowledged', type: 'Boolean' }
+ });
+ }
+
+};
View
67 lib/sellers.js
@@ -0,0 +1,67 @@
+/**
+ * Sellers API requests and definitions for Amazon's MWS web services.
+ * For information on using, please see examples folder.
+ *
+ * @author Robert Saunders
+ */
+var mws = require('./mws');
+
+/**
+ * Construct a Sellers API request for mws.Client.invoke()
+ *
+ * @param {String} action Action parameter of request
+ * @param {Object} params Schemas for all supported parameters
+ */
+function SellersRequest(action, params) {
+ var opts = {
+ name: 'Sellers',
+ group: 'Sellers Retrieval',
+ path: '/Sellers/2011-07-01',
+ version: '2011-07-01',
+ legacy: false,
+ action: action,
+ params: params
+ };
+ return new mws.Request(opts);
+}
+
+/**
+ * Contains brief definitions for unique data type values.
+ * Can be used to explain input/output to users via tooltips, for example
+ * @type {Object}
+ */
+var types = exports.types = {
+
+ ServiceStatus: {
+ 'GREEN':'The service is operating normally.',
+ 'GREEN_I':'The service is operating normally + additional info provided',
+ 'YELLOW':'The service is experiencing higher than normal error rates or degraded performance.',
+ 'RED':'The service is unabailable or experiencing extremely high error rates.' }
+
+};
+
+/**
+ * A collection of currently supported request constructors. Once created and
+ * configured, the returned requests can be passed to an mws client `invoke` call
+ * @type {Object}
+ */
+var calls = exports.requests = {
+
+ /**
+ * Requests the operational status of the Sellers API section.
+ */
+ GetServiceStatus: function() {
+ return new SellersRequest('GetServiceStatus', {});
+ },
+
+ ListMarketplaceParticipations: function() {
+ return new SellersRequest('ListMarketplaceParticipations', {});
+ },
+
+ ListMarketplaceParticipationsByNextToken: function() {
+ return new SellersRequest('ListMarketplaceParticipationsByNextToken', {
+ NextToken: { name: 'NextToken', required: true }
+ });
+ }
+
+};
View
2  node_modules/xml2js/.npmignore
@@ -0,0 +1,2 @@
+*.swp
+node_modules
View
12 node_modules/xml2js/Cakefile
@@ -0,0 +1,12 @@
+{spawn, exec} = require 'child_process'
+
+task 'build', 'continually build the JavaScript code', ->
+ coffee = spawn 'coffee', ['-cw', '-o', 'lib', 'src']
+ coffee.stdout.on 'data', (data) -> console.log data.toString().trim()
+
+task 'doc', 'rebuild the Docco documentation', ->
+ exec([
+ 'docco src/xml2js.coffee'
+ ].join(' && '), (err) ->
+ throw err if err
+ )
View
19 node_modules/xml2js/LICENSE
@@ -0,0 +1,19 @@
+Copyright 2010, 2011. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
View
139 node_modules/xml2js/README.md
@@ -0,0 +1,139 @@
+node-xml2js
+===========
+
+Ever had the urge to parse XML? And wanted to access the data in some sane,
+easy way? Don't want to compile a C parser, for whatever reason? Then xml2js is
+what you're looking for!
+
+Description
+===========
+
+Simple XML to JavaScript object converter. Uses
+[sax-js](https://github.com/isaacs/sax-js/).
+
+Note: If you're looking for a full DOM parser, you probably want
+[JSDom](https://github.com/tmpvar/jsdom).
+
+Installation
+============
+
+Simplest way to install `xml2js` is to use [npm](http://npmjs.org), just `npm
+install xml2js` which will download xml2js and all dependencies.
+
+Usage
+=====
+
+This will have to do, unless you're looking for some fancy extensive
+documentation. If you're looking for every single option and usage, see the
+unit tests.
+
+Simple as pie usage
+-------------------
+
+The simplest way to use it, is to use the optional callback interface added in
+0.1.11. That's right, if you have been using xml-simple or a home-grown
+wrapper, this is for you:
+
+```javascript
+var fs = require('fs'),
+ xml2js = require('xml2js');
+
+var parser = new xml2js.Parser();
+fs.readFile(__dirname + '/foo.xml', function(err, data) {
+ parser.parseString(data, function (err, result) {
+ console.dir(result);
+ console.log('Done');
+ });
+});
+```
+
+Look ma, no event listeners! Alternatively you can still use the traditional
+`addListener` variant:
+
+```javascript
+var fs = require('fs'),
+ xml2js = require('xml2js');
+
+var parser = new xml2js.Parser();
+parser.addListener('end', function(result) {
+ console.dir(result);
+ console.log('Done.');
+});
+fs.readFile(__dirname + '/foo.xml', function(err, data) {
+ parser.parseString(data);
+});
+```
+
+You can also use xml2js from
+[CoffeeScript](http://jashkenas.github.com/coffee-script/), further reducing
+the clutter:
+
+```coffeescript
+fs = require 'fs',
+xml2js = require 'xml2js'
+
+parser = new xml2js.Parser()
+fs.readFile __dirname + '/foo.xml', (err, data) ->
+ parser.parseString data, (err, result) ->
+ console.dir result
+ console.log 'Done.'
+```
+
+So you wanna some JSON?
+-----------------------
+
+Just wrap the `result` object in a call to `JSON.stringify` like this
+`JSON.stringify(result)`. You get a string containing the JSON representation
+of the parsed object that you can feed to JSON-hungry consumers.
+
+Displaying results
+------------------
+
+You might wonder why, using `console.dir` or `console.log` the output at some
+level is only `[Object]`. Don't worry, this is not because xml2js got lazy.
+That's because Node uses `util.inspect` to convert the object into strings and
+that function stops after `depth=2` which is a bit low for most XML.
+
+To display the whole deal, you can use `console.log(util.inspect(result, false,
+null))`, which displays the whole result.
+
+So much for that, but what if you use
+[eyes](https://github.com/cloudhead/eyes.js) for nice colored output and it
+truncates the output with `…`? Don't fear, there's also a solution for that,
+you just need to increase the `maxLength` limit by creating a custom inspector
+`var inspect = require('eyes').inspector({maxLength: false})` and then you can
+easily `inspect(result)`.
+
+Options
+=======
+
+Apart from the default settings, there is a number of options that can be
+specified for the parser. Options are specified by ``new Parser({optionName:
+value})``. Possible options are:
+
+ * `explicitCharkey` (default: `false`)
+ * `trim` (default: `true`): Trim the whitespace at the beginning and end of
+ text nodes.
+ * `normalize` (default: `true`): Trim whitespaces inside text nodes.
+ * `explicitRoot` (default: `false`): Set this if you want to get the root
+ node in the resulting object.
+ * `emptyTag` (default: `undefined`): what will the value of empty nodes be.
+ Default is `{}`.
+ * `explicitArray` (default: `false`): Always put child nodes in an array if
+ true; otherwise an array is created only if there is more than one.
+ * `ignoreAttrs` (default: `false`): Ignore all XML attributes and only create
+ text nodes.
+ * `mergeAttrs` (default: `false`): Merge attributes and child elements as
+ properties of the parent, instead of keying attributes off a child
+ attribute object. This option is ignored if `ignoreAttrs` is `false`.
+
+These default settings are for backward-compatibility (and might change in the
+future). For the most 'clean' parsing, you should disable `normalize` and
+`trimming` and enable `explicitRoot`.
+
+Running tests, development
+==========================
+
+The development requirements are handled by npm, you just need to install
+them. We also have a number of unit tests, they can be run using `zap`
+directly from the project root.
View
152 node_modules/xml2js/lib/xml2js.js
@@ -0,0 +1,152 @@
+(function() {
+ var events, isEmpty, sax;
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
+
+ sax = require('sax');
+
+ events = require('events');
+
+ isEmpty = function(thing) {
+ return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0;
+ };
+
+ exports.Parser = (function() {
+
+ __extends(Parser, events.EventEmitter);
+
+ function Parser(opts) {
+ this.parseString = __bind(this.parseString, this);
+ this.reset = __bind(this.reset, this);
+ var key, value;
+ this.options = {
+ explicitCharkey: false,
+ trim: true,
+ normalize: true,
+ attrkey: "@",
+ charkey: "#",
+ explicitArray: false,
+ ignoreAttrs: false,
+ mergeAttrs: false
+ };
+ for (key in opts) {
+ if (!__hasProp.call(opts, key)) continue;
+ value = opts[key];
+ this.options[key] = value;
+ }
+ this.reset();
+ }
+
+ Parser.prototype.reset = function() {
+ var attrkey, charkey, err, stack;
+ var _this = this;
+ this.removeAllListeners();
+ this.saxParser = sax.parser(true, {
+ trim: false,
+ normalize: false
+ });
+ err = false;
+ this.saxParser.onerror = function(error) {
+ if (!err) {
+ err = true;
+ return _this.emit("error", error);
+ }
+ };
+ this.EXPLICIT_CHARKEY = this.options.explicitCharkey;
+ this.resultObject = null;
+ stack = [];
+ attrkey = this.options.attrkey;
+ charkey = this.options.charkey;
+ this.saxParser.onopentag = function(node) {
+ var key, obj, _ref;
+ obj = {};
+ obj[charkey] = "";
+ if (!_this.options.ignoreAttrs) {
+ _ref = node.attributes;
+ for (key in _ref) {
+ if (!__hasProp.call(_ref, key)) continue;
+ if (!(attrkey in obj) && !_this.options.mergeAttrs) obj[attrkey] = {};
+ if (_this.options.mergeAttrs) {
+ obj[key] = node.attributes[key];
+ } else {
+ obj[attrkey][key] = node.attributes[key];
+ }
+ }
+ }
+ obj["#name"] = node.name;
+ return stack.push(obj);
+ };
+ this.saxParser.onclosetag = function() {
+ var nodeName, obj, old, s;
+ obj = stack.pop();
+ nodeName = obj["#name"];
+ delete obj["#name"];
+ s = stack[stack.length - 1];
+ if (obj[charkey].match(/^\s*$/)) {
+ delete obj[charkey];
+ } else {
+ if (_this.options.trim) obj[charkey] = obj[charkey].trim();
+ if (_this.options.normalize) {
+ obj[charkey] = obj[charkey].replace(/\s{2,}/g, " ").trim();
+ }
+ if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
+ obj = obj[charkey];
+ }
+ }
+ if (_this.options.emptyTag !== void 0 && isEmpty(obj)) {
+ obj = _this.options.emptyTag;
+ }
+ if (stack.length > 0) {
+ if (!_this.options.explicitArray) {
+ if (!(nodeName in s)) {
+ return s[nodeName] = obj;
+ } else if (s[nodeName] instanceof Array) {
+ return s[nodeName].push(obj);
+ } else {
+ old = s[nodeName];
+ s[nodeName] = [old];
+ return s[nodeName].push(obj);
+ }
+ } else {
+ if (!(s[nodeName] instanceof Array)) s[nodeName] = [];
+ return s[nodeName].push(obj);
+ }
+ } else {
+ if (_this.options.explicitRoot) {
+ old = obj;
+ obj = {};
+ obj[nodeName] = old;
+ }
+ _this.resultObject = obj;
+ return _this.emit("end", _this.resultObject);
+ }
+ };
+ return this.saxParser.ontext = this.saxParser.oncdata = function(text) {
+ var s;
+ s = stack[stack.length - 1];
+ if (s) return s[charkey] += text;
+ };
+ };
+
+ Parser.prototype.parseString = function(str, cb) {
+ if ((cb != null) && typeof cb === "function") {
+ this.on("end", function(result) {
+ this.reset();
+ return cb(null, result);
+ });
+ this.on("error", function(err) {
+ this.reset();
+ return cb(err);
+ });
+ }
+ if (str.toString().trim() === '') {
+ this.emit("end", null);
+ return true;
+ }
+ return this.saxParser.write(str.toString());
+ };
+
+ return Parser;
+
+ })();
+
+}).call(this);
View
9 node_modules/xml2js/node_modules/sax/AUTHORS
@@ -0,0 +1,9 @@
+# contributors sorted by whether or not they're me.
+Isaac Z. Schlueter <i@izs.me>
+Stein Martin Hustad <stein@hustad.com>
+Mikeal Rogers <mikeal.rogers@gmail.com>
+Laurie Harper <laurie@holoweb.net>
+Jann Horn <jann@Jann-PC.fritz.box>
+Elijah Insua <tmpvar@gmail.com>
+Henry Rawas <henryr@schakra.com>
+Justin Makeig <jmpublic@makeig.com>
View
23 node_modules/xml2js/node_modules/sax/LICENSE
@@ -0,0 +1,23 @@
+Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
View
215 node_modules/xml2js/node_modules/sax/README.md
@@ -0,0 +1,215 @@
+# sax js
+
+A sax-style parser for XML and HTML.
+
+Designed with [node](http://nodejs.org/) in mind, but should work fine in
+the browser or other CommonJS implementations.
+
+## What This Is
+
+* A very simple tool to parse through an XML string.
+* A stepping stone to a streaming HTML parser.
+* A handy way to deal with RSS and other mostly-ok-but-kinda-broken XML
+ docs.
+
+## What This Is (probably) Not
+
+* An HTML Parser - That's a fine goal, but this isn't it. It's just
+ XML.
+* A DOM Builder - You can use it to build an object model out of XML,
+ but it doesn't do that out of the box.
+* XSLT - No DOM = no querying.
+* 100% Compliant with (some other SAX implementation) - Most SAX
+ implementations are in Java and do a lot more than this does.
+* An XML Validator - It does a little validation when in strict mode, but
+ not much.
+* A Schema-Aware XSD Thing - Schemas are an exercise in fetishistic
+ masochism.
+* A DTD-aware Thing - Fetching DTDs is a much bigger job.
+
+## Regarding `<!DOCTYPE`s and `<!ENTITY`s
+
+The parser will handle the basic XML entities in text nodes and attribute
+values: `&amp; &lt; &gt; &apos; &quot;`. It's possible to define additional
+entities in XML by putting them in the DTD. This parser doesn't do anything
+with that. If you want to listen to the `ondoctype` event, and then fetch
+the doctypes, and read the entities and add them to `parser.ENTITIES`, then
+be my guest.
+
+Unknown entities will fail in strict mode, and in loose mode, will pass
+through unmolested.
+
+## Usage
+
+ var sax = require("./lib/sax"),
+ strict = true, // set to false for html-mode
+ parser = sax.parser(strict);
+
+ parser.onerror = function (e) {
+ // an error happened.
+ };
+ parser.ontext = function (t) {
+ // got some text. t is the string of text.
+ };
+ parser.onopentag = function (node) {
+ // opened a tag. node has "name" and "attributes"
+ };
+ parser.onattribute = function (attr) {
+ // an attribute. attr has "name" and "value"
+ };
+ parser.onend = function () {
+ // parser stream is done, and ready to have more stuff written to it.
+ };
+
+ parser.write('<xml>Hello, <who name="world">world</who>!</xml>').close();
+
+ // stream usage
+ // takes the same options as the parser
+ var saxStream = require("sax").createStream(strict, options)
+ saxStream.on("error", function (e) {
+ // unhandled errors will throw, since this is a proper node
+ // event emitter.
+ console.error("error!", e)
+ // clear the error
+ this._parser.error = null
+ this._parser.resume()
+ })
+ saxStream.on("opentag", function (node) {
+ // same object as above
+ })
+ // pipe is supported, and it's readable/writable
+ // same chunks coming in also go out.
+ fs.createReadStream("file.xml")
+ .pipe(saxStream)
+ .pipe(fs.createReadStream("file-copy.xml"))
+
+
+
+## Arguments
+
+Pass the following arguments to the parser function. All are optional.
+
+`strict` - Boolean. Whether or not to be a jerk. Default: `false`.
+
+`opt` - Object bag of settings regarding string formatting. All default to `false`.
+
+Settings supported:
+
+* `trim` - Boolean. Whether or not to trim text and comment nodes.
+* `normalize` - Boolean. If true, then turn any whitespace into a single
+ space.
+* `lowercase` - Boolean. If true, then lowercase tag names and attribute names
+ in loose mode, rather than uppercasing them.
+* `xmlns` - Boolean. If true, then namespaces are supported.
+
+## Methods
+
+`write` - Write bytes onto the stream. You don't have to do this all at
+once. You can keep writing as much as you want.
+
+`close` - Close the stream. Once closed, no more data may be written until
+it is done processing the buffer, which is signaled by the `end` event.
+
+`resume` - To gracefully handle errors, assign a listener to the `error`
+event. Then, when the error is taken care of, you can call `resume` to
+continue parsing. Otherwise, the parser will not continue while in an error
+state.
+
+## Members
+
+At all times, the parser object will have the following members:
+
+`line`, `column`, `position` - Indications of the position in the XML
+document where the parser currently is looking.
+
+`startTagPosition` - Indicates the position where the current tag starts.
+
+`closed` - Boolean indicating whether or not the parser can be written to.
+If it's `true`, then wait for the `ready` event to write again.
+
+`strict` - Boolean indicating whether or not the parser is a jerk.
+
+`opt` - Any options passed into the constructor.
+
+`tag` - The current tag being dealt with.
+
+And a bunch of other stuff that you probably shouldn't touch.
+
+## Events
+
+All events emit with a single argument. To listen to an event, assign a
+function to `on<eventname>`. Functions get executed in the this-context of
+the parser object. The list of supported events are also in the exported
+`EVENTS` array.
+
+When using the stream interface, assign handlers using the EventEmitter
+`on` function in the normal fashion.
+
+`error` - Indication that something bad happened. The error will be hanging
+out on `parser.error`, and must be deleted before parsing can continue. By
+listening to this event, you can keep an eye on that kind of stuff. Note:
+this happens *much* more in strict mode. Argument: instance of `Error`.
+
+`text` - Text node. Argument: string of text.
+
+`doctype` - The `<!DOCTYPE` declaration. Argument: doctype string.
+
+`processinginstruction` - Stuff like `<?xml foo="blerg" ?>`. Argument:
+object with `name` and `body` members. Attributes are not parsed, as
+processing instructions have implementation dependent semantics.
+
+`sgmldeclaration` - Random SGML declarations. Stuff like `<!ENTITY p>`
+would trigger this kind of event. This is a weird thing to support, so it
+might go away at some point. SAX isn't intended to be used to parse SGML,
+after all.
+
+`opentag` - An opening tag. Argument: object with `name` and `attributes`.
+In non-strict mode, tag names are uppercased, unless the `lowercase`
+option is set. If the `xmlns` option is set, then it will contain
+namespace binding information on the `ns` member, and will have a
+`local`, `prefix`, and `uri` member.
+
+`closetag` - A closing tag. In loose mode, tags are auto-closed if their
+parent closes. In strict mode, well-formedness is enforced. Note that
+self-closing tags will have `closeTag` emitted immediately after `openTag`.
+Argument: tag name.
+
+`attribute` - An attribute node. Argument: object with `name` and `value`.
+In non-strict mode, attribute names are uppercased, unless the `lowercase`
+option is set. If the `xmlns` option is set, it will also contains namespace
+information.
+
+`comment` - A comment node. Argument: the string of the comment.
+
+`opencdata` - The opening tag of a `<![CDATA[` block.
+
+`cdata` - The text of a `<![CDATA[` block. Since `<![CDATA[` blocks can get
+quite large, this event may fire multiple times for a single block, if it
+is broken up into multiple `write()`s. Argument: the string of random
+character data.
+
+`closecdata` - The closing tag (`]]>`) of a `<![CDATA[` block.
+
+`opennamespace` - If the `xmlns` option is set, then this event will
+signal the start of a new namespace binding.
+
+`closenamespace` - If the `xmlns` option is set, then this event will
+signal the end of a namespace binding.
+
+`end` - Indication that the closed stream has ended.
+
+`ready` - Indication that the stream has reset, and is ready to be written
+to.
+
+`noscript` - In non-strict mode, `<script>` tags trigger a `"script"`
+event, and their contents are not checked for special xml characters.
+If you pass `noscript: true`, then this behavior is suppressed.
+
+## Reporting Problems
+
+It's best to write a failing test if you find an issue. I will always
+accept pull requests with failing tests if they demonstrate intended
+behavior, but it is very hard to figure out what issue you're describing
+without a test. Writing a test is also the best way for you yourself
+to figure out if you really understand the issue you think you have with
+sax-js.
View
8,002 node_modules/xml2js/node_modules/sax/examples/big-not-pretty.xml
8,002 additions, 0 deletions not shown
View
41 node_modules/xml2js/node_modules/sax/examples/example.js
@@ -0,0 +1,41 @@
+
+var fs = require("fs"),
+ sys = require("sys"),
+ path = require("path"),
+ xml = fs.cat(path.join(__dirname, "test.xml")),
+ sax = require("../lib/sax"),
+ strict = sax.parser(true),
+ loose = sax.parser(false, {trim:true}),
+ inspector = function (ev) { return function (data) {
+ // sys.error("");
+ // sys.error(ev+": "+sys.inspect(data));
+ // for (var i in data) sys.error(i+ " "+sys.inspect(data[i]));
+ // sys.error(this.line+":"+this.column);
+ }};
+
+xml.addCallback(function (xml) {
+ // strict.write(xml);
+
+ sax.EVENTS.forEach(function (ev) {
+ loose["on"+ev] = inspector(ev);
+ });
+ loose.onend = function () {
+ // sys.error("end");
+ // sys.error(sys.inspect(loose));
+ };
+
+ // do this one char at a time to verify that it works.
+ // (function () {
+ // if (xml) {
+ // loose.write(xml.substr(0,1000));
+ // xml = xml.substr(1000);
+ // process.nextTick(arguments.callee);
+ // } else loose.close();
+ // })();
+
+ for (var i = 0; i < 1000; i ++) {
+ loose.write(xml);
+ loose.close();
+ }
+
+});
View
58 node_modules/xml2js/node_modules/sax/examples/get-products.js
@@ -0,0 +1,58 @@
+// pull out /GeneralSearchResponse/categories/category/items/product tags
+// the rest we don't care about.
+
+var sax = require("../lib/sax.js")
+var fs = require("fs")
+var path = require("path")
+var xmlFile = path.resolve(__dirname, "shopping.xml")
+var util = require("util")
+var http = require("http")
+
+fs.readFile(xmlFile, function (er, d) {
+ http.createServer(function (req, res) {
+ if (er) throw er
+ var xmlstr = d.toString("utf8")
+
+ var parser = sax.parser(true)
+ var products = []
+ var product = null
+ var currentTag = null
+
+ parser.onclosetag = function (tagName) {
+ if (tagName === "product") {
+ products.push(product)
+ currentTag = product = null
+ return
+ }
+ if (currentTag && currentTag.parent) {
+ var p = currentTag.parent
+ delete currentTag.parent
+ currentTag = p
+ }
+ }
+
+ parser.onopentag = function (tag) {
+ if (tag.name !== "product" && !product) return
+ if (tag.name === "product") {
+ product = tag
+ }
+ tag.parent = currentTag
+ tag.children = []
+ tag.parent && tag.parent.children.push(tag)
+ currentTag = tag
+ }
+
+ parser.ontext = function (text) {
+ if (currentTag) currentTag.children.push(text)
+ }
+
+ parser.onend = function () {
+ var out = util.inspect(products, false, 3, true)
+ res.writeHead(200, {"content-type":"application/json"})
+ res.end("{\"ok\":true}")
+ // res.end(JSON.stringify(products))
+ }
+
+ parser.write(xmlstr).end()
+ }).listen(1337)
+})
View
4 node_modules/xml2js/node_modules/sax/examples/hello-world.js
@@ -0,0 +1,4 @@
+require("http").createServer(function (req, res) {
+ res.writeHead(200, {"content-type":"application/json"})
+ res.end(JSON.stringify({ok: true}))
+}).listen(1337)
View
8 node_modules/xml2js/node_modules/sax/examples/not-pretty.xml
@@ -0,0 +1,8 @@
+<root>
+ something<else> blerm <slurm
+
+
+ attrib =
+ "blorg" ></else><!-- COMMENT!
+
+--><![CDATA[processing...]]> <selfclosing tag="blr>&quot;"/> a bit down here</root>
View
74 node_modules/xml2js/node_modules/sax/examples/pretty-print.js
@@ -0,0 +1,74 @@
+var sax = require("../lib/sax")
+ , printer = sax.createStream(false, {lowercasetags:true, trim:true})
+ , fs = require("fs")
+
+function entity (str) {
+ return str.replace('"', '&quot;')
+}
+
+printer.tabstop = 2
+printer.level = 0
+printer.indent = function () {
+ print("\n")
+ for (var i = this.level; i > 0; i --) {
+ for (var j = this.tabstop; j > 0; j --) {
+ print(" ")
+ }
+ }
+}
+printer.on("opentag", function (tag) {
+ this.indent()
+ this.level ++
+ print("<"+tag.name)
+ for (var i in tag.attributes) {
+ print(" "+i+"=\""+entity(tag.attributes[i])+"\"")
+ }
+ print(">")
+})
+
+printer.on("text", ontext)
+printer.on("doctype", ontext)
+function ontext (text) {
+ this.indent()
+ print(text)
+}
+
+printer.on("closetag", function (tag) {
+ this.level --
+ this.indent()
+ print("</"+tag+">")
+})
+
+printer.on("cdata", function (data) {
+ this.indent()
+ print("<![CDATA["+data+"]]>")
+})
+
+printer.on("comment", function (comment) {
+ this.indent()
+ print("<!--"+comment+"-->")
+})
+
+printer.on("error", function (error) {
+ console.error(error)
+ throw error
+})
+
+if (!process.argv[2]) {
+ throw new Error("Please provide an xml file to prettify\n"+
+ "TODO: read from stdin or take a file")
+}
+var xmlfile = require("path").join(process.cwd(), process.argv[2])
+var fstr = fs.createReadStream(xmlfile, { encoding: "utf8" })
+
+function print (c) {
+ if (!process.stdout.write(c)) {
+ fstr.pause()
+ }
+}
+
+process.stdout.on("drain", function () {
+ fstr.resume()
+})
+
+fstr.pipe(printer)
View
2  node_modules/xml2js/node_modules/sax/examples/shopping.xml
@@ -0,0 +1,2 @@
+
+<GeneralSearchResponse xmlns="urn:types.partner.api.shopping.com"><serverDetail><apiEnv>sandbox</apiEnv><apiVersion>3.1 r31.Kadu4DC.phase3</apiVersion><buildNumber>5778</buildNumber><buildTimestamp>2011.10.06 15:37:23 PST</buildTimestamp><requestId>p2.a121bc2aaf029435dce6</requestId><timestamp>2011-10-21T18:38:45.982-04:00</timestamp><responseTime>P0Y0M0DT0H0M0.169S</responseTime></serverDetail><exceptions exceptionCount="1"><exception type="warning"><code>1112</code><message>You are currently using the SDC API sandbox environment! No clicks to merchant URLs from this response will be paid. Please change the host of your API requests to 'publisher.api.shopping.com' when you have finished development and testing</message></exception></exceptions><clientTracking height="19" type="logo" width="106"><sourceURL>http://statTest.dealtime.com/pixel/noscript?PV_EvnTyp=APPV&amp;APPV_APITSP=10%2F21%2F11_06%3A38%3A45_PM&amp;APPV_DSPRQSID=p2.a121bc2aaf029435dce6&amp;APPV_IMGURL=http://img.shopping.com/sc/glb/sdc_logo_106x19.gif&amp;APPV_LI_LNKINID=7000610&amp;APPV_LI_SBMKYW=nikon&amp;APPV_MTCTYP=1000&amp;APPV_PRTID=2002&amp;APPV_BrnID=14804</sourceURL><hrefURL>http://www.shopping.com/digital-cameras/products</hrefURL><titleText>Digital Cameras</titleText><altText>Digital Cameras</altText></clientTracking><searchHistory><categorySelection id="3"><name>Electronics</name><categoryURL>http://www.shopping.com/xCH-electronics-nikon~linkin_id-7000610?oq=nikon</categoryURL></categorySelection><categorySelection id="449"><name>Cameras and Photography</name><categoryURL>http://www.shopping.com/xCH-cameras_and_photography-nikon~linkin_id-7000610?oq=nikon</categoryURL></categorySelection><categorySelection id="7185"><name>Digital Cameras</name><categoryURL>http://www.shopping.com/digital-cameras/nikon/products?oq=nikon&amp;linkin_id=7000610</categoryURL></categorySelection><dynamicNavigationHistory><keywordSearch dropped="false" modified="false"><originalKeyword>nikon</originalKeyword><resultKeyword>nikon</resultKeyword></keywordSearch></dynamicNavigationHistory></searchHistory><categories matchedCategoryCount="1" returnedCategoryCount="1"><category id="7185"><name>Digital Cameras</name><categoryURL>http://www.shopping.com/digital-cameras/nikon/products?oq=nikon&amp;linkin_id=7000610</categoryURL><items matchedItemCount="322" pageNumber="1" returnedItemCount="5"><product id="101677489"><name>Nikon D3100 Digital Camera</name><shortDescription>14.2 Megapixel, SLR Camera, 3 in. LCD Screen, With High Definition Video, Weight: 1 lb.</shortDescription><fullDescription>The Nikon D3100 digital SLR camera speaks to the growing ranks of enthusiastic D-SLR users and aspiring photographers by providing an easy-to-use and affordable entrance to the world of Nikon D-SLR’s. The 14.2-megapixel D3100 has powerful features, such as the enhanced Guide Mode that makes it easy to unleash creative potential and capture memories with still images and full HD video. Like having a personal photo tutor at your fingertips, this unique feature provides a simple graphical interface on the camera’s LCD that guides users by suggesting and/or adjusting camera settings to achieve the desired end result images. The D3100 is also the world’s first D-SLR to introduce full time auto focus (AF) in Live View and D-Movie mode to effortlessly achieve the critical focus needed when shooting Full HD 1080p video.</fullDescription><images><image available="true" height="100" width="100"><sourceURL>http://di1.shopping.com/images/pi/93/bc/04/101677489-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=2&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=1</sourceURL></image><image available="true" height="200" width="200"><sourceURL>http://di1.shopping.com/images/pi/93/bc/04/101677489-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=2&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=1</sourceURL></image><image available="true" height="300" width="300"><sourceURL>http://di1.shopping.com/images/pi/93/bc/04/101677489-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=2&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=1</sourceURL></image><image available="true" height="400" width="400"><sourceURL>http://di1.shopping.com/images/pi/93/bc/04/101677489-400x400-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=2&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=1</sourceURL></image><image available="true" height="500" width="606"><sourceURL>http://di1.shopping.com/images/pi/93/bc/04/101677489-606x500-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=2&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=1</sourceURL></image></images><rating><reviewCount>9</reviewCount><rating>4.56</rating><ratingImage height="18" width="91"><sourceURL>http://img.shopping.com/sc/pr/sdc_stars_sm_4.5.gif</sourceURL></ratingImage><reviewURL>http://www.shopping.com/Nikon-D3100/reviews~linkin_id-7000610</reviewURL></rating><minPrice>429.00</minPrice><maxPrice>1360.00</maxPrice><productOffersURL>http://www.shopping.com/Nikon-D3100/prices~linkin_id-7000610</productOffersURL><productSpecsURL>http://www.shopping.com/Nikon-D3100/info~linkin_id-7000610</productSpecsURL><offers matchedOfferCount="64" pageNumber="1" returnedOfferCount="5"><offer featured="false" id="-ZW6BMZqz6fbS-aULwga_g==" smartBuy="false" used="false"><name>Nikon D3100 Digital SLR Camera with 18-55mm NIKKOR VR Lens</name><description>The Nikon D3100 Digital SLR Camera is an affordable compact and lightweight photographic power-house. It features the all-purpose 18-55mm VR lens a high-resolution 14.2 MP CMOS sensor along with a feature set that's comprehensive yet easy to navigate - the intuitive onboard learn-as-you grow guide mode allows the photographer to understand what the 3100 can do quickly and easily. Capture beautiful pictures and amazing Full HD 1080p movies with sound and full-time autofocus. Availabilty: In Stock!</description><categoryId>7185</categoryId><manufacturer>Nikon</manufacturer><imageList><image available="true" height="100" width="100"><sourceURL>http://di102.shopping.com/images/di/2d/5a/57/36424d5a717a366662532d61554c7767615f67-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=1</sourceURL></image><image available="true" height="200" width="200"><sourceURL>http://di102.shopping.com/images/di/2d/5a/57/36424d5a717a366662532d61554c7767615f67-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=1</sourceURL></image><image available="true" height="300" width="300"><sourceURL>http://di102.shopping.com/images/di/2d/5a/57/36424d5a717a366662532d61554c7767615f67-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=1</sourceURL></image><image available="false" height="400" width="400"><sourceURL>http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=1</sourceURL></image><image available="true" height="350" width="350"><sourceURL>http://di102.shopping.com/images/di/2d/5a/57/36424d5a717a366662532d61554c7767615f67-350x350-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=1</sourceURL></image></imageList><stockStatus>in-stock</stockStatus><storeNotes>Free Shipping with Any Purchase!</storeNotes><basePrice currency="USD">529.00</basePrice><tax checkSite="true"></tax><shippingCost currency="USD">0.00</shippingCost><totalPrice checkSite="true"></totalPrice><originalPrice currency="USD">799.00</originalPrice><offerURL>http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=647&amp;BEFID=7185&amp;aon=%5E1&amp;MerchantID=475674&amp;crawler_id=475674&amp;dealId=-ZW6BMZqz6fbS-aULwga_g%3D%3D&amp;url=http%3A%2F%2Fwww.fumfie.com%2Fproduct%2F343.5%2Fshopping-com%3F&amp;linkin_id=7000610&amp;Issdt=111021183845&amp;searchID=p2.a121bc2aaf029435dce6&amp;DealName=Nikon+D3100+Digital+SLR+Camera+with+18-55mm+NIKKOR+VR+Lens&amp;dlprc=529.0&amp;crn=&amp;istrsmrc=1&amp;isathrsl=0&amp;AR=1&amp;NG=20&amp;NDP=200&amp;PN=1&amp;ST=7&amp;DB=sdcprod&amp;MT=phx-pkadudc2&amp;FPT=DSP&amp;NDS=&amp;NMS=&amp;MRS=&amp;PD=101677489&amp;brnId=14804&amp;IsFtr=0&amp;IsSmart=0&amp;DMT=&amp;op=&amp;CM=&amp;DlLng=1&amp;RR=1&amp;cid=&amp;semid1=&amp;semid2=&amp;IsLps=0&amp;CC=1&amp;SL=1&amp;FS=1&amp;code=&amp;acode=658&amp;category=&amp;HasLink=&amp;frameId=&amp;ND=&amp;MN=&amp;PT=&amp;prjID=&amp;GR=&amp;lnkId=&amp;VK=</offerURL><store authorizedReseller="false" id="475674" trusted="true"><name>FumFie</name><logo available="true" height="31" width="88"><sourceURL>http://img.shopping.com/cctool/merch_logos/475674.gif</sourceURL></logo><phoneNumber>866 666 9198</phoneNumber><ratingInfo><reviewCount>560</reviewCount><rating>4.27</rating><ratingImage height="18" width="91"><sourceURL>http://img.shopping.com/sc/mr/sdc_checks_45.gif</sourceURL></ratingImage><reviewURL>http://www.shopping.com/xMR-store_fumfie~MRD-475674~S-1~linkin_id-7000610</reviewURL></ratingInfo><countryFlag height="11" width="18"><sourceURL>http://img.shopping.com/sc/glb/flag/US.gif</sourceURL><countryCode>US</countryCode></countryFlag></store><sku>F343C5</sku></offer><offer featured="false" id="md1e9lD8vdOu4FHQfJqKng==" smartBuy="false" used="false"><name>Nikon Nikon D3100 14.2MP Digital SLR Camera with 18-55mm f/3.5-5.6 AF-S DX VR, Cameras</name><description>Nikon D3100 14.2MP Digital SLR Camera with 18-55mm f/3.5-5.6 AF-S DX VR</description><categoryId>7185</categoryId><manufacturer>Nikon</manufacturer><imageList><image available="true" height="100" width="100"><sourceURL>http://di109.shopping.com/images/di/6d/64/31/65396c443876644f7534464851664a714b6e67-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=2</sourceURL></image><image available="true" height="200" width="200"><sourceURL>http://di109.shopping.com/images/di/6d/64/31/65396c443876644f7534464851664a714b6e67-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=2</sourceURL></image><image available="true" height="300" width="300"><sourceURL>http://di109.shopping.com/images/di/6d/64/31/65396c443876644f7534464851664a714b6e67-300x300-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=2</sourceURL></image><image available="false" height="400" width="400"><sourceURL>http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=2</sourceURL></image><image available="true" height="352" width="385"><sourceURL>http://di109.shopping.com/images/di/6d/64/31/65396c443876644f7534464851664a714b6e67-385x352-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=2</sourceURL></image></imageList><stockStatus>in-stock</stockStatus><basePrice currency="USD">549.00</basePrice><tax checkSite="true"></tax><shippingCost currency="USD">0.00</shippingCost><totalPrice checkSite="true"></totalPrice><originalPrice currency="USD">549.00</originalPrice><offerURL>http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=779&amp;BEFID=7185&amp;aon=%5E1&amp;MerchantID=305814&amp;crawler_id=305814&amp;dealId=md1e9lD8vdOu4FHQfJqKng%3D%3D&amp;url=http%3A%2F%2Fwww.electronics-expo.com%2Findex.php%3Fpage%3Ditem%26id%3DNIKD3100%26source%3DSideCar%26scpid%3D8%26scid%3Dscsho318727%26&amp;linkin_id=7000610&amp;Issdt=111021183845&amp;searchID=p2.a121bc2aaf029435dce6&amp;DealName=Nikon+Nikon+D3100+14.2MP+Digital+SLR+Camera+with+18-55mm+f%2F3.5-5.6+AF-S+DX+VR%2C+Cameras&amp;dlprc=549.0&amp;crn=&amp;istrsmrc=1&amp;isathrsl=0&amp;AR=9&amp;NG=20&amp;NDP=200&amp;PN=1&amp;ST=7&amp;DB=sdcprod&amp;MT=phx-pkadudc2&amp;FPT=DSP&amp;NDS=&amp;NMS=&amp;MRS=&amp;PD=101677489&amp;brnId=14804&amp;IsFtr=0&amp;IsSmart=0&amp;DMT=&amp;op=&amp;CM=&amp;DlLng=1&amp;RR=9&amp;cid=&amp;semid1=&amp;semid2=&amp;IsLps=0&amp;CC=0&amp;SL=0&amp;FS=1&amp;code=&amp;acode=771&amp;category=&amp;HasLink=&amp;frameId=&amp;ND=&amp;MN=&amp;PT=&amp;prjID=&amp;GR=&amp;lnkId=&amp;VK=</offerURL><store authorizedReseller="false" id="305814" trusted="true"><name>Electronics Expo</name><logo available="true" height="31" width="88"><sourceURL>http://img.shopping.com/cctool/merch_logos/305814.gif</sourceURL></logo><phoneNumber>1-888-707-EXPO</phoneNumber><ratingInfo><reviewCount>371</reviewCount><rating>3.90</rating><ratingImage height="18" width="91"><sourceURL>http://img.shopping.com/sc/mr/sdc_checks_4.gif</sourceURL></ratingImage><reviewURL>http://www.shopping.com/xMR-store_electronics_expo~MRD-305814~S-1~linkin_id-7000610</reviewURL></ratingInfo><countryFlag height="11" width="18"><sourceURL>http://img.shopping.com/sc/glb/flag/US.gif</sourceURL><countryCode>US</countryCode></countryFlag></store><sku>NIKD3100</sku></offer><offer featured="false" id="yYuaXnDFtCY7rDUjkY2aaw==" smartBuy="false" used="false"><name>Nikon D3100 14.2-Megapixel Digital SLR Camera With 18-55mm Zoom-Nikkor Lens, Black</name><description>Split-second shutter response captures shots other cameras may have missed Helps eliminate the frustration of shutter delay! 14.2-megapixels for enlargements worth framing and hanging. Takes breathtaking 1080p HD movies. ISO sensitivity from 100-1600 for bright or dimly lit settings. 3.0in. color LCD for beautiful, wide-angle framing and viewing. In-camera image editing lets you retouch with no PC. Automatic scene modes include Child, Sports, Night Portrait and more. Accepts SDHC memory cards. Nikon D3100 14.2-Megapixel Digital SLR Camera With 18-55mm Zoom-Nikkor Lens, Black is one of many Digital SLR Cameras available through Office Depot. Made by Nikon.</description><categoryId>7185</categoryId><manufacturer>Nikon</manufacturer><imageList><image available="true" height="100" width="100"><sourceURL>http://di109.shopping.com/images/di/79/59/75/61586e4446744359377244556a6b5932616177-100x100-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=3</sourceURL></image><image available="true" height="200" width="200"><sourceURL>http://di109.shopping.com/images/di/79/59/75/61586e4446744359377244556a6b5932616177-200x200-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=3</sourceURL></image><image available="false" height="300" width="300"><sourceURL>http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=3</sourceURL></image><image available="false" height="400" width="400"><sourceURL>http://img.shopping.com/sc/ds/no_image_100X100.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=3</sourceURL></image><image available="true" height="250" width="250"><sourceURL>http://di109.shopping.com/images/di/79/59/75/61586e4446744359377244556a6b5932616177-250x250-0-0.jpg?p=p2.a121bc2aaf029435dce6&amp;a=1&amp;c=1&amp;l=7000610&amp;t=111021183845&amp;r=3</sourceURL></image></imageList><stockStatus>in-stock</stockStatus><basePrice currency="USD">549.99</basePrice><tax checkSite="true"></tax><shippingCost currency="USD">0.00</shippingCost><totalPrice checkSite="true"></totalPrice><originalPrice currency="USD">699.99</originalPrice><offerURL>http://statTest.dealtime.com/DealFrame/DealFrame.cmp?bm=698&amp;BEFID=7185&amp;aon=%5E1&amp;MerchantID=467671&amp;crawler_id=467671&amp;dealId=yYuaXnDFtCY7rDUjkY2aaw%3D%3D&amp;url=http%3A%2F%2Flink.mercent.com%2Fredirect.ashx%3Fmr%3AmerchantID%3DOfficeDepot%26mr%3AtrackingCode%3DCEC9669E-6ABC-E011-9F24-0019B9C043EB%26mr%3AtargetUrl%3Dhttp%3A%2F%2Fwww.officedepot.com%2Fa%2Fproducts%2F486292%2FNikon-D3100-142-Megapixel-Digital-SLR%2F%253fcm_mmc%253dMercent-_-Shopping-_-Cameras_and_Camcorders-_-486292&amp;linkin_id=7000610&amp;Issdt=111021183845&amp;searchID=p2.a121bc2aaf029435dce6&amp;DealName=Nikon+D3100+14.2-Megapixel+Digital+SLR+Camera+With+18-55mm+Zoom-Nikkor+Lens%2C+Black&amp;dlprc=549.99&amp;crn=&amp;istrsmrc=1&amp;isathrsl=0&amp;AR=10&amp;NG=20&amp;NDP=200&amp;PN=1&amp;ST=7&amp;DB=sdcprod&amp;MT=phx-pkadudc2&amp;FPT=DSP&amp;NDS=&amp;NMS=&amp;MRS=&amp;PD=101677489&amp;brnId=14804&amp;IsFtr=0&amp;IsSmart=0&amp;DMT=&amp;op=&amp;CM=&amp;DlLng=1&amp;RR=10&amp;cid=&amp;semid1=&amp;semid2=&amp;IsLps=0&amp;CC=1&amp;SL=1&amp;FS=1&amp;code=&amp;acode=690&amp;category=&amp;HasLink=&amp;frameId=&amp;ND=&amp;MN=&amp;PT=&amp;prjID=&amp;GR=&amp;lnkId=&amp;VK=</offerURL><store authorizedReseller="false" id="467671" trusted="true"><name>Office Depot</name><logo available="true" height="31" width="88"><sourceURL>http://img.shopping.com/cctool/merch_logos/467671.gif</sourceURL></logo><phoneNumber>1-800-GO-DEPOT</phoneNumber><ratingInfo><reviewCount>135</reviewCount><rating>2.37</rating><ratingImage height="18" width="91"><sourceURL>http://img.shopping.com/sc/mr/sdc_checks_25.gif</sourceURL></ratingImage><reviewURL>http://www.shopping.com/xMR-store_office_depot_4158555~MRD-467671~S-1~linkin_id-7000610</reviewURL></ratingInfo><countryFlag height="11" width="18"><sourceURL>http://img.shopping.com/sc/glb/flag/US.gif</sourceURL><countryCode>US</countryCode></countryFlag></store><sku>486292</sku></offer><offer featured="false" id="Rl56U7CuiTYsH4MGZ02lxQ==" smartBuy="false" used="false"><name>Nikon® D3100™ 14.2MP Digital SLR with 18-55mm Lens</name><description>The Nikon D3100 DSLR will surprise you with its simplicity and impress you with superb results.</description><categoryId>7185</categoryId><manufacturer>Nikon</manufacturer><imageList><image available="true" height="100" width="100"><sourceURL>http://di103.shopping.com/images