Repository: chrisdiana/simplestore Branch: master Commit: ec7c14138ce7 Files: 13 Total size: 38.4 KB Directory structure: gitextract_sfvr6rzj/ ├── .gitignore ├── Gruntfile.js ├── LICENSE.md ├── README.md ├── bower.json ├── css/ │ └── simpleStore.css ├── index.html ├── js/ │ ├── config.js │ └── simpleStore.js ├── package.json ├── plugins/ │ └── google-sheets/ │ ├── DemoSpreadsheet.xlsx │ └── google-sheets.js └── products.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store node_modules/* bower_components/* release/* release.zip .idea/ dev/* ================================================ FILE: Gruntfile.js ================================================ module.exports = function(grunt) { /* * To build a release run 'grunt' * index.html has to be manually copied to 'release/' because of * reference differences (import.min.css, jquery) */ var minBanner = '/* <%= pkg.name %> | Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %> (<%= pkg.homepage %>) | <%= pkg.license %> license | v<%= pkg.version %> */'; var largeBanner = '/*' + '\n' + '* <%= pkg.name %> v<%= pkg.version %>' + '\n' + '* Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %>' + '\n' + '* <%= pkg.homepage %>' + '\n' + '* Free to use under the MIT license.' + '\n' + '* http://www.opensource.org/licenses/mit-license.php' + '\n' + '*/' + '\n'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: minBanner + '\n' }, build: { files: { 'js/simpleStore.min.js': 'js/simpleStore.js' } } }, cssmin: { target: { files: { 'css/simpleStore.min.css': 'css/simpleStore.css' } } }, file_append: { default_options: { files: [ {prepend: minBanner + '\n', input: 'css/simpleStore.min.css'} ] } }, copy: { main: { files: [ {src: 'css/simpleStore.min.css', dest: 'release/css/simpleStore.min.css'}, {src: 'js/simpleStore.min.js', dest: 'release/js/simpleStore.min.js'}, {src: 'js/simpleCart.min.js', dest: 'release/js/simpleCart.min.js'}, {src: 'js/config.js', dest: 'release/js/config.js'}, {src: 'products.json', dest: 'release/products.json'}, {src: 'images/favicon.png', dest: 'release/images/favicon.png'} ] }, } }); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-file-append'); grunt.registerTask('default', ['uglify', 'cssmin', 'file_append', 'copy']); }; ================================================ FILE: LICENSE.md ================================================ The MIT License (MIT) Copyright (c) 2015 Chris Diana 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. ================================================ FILE: README.md ================================================ **UPDATES COMING SOON! [Get notified](http://eepurl.com/dHBK11)** [Sign up](http://eepurl.com/gntUvf) to get updates on new features and releases # simpleStore [simpleStore](http://chrisdiana.github.io/simplestore) is a clean, responsive storefront boilerplate with no database you can setup in minutes. simpleStore is built on [simpleCart.js](http://simplecartjs.org) and [Skeleton](http://getskeleton.com) CSS Framework for a lightweight, fast, simple to use, and completely customizable experience. ![simpleStore Screenshot](https://raw.githubusercontent.com/chrisdiana/simplestore/gh-pages/images/screenshot-v1.1-full.png) ![simpleStore Cart Screenshot](https://raw.githubusercontent.com/chrisdiana/simplestore/gh-pages/images/screenshot-v1.1-cart.png) ![simpleStore Detail Screenshot](https://raw.githubusercontent.com/chrisdiana/simplestore/gh-pages/images/screenshot-v1.1-detail.png) --- # Features * No Databases, all client-side (just simple HTML, CSS & Javascript) * Lightweight & Fast * Tax Rate Calculations * Unlimited product attributes * Shipping * Multiple Currencies * Payment Gateways (Paypal, Google Checkout, Amazon Payments) * For more features check out [simpleCart.js](http://simplecartjs.org) # Plugins * Google Sheets (Control products from a Google Sheet instead of JSON file) # Demo You can see a working demo [here](http://chrisdiana.github.io/simplestore/demo/) # Installation Install with Bower ``` bower install ``` or manually install using the latest [release](https://github.com/chrisdiana/simplestore/releases/latest) # Setup 1.Make sure simpleStore is on a web server (any type will do as long as it can serve static web pages). 2.Configure your payment options in `js/config.js`. ``` checkout: { type: "PayPal" , email: "you@yours.com" }, ``` 3.Edit the `js/config.js` to your liking. 4.Add additional products in the `products.json` file. # Using Plugins To use a plugin, add a reference just before your `config.js` file ``` ``` ## HTML Version If you are looking for something more basic, check out the [HTML version on this branch](https://github.com/chrisdiana/simplestore/tree/simplestore-html). The HTML version uses plain HTML to build the store instead of a JSON file. Add additional products using the `
` tags. ## Credit where credit is due For further documentation on expanding/tweaking simpleStore, check out the framework/plugin pages. * [Skeleton](http://getskeleton.com) * [simpleCart.js](http://simplecartjs.org) * [Normalize.css](http://necolas.github.io/normalize.css) * [FontAwesome](http://fortawesome.github.io/Font-Awesome) * [jQuery](https://jquery.com/) ### A note about JavaScript shopping carts ALL JavaScript shopping carts are NOT fullproof. Because simpleStore is fully client-side, some users may attempt to alter prices before checkout. SimpleStore does the best it can to minimize this kind of activity. Make sure to monitor your sales. Just like in real life, if someone walks into your store and changes the price tag, you will certainly not honor those changes. # Contributing All forms of contribution are welcome: bug reports, bug fixes, pull requests and simple suggestions. If you do wish to contribute, please follow the [Airbnb Javascript Style Guide](https://github.com/airbnb/javascript) Thanks! ## List of contributors You can find the list of contributors [here](https://github.com/chrisdiana/simplestore/graphs/contributors). ================================================ FILE: bower.json ================================================ { "name": "simpleStore", "description": "A clean, responsive storefront boilerplate with no database or backend.", "version": "1.1.1", "homepage": "http://cdmedia.github.io/simplestore", "repository": { "type": "git", "url": "https://github.com/cdmedia/simplestore" }, "main": "index.html", "license": "MIT", "dependencies": { "jquery": "latest", "skeleton": "~2.0.4", "fontawesome": "latest" } } ================================================ FILE: css/simpleStore.css ================================================ /* * simplestore * Copyright 2015 Chris Diana * https://github.com/cdmedia/simplestore * Free to use under the MIT license. * http://www.opensource.org/licenses/mit-license.php */ select { display: block; float: left; margin-right: 10px; } a.hover { text-decoration: none; } /* Main Layout */ .simpleStore { margin-top: 20px; } .brand { display: block; float: left; text-decoration: none; color: #000; } .brand:hover { color: #777; } .brand img { max-height: 50px; } /* simpleCart */ .simpleCart_shelfItem { border: 1px solid #EFEFEF; padding: 25px 18px 10px 18px; margin: 15px 0; } .simpleCart_items { display: table; border-collapse: collapse; width: 100%; min-height: 20px; margin-bottom: 50px; } .simpleCart_items div:first-child { width: 100% } .simpleCart_decrement, .simpleCart_increment { font-size: 18px; line-height: 5px; text-decoration: none; } .simpleCart_shelfItem label { display: block; float: left; line-height: 40px; margin-right: 10px; } .item_thumb, .detail_thumb { height: 150px; display: block; margin: 0 auto; padding-bottom: 10px; } .item_Quantity { display: inline-block; width: 50px; } .item_price { font-weight: 700; font-size: 20px; line-height: 22px; } .item_description { margin-top: 1rem; margin-bottom: 1.5rem; } .headerRow { display: table-row; font-weight: 700; border-bottom: 1px solid #E1E1E1; } .headerRow div { display: table-cell; padding: 10px; } .itemRow.odd { background: #FAFAFA; } .itemRow { display: table-row; border-bottom: 1px solid #E1E1E1; } .itemRow:last-child { border-bottom: 0 solid #fff; } .itemRow div { display: table-cell; padding: 10px; } .itemRow .item-quantity { text-align: center; } /* Cart */ .cart_info { position: absolute; bottom: 0px; right: 0px; margin: 22px; text-align: right; } .cart_info div { display: inline; } .cart_info_item { display: block !important; } .cart_total { font-size: 20px; } /* Views */ .simpleStore_getDetail_container { display: block; float: left; width: 50%; padding: 5px 0px; } .simpleStore_detailView { margin: 15px 0px; border: 1px solid #EFEFEF; position: relative; } .simpleStore_detailView.simpleCart_shelfItem { padding: 26px 18px 8px 18px; } .simpleStore_detailView .item_add { margin: 0px; margin-bottom: 0px; } .simpleStore_detailView .item_name { margin-bottom: 1rem; } .simpleStore_detailView .item_price { display: block; margin-bottom: 1.5rem; } .simpleStore_cart_container { display: none; } .simpleStore_cart { position: relative; border: 1px solid #EFEFEF; padding: 22px; min-height: 230px; margin: 15px 0px; } a.simpleCart_empty { color: #CDCDCD; text-decoration: none; text-transform: uppercase; font-size: 14px; } a.simpleCart_empty:hover { color: #777; } .simpleCart_checkout { margin: 12px 0px 0px 0px; } /* Utility */ .hidden { display: none; } .loader { display: inline-block; font-size: 60px; line-height: 50px; color: #ddd; margin: 120px auto; width: 100%; height: 50px; text-align: center; vertical-align: bottom; } a.back { position: absolute; top: 10px; left: 18px; line-height: 0px; margin: 0; padding: 0; font-size: 34px; color: #CDCDCD; text-decoration: none; } a.back:hover { color: #777; } a.close { position: absolute; top: 18px; right: 14px; margin: 0; line-height: 0px; padding: 0; font-size: 32px; color: #CDCDCD; text-decoration: none; font-family: Garamond, "Apple Garamond"; } a.close:hover { color: #777; } .chevron::before { border-style: solid; border-width: 0.1em 0.1em 0 0; content: ''; display: inline-block; height: 0.45em; position: relative; top: 0.15em; transform: rotate(-45deg); vertical-align: top; width: 0.45em; left: 0.25em; transform: rotate(-135deg); } .error { background: #FFE5E5; padding: 15px; border-radius: 5px; position: relative; margin-bottom: 10px; } p.error_text { margin-bottom: 0rem; } .notify { background: #D9F6FF; background: rgba(81, 81, 81, 0.8); color: #fff; position: fixed; padding: 14px 0; text-align: center; margin: 0 auto; width: 100%; font-size: 14px; top: 0; right: 0; top: 0; z-index: 9999; } p.notify_text { margin-bottom: 0rem; } @media (max-width: 920px) { .simpleStore_getDetail_container { width: 100%; } .simpleCart_shelfItem { position: relative; } .simpleCart_shelfItem .item_name { font-size: 1.8rem; } .simpleStore_getDetail { position: inherit; display: block; width: 100%; margin: 0px; margin-bottom: 0px; } } @media (max-width: 620px) { .simpleStore_detailView .item_thumb { margin-bottom: 10px; display: block; width: 100%; height: auto; } .simpleStore_detailView .item_add { position: inherit; display: block; width: 100%; } .cart_info { position: inherit; margin: 0px; padding-top: 15px; text-align: left; border-top: 1px solid #E1E1E1; } .simpleCart_checkout { width: 100%; } .simpleCart_items { font-size: 12px; } /* Better mobile support for cart */ .item-decrement, .item-increment, .item-total { display: none !important; } } ================================================ FILE: index.html ================================================ simpleStore
Cart
================================================ FILE: js/config.js ================================================ $(function() { simpleCart({ // array representing the format and columns of the cart, see // the cart columns documentation cartColumns: [ { attr: "name" , label: "Name" }, { attr: "price" , label: "Price", view: 'currency' }, { view: "decrement" , label: false }, { attr: "quantity" , label: "Qty" }, { view: "increment" , label: false }, { attr: "total" , label: "SubTotal", view: 'currency' }, { view: "remove" , text: "Remove" , label: false } ], // "div" or "table" - builds the cart as a table or collection of divs cartStyle: "div", // how simpleCart should checkout, see the checkout reference for more info checkout: { type: "PayPal" , email: "you@yours.com" }, // set the currency, see the currency reference for more info currency: "USD", // collection of arbitrary data you may want to store with the cart, // such as customer info data: {}, // set the cart langauge (may be used for checkout) language: "english-us", // array of item fields that will not be sent to checkout excludeFromCheckout: [ 'qty', 'thumb' ], // custom function to add shipping cost shippingCustom: null, // flat rate shipping option shippingFlatRate: 0, // added shipping based on this value multiplied by the cart quantity shippingQuantityRate: 0, // added shipping based on this value multiplied by the cart subtotal shippingTotalRate: 0, // tax rate applied to cart subtotal taxRate: 0, // true if tax should be applied to shipping taxShipping: false, // event callbacks beforeAdd : null, afterAdd : null, load : null, beforeSave : null, afterSave : null, update : null, ready : null, checkoutSuccess : null, checkoutFail : null, beforeCheckout : null }); simpleStore.init({ // brand can be text or image URL brand : "SimpleStore", // numder of products per row (accepts 1, 2 or 3) numColumns : 3, // name of JSON file, located in directory root JSONFile : "products.json" }); }); ================================================ FILE: js/simpleStore.js ================================================ /* * simplestore * Copyright 2015 Chris Diana * https://github.com/cdmedia/simplestore * Free to use under the MIT license. * http://www.opensource.org/licenses/mit-license.php */ var simpleStore = { products: [], plugins: {}, // Default settings settings: { numColumns: 3, brand: "SimpleStore", mode: "JSON", JSONFile: "products.json", fadeSpeed: 200, buttonColor: null, backgroundColor: null, textColor: null, container: $('.simpleStore_container'), cartContainer: $('.simpleStore_cart_container'), rowClass: 'simpleStore_row_', columnWidthClasses: { 1: "", 2: "one-half", 3: "one-third" } }, productPageOptions: [ 'OneOfAKind' ], extend: function (target, opts, callback) { var next; if (typeof opts === "undefined") { opts = target; target = simpleStore; } for (next in opts) { if (Object.prototype.hasOwnProperty.call(opts, next)) { target[next] = opts[next]; } } callback(); // check user config options return target; }, render: function (url, s) { var type = url.split('/')[0]; var map = { // Main view '': function () { simpleStore.renderProducts(simpleStore.products, s); }, // Detail view '#product': function () { var id = url.split('#product/')[1].trim(); simpleStore.renderSingleProduct(id, s); }, // Cart view '#cart': function () { simpleStore.renderCart(s); } }; if (map[type]) { map[type](); } else { simpleStore.renderError(s); } }, insertData: function (tmpl, product) { tmpl.find('.item_thumb').attr("src", product.image); tmpl.find('.item_name').text(product.name); tmpl.find('.item_price').text(product.price); tmpl.find('.item_description').text(product.description); }, renderProducts: function (products, s) { var rowCount = 1, numProducts = products.length, numRows = Math.ceil(products.length / s.numColumns), itemWidth; s.cartContainer.hide(); s.container.fadeOut(s.fadeSpeed, function () { // Empty out main container on load s.container.html('').fadeIn(s.fadeSpeed); // Build rows based on number of products for (var r = 0; r < numRows; r++) { s.container.append('
'); } // Get item column width var widthClasses = s.columnWidthClasses; for (var k in widthClasses) { if (k == s.numColumns) { itemWidth = widthClasses[k]; } } // List layout products.forEach(function (product, i) { if (!product.soldOut) { var tmpl = $('#products-template').html(), $tmpl = $(tmpl); // Set item width $tmpl.first().addClass(itemWidth); // Insert data into template simpleStore.insertData($tmpl, product); // Render detail view on hash change var getDetail = $tmpl.find('.simpleStore_getDetail'); getDetail.on('click', function (e) { e.preventDefault(); window.location.hash = 'product/' + product.id; }); // Check where to add new item based on row if (i === 0) { i = 1; } if (i % (s.numColumns) === 0) { rowCount++; } // Append to appropriate container $('.' + s.rowClass + rowCount).append($tmpl); } }); }); }, renderProductOptions: function (options, s) { var optionsLayout = ''; options.forEach(function (option) { if (!(simpleStore.productPageOptions in option)) { var selectItems = ''; var attributeLabel = Object.keys(option)[0].trim(); var attributeValues = option[attributeLabel].trim().split(","); // Set attribute values $(attributeValues).each(function (attribute, attributeValue) { selectItems += ''; }); // Build options layout if (options.length) { optionsLayout += ''; } } else { simpleStore.renderProductPageOptions(option); } }); return optionsLayout; }, renderProductPageOptions: function (option) { if (option.OneOfAKind) { $('.qty').hide(); } }, renderSingleProduct: function (id, s) { s.container.fadeOut(s.fadeSpeed, function () { var tmpl = $('#product-detail-template').html(), $tmpl = $(tmpl); simpleStore.products.forEach(function (product) { if (product.id == id) { // Insert data into template simpleStore.insertData($tmpl, product); // Load detail view into main container s.container.html($tmpl); // Render product options if (product.options.length) { var options = simpleStore.renderProductOptions(product.options, s); $('.simpleStore_options').append(options); } s.container.fadeIn(s.fadeSpeed); } }); }); }, renderCart: function (s) { s.container.fadeOut(s.fadeSpeed, function () { s.cartContainer.fadeIn(s.fadeSpeed); }); }, renderError: function (s, msg) { var tmpl = $('#error-template').html(), $tmpl = $(tmpl); // Empty out main container on load s.container.html('').fadeIn(s.fadeSpeed); if (msg.length) { $tmpl.find('.error_text').text(msg); } s.container.append($tmpl); s.container.fadeIn(s.fadeSpeed); $tmpl.find('.alert_close').on('click', function (e) { e.preventDefault(); $tmpl.fadeOut(s.fadeSpeed, function() { $tmpl.remove(); }); }); }, handleFailure: function(s, errorMsg) { setTimeout(function () { simpleStore.renderError(s, errorMsg); }, 1000); }, notifier: function(msg) { s = this.settings; var tmpl = $('#notify-template').html(), $tmpl = $(tmpl); if (msg.length) { $tmpl.find('.notify_text').text(msg); s.container.append($tmpl); $tmpl.hide(); $tmpl.fadeIn(s.fadeSpeed); setTimeout(function () { $tmpl.fadeOut(s.fadeSpeed); }, 1000); } }, initJSON: function (s) { var errorMsg = 'There was an error loading the JSON file.' + ' Please make sure you have "' + s.JSONFile + '" file in' + ' your main directory.'; // Checks to make sure file exists $.get(s.JSONFile) .success(function () { // Get product data from JSON file $.getJSON(s.JSONFile, function (data) { simpleStore.setProducts(data.products); }) .fail(function () { simpleStore.handleFailure(s, errorMsg); }); }) .fail(function () { simpleStore.handleFailure(s, errorMsg); }); }, checkMode : function (s) { if (s.hasOwnProperty("spreadsheetID") || s.hasOwnProperty("spreadsheetId")) { s.mode = "Google"; } }, checkout : function (s, checkoutData) { if (!$.isEmptyObject(checkoutData)) { simpleCart.checkout(); s.cartContainer.fadeOut(s.fadeSpeed, function () { s.container.html(''); s.container.fadeIn(s.fadeSpeed); }); } }, verifyCheckoutData : function (cdata, adata, v) { for (var d in cdata) { if (cdata.hasOwnProperty(d)) { var cp = cdata[d], cn = cp.name, cpp = cp.price; for (var i = 0; i < adata.length; i++) { var ap = adata[i], an = ap.name, app = ap.price; if (cn === an) {if (cpp != app) { v = false; }} } } } return v; }, validatePrices : function (s) { var checkoutData = JSON.parse(localStorage.simpleCart_items), errorMsg = 'There was an error validating your cart.'; if (s.mode === "JSON") { $.get(s.JSONFile) .success(function () { $.getJSON(s.JSONFile, function (data) { var JSONData = data.products; if (simpleStore.verifyCheckoutData(checkoutData, JSONData, true)) { simpleStore.checkout(s, checkoutData); } else { simpleStore.renderError(s, errorMsg); } }) .fail(function () { simpleStore.handleFailure(s, errorMsg); }); }) .fail(function () { simpleStore.handleFailure(s, errorMsg); }); } else { var plugin = s.mode.toLowerCase(); if(simpleStore.plugins[plugin]) { simpleStore.plugins[plugin].validate(checkoutData); } } }, setProducts: function (products, s) { if (products.length > 0) { products.forEach(function (product, index) { product.id = index + 1; simpleStore.products.push(product); }); } // Manually trigger on initial load $(window).trigger('hashchange'); }, setLayout: function (s) { // Set brand if (s.brand.match('^http://') || s.brand.match('^https://') || s.brand.match('^www.')) { $('.brand').html(''); } else { $('.brand').html('
' + s.brand + '
'); } // Set title $('title').html(s.brand); }, generateCart: function (s) { var tmpl = $('#cart-template').html(), $tmpl = $(tmpl); s.cartContainer.html($tmpl); }, generateStore: function () { var s = this.settings; // Set mode this.checkMode(s); // Check for hash changes $(window).on('hashchange', function () { simpleStore.render(window.location.hash, s); }); // Set products based on mode switch (s.mode) { case 'JSON': this.initJSON(s); break; case 'Google': if(simpleStore.plugins.google) { simpleStore.plugins.google.init(function (products) { simpleStore.setProducts(products, s); }); } else { var errorMsg = 'There was an error loading the Google plugin. Make sure it is installed properly.'; simpleStore.renderError(s, errorMsg); } break; default: this.initJSON(s); } // Because simpleCart items appends to cart, set up only once this.generateCart(s); // Setup layout this.setLayout(s); // Handle Checkout $('.simpleStore_checkout').on('click', function (e) { e.preventDefault(); simpleStore.validatePrices(s); }); // View Cart $('.simpleStore_viewCart').on('click', function (e) { e.preventDefault(); window.location = '#cart'; }); // Go to home on close $('.view_close').on('click', function (e) { e.preventDefault(); window.location.hash = ''; }); // SimpleCart extend simpleCart({ afterAdd: function() { simpleStore.notifier('Item added to cart'); } }); }, init: function (options) { if ($.isPlainObject(options)) { return this.extend(this.settings, options, function () { simpleStore.generateStore(); }); } } }; ================================================ FILE: package.json ================================================ { "name": "simplestore", "version": "1.5", "description": "A clean, responsive storefront boilerplate with no database or backend.", "main": "index.html", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/cdmedia/simplestore.git" }, "author": "Chris Diana", "license": "MIT", "bugs": { "url": "https://github.com/cdmedia/simplestore/issues" }, "private": true, "homepage": "https://github.com/cdmedia/simplestore", "devDependencies": { "grunt": "~0.4.4", "grunt-contrib-copy": "latest", "grunt-contrib-cssmin": "latest", "grunt-contrib-uglify": "latest", "grunt-contrib-watch": "latest", "grunt-file-append": "0.0.6" } } ================================================ FILE: plugins/google-sheets/google-sheets.js ================================================ /* * SimpleStore Google Sheets Plugin * To use Google spreadsheet as your database, follow the steps below: * 1. Use the "DemoSpreadsheet.xlsx" as a starting point * 2. Create a new Google spreadsheet * 3. Set sharing permissions to either “Public” or set to “Anyone with link can view” * 4. Publish the sheet (File -> Publish to the web -> Publish) * 5. Add the spreadsheet ID to your 'config.js' ( spreadsheetID : "XXXXXXXXXXXXXXXXXXXXXXX" ) */ simpleStore.plugins.google = (function() { var storeProducts = verifyProducts = []; function getSpreadsheetData(s, verify, callback) { verify = typeof verify !== 'undefined' ? verify : false; var hostname = "https://spreadsheets.google.com"; var format = "json"; var spreadsheetURL = hostname + "/feeds/worksheets/" + s.spreadsheetID + "/public/full?alt=" + format; var mainsheetURL = hostname + "/feeds/list/" + s.spreadsheetID + "/od6/public/values?alt=" + format; var settingsSheetName = "Settings"; var productsSheetName = "Products"; var sheetIDs = {}; function getSheetInfo (url, callback) { // Need to do this because od6 is default Google Sheet ID $.getJSON(url) .done(function(data) { var sheets = data.feed.entry; $(sheets).each(function(i, sheet) { var title = sheet.title.$t; var id = sheet.id.$t; var sheetID = id.substr(id.lastIndexOf('/') + 1); if(title == settingsSheetName) { sheetIDs.settingsSheetID = sheetID; } if(title == productsSheetName) { sheetIDs.productsSheetID = sheetID; } }); callback(sheetIDs.settingsSheetID); loadProductData(sheetIDs.productsSheetID); }); } function loadSiteSettings (id, callback) { var settingsSheetURL = hostname + "/feeds/list/" + s.spreadsheetID + "/" + id + "/public/values?alt=" + format; $.getJSON(settingsSheetURL) .done(function(data) { var data = data.feed.entry; var s = simpleStore.settings; if(data[0]) { var siteName = data[0].gsx$sitenametextorimagelink.$t; var columns = data[0].gsx$columns123.$t; if (siteName) { s.brand = siteName; } if (columns) { s.numColumns = columns; } simpleStore.setLayout(s); } }); } function loadProductData (id) { var productsSheetURL = hostname + "/feeds/list/" + s.spreadsheetID + "/" + id + "/public/values?alt=" + format; // Get Main Sheet Products data $.getJSON(productsSheetURL) .done(function(data) { var productsData = data.feed.entry; // Build products $(productsData).each(function(i) { var options = this.gsx$options.$t; var setOptions = function(options) { var productOptions = []; if(options) { var opts = options.split(";").filter(function(el) {return el.length != 0}); $(opts).each(function(i, option) { var opt = option.trim().split(":"), key = opt[0], val = opt[1], optObj = {}; optObj[key] = val; productOptions.push(optObj); }); } return productOptions; }; // Get product values var product = { name : this.gsx$name.$t, price : this.gsx$price.$t, description : this.gsx$description.$t, options : setOptions(options), image : this.gsx$image.$t }; if (verify) { verifyProducts.push(product); } else { storeProducts.push(product); } }); callback(); }) .fail(function(data){ if (verify) { var errorMsg = 'There was an error validating your cart.'; } else { var errorMsg = 'Error loading spreadsheet data. Make sure the spreadsheet ID is correct.'; } setTimeout(function(){ simpleStore.renderError(s, errorMsg); }, 1000); }); } // Get Sheet data getSheetInfo(spreadsheetURL, loadSiteSettings); } function validatePrices(s, checkoutData) { verifyProducts = []; getSpreadsheetData(s, true, function() { if(simpleStore.verifyCheckoutData(checkoutData, verifyProducts, true)) { simpleStore.checkout(s, checkoutData); } else { var errorMsg = 'There was an error validating your cart.'; simpleStore.renderError(s, errorMsg); } }); } return { init: function(callback) { var s = simpleStore.settings; // Clears out brand to allow for spreadsheet site name s.brand = ""; simpleStore.setLayout(s); getSpreadsheetData(s, false, function(){ callback(storeProducts); }); }, validate: function(checkoutData) { validatePrices(simpleStore.settings, checkoutData); } }; })(); ================================================ FILE: products.json ================================================ { "products" : [ { "name" : "Awesome T-shirt", "price" : "35.99", "options" : [ { "Size" : "Small,Medium,Large" } ], "image" : "http://upload.wikimedia.org/wikipedia/commons/c/c6/Grey_Tshirt.jpg", "description" : "This is a very awesome grey T-shirt" }, { "name" : "Cool T-shirt", "price" : "15.99", "options" : [ { "Size" : "Small,Medium,Large" }, { "Color" : "Blue,Red" }, { "OneOfAKind" : true } ], "image" : "http://upload.wikimedia.org/wikipedia/commons/2/24/Blue_Tshirt.jpg", "description" : "This is a really cool blue T-shirt", "soldOut" : true }, { "name" : "Fun T-shirt", "price" : "15.99", "options" : [ { "Size" : "Small,Medium,Large" } ], "image" : "http://upload.wikimedia.org/wikipedia/commons/c/c6/Grey_Tshirt.jpg", "description" : "This is another fun grey T-shirt" } ] }