├── images
└── favicon.png
├── .gitignore
├── plugins
└── google-sheets
│ ├── DemoSpreadsheet.xlsx
│ └── google-sheets.js
├── css
├── imports.min.css
├── simpleStore.min.css
└── simpleStore.css
├── bower.json
├── package.json
├── products.json
├── LICENSE.md
├── Gruntfile.js
├── js
├── config.js
├── simpleStore.min.js
├── simpleStore.js
└── simpleCart.min.js
├── README.md
└── index.html
/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrisdiana/simplestore/HEAD/images/favicon.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/*
3 | bower_components/*
4 | release/*
5 | release.zip
6 | .idea/
7 | dev/*
8 |
--------------------------------------------------------------------------------
/plugins/google-sheets/DemoSpreadsheet.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrisdiana/simplestore/HEAD/plugins/google-sheets/DemoSpreadsheet.xlsx
--------------------------------------------------------------------------------
/css/imports.min.css:
--------------------------------------------------------------------------------
1 | @import '../bower_components/skeleton/css/normalize.css';
2 | @import '../bower_components/skeleton/css/skeleton.css';
3 | @import '../bower_components/fontawesome/css/font-awesome.min.css';
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simpleStore",
3 | "description": "A clean, responsive storefront boilerplate with no database or backend.",
4 | "version": "1.1.1",
5 | "homepage": "http://cdmedia.github.io/simplestore",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/cdmedia/simplestore"
9 | },
10 | "main": "index.html",
11 | "license": "MIT",
12 | "dependencies": {
13 | "jquery": "latest",
14 | "skeleton": "~2.0.4",
15 | "fontawesome": "latest"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simplestore",
3 | "version": "1.5",
4 | "description": "A clean, responsive storefront boilerplate with no database or backend.",
5 | "main": "index.html",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/cdmedia/simplestore.git"
12 | },
13 | "author": "Chris Diana",
14 | "license": "MIT",
15 | "bugs": {
16 | "url": "https://github.com/cdmedia/simplestore/issues"
17 | },
18 | "private": true,
19 | "homepage": "https://github.com/cdmedia/simplestore",
20 | "devDependencies": {
21 | "grunt": "~0.4.4",
22 | "grunt-contrib-copy": "latest",
23 | "grunt-contrib-cssmin": "latest",
24 | "grunt-contrib-uglify": "latest",
25 | "grunt-contrib-watch": "latest",
26 | "grunt-file-append": "0.0.6"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/products.json:
--------------------------------------------------------------------------------
1 | {
2 | "products" : [
3 | {
4 | "name" : "Awesome T-shirt",
5 | "price" : "35.99",
6 | "options" : [
7 | { "Size" : "Small,Medium,Large" }
8 | ],
9 | "image" : "http://upload.wikimedia.org/wikipedia/commons/c/c6/Grey_Tshirt.jpg",
10 | "description" : "This is a very awesome grey T-shirt"
11 | },
12 | {
13 | "name" : "Cool T-shirt",
14 | "price" : "15.99",
15 | "options" : [
16 | { "Size" : "Small,Medium,Large" },
17 | { "Color" : "Blue,Red" },
18 | { "OneOfAKind" : true }
19 | ],
20 | "image" : "http://upload.wikimedia.org/wikipedia/commons/2/24/Blue_Tshirt.jpg",
21 | "description" : "This is a really cool blue T-shirt",
22 | "soldOut" : true
23 | },
24 | {
25 | "name" : "Fun T-shirt",
26 | "price" : "15.99",
27 | "options" : [
28 | { "Size" : "Small,Medium,Large" }
29 | ],
30 | "image" : "http://upload.wikimedia.org/wikipedia/commons/c/c6/Grey_Tshirt.jpg",
31 | "description" : "This is another fun grey T-shirt"
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Chris Diana
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | /*
4 | * To build a release run 'grunt'
5 | * index.html has to be manually copied to 'release/' because of
6 | * reference differences (import.min.css, jquery)
7 | */
8 |
9 | var minBanner = '/* <%= pkg.name %> | Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %> (<%= pkg.homepage %>) | <%= pkg.license %> license | v<%= pkg.version %> */';
10 | var largeBanner = '/*' + '\n' +
11 | '* <%= pkg.name %> v<%= pkg.version %>' + '\n' +
12 | '* Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %>' + '\n' +
13 | '* <%= pkg.homepage %>' + '\n' +
14 | '* Free to use under the MIT license.' + '\n' +
15 | '* http://www.opensource.org/licenses/mit-license.php' + '\n' +
16 | '*/' + '\n';
17 |
18 | grunt.initConfig({
19 |
20 | pkg: grunt.file.readJSON('package.json'),
21 |
22 | uglify: {
23 | options: {
24 | banner: minBanner + '\n'
25 | },
26 | build: {
27 | files: {
28 | 'js/simpleStore.min.js': 'js/simpleStore.js'
29 | }
30 | }
31 | },
32 |
33 | cssmin: {
34 | target: {
35 | files: {
36 | 'css/simpleStore.min.css': 'css/simpleStore.css'
37 | }
38 | }
39 | },
40 |
41 | file_append: {
42 | default_options: {
43 | files: [
44 | {prepend: minBanner + '\n', input: 'css/simpleStore.min.css'}
45 | ]
46 | }
47 | },
48 |
49 | copy: {
50 | main: {
51 | files: [
52 | {src: 'css/simpleStore.min.css', dest: 'release/css/simpleStore.min.css'},
53 | {src: 'js/simpleStore.min.js', dest: 'release/js/simpleStore.min.js'},
54 | {src: 'js/simpleCart.min.js', dest: 'release/js/simpleCart.min.js'},
55 | {src: 'js/config.js', dest: 'release/js/config.js'},
56 | {src: 'products.json', dest: 'release/products.json'},
57 | {src: 'images/favicon.png', dest: 'release/images/favicon.png'}
58 | ]
59 | },
60 | }
61 |
62 | });
63 |
64 | grunt.loadNpmTasks('grunt-contrib-uglify');
65 | grunt.loadNpmTasks('grunt-contrib-cssmin');
66 | grunt.loadNpmTasks('grunt-contrib-copy');
67 | grunt.loadNpmTasks('grunt-file-append');
68 |
69 | grunt.registerTask('default', ['uglify', 'cssmin', 'file_append', 'copy']);
70 | };
71 |
--------------------------------------------------------------------------------
/js/config.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 | simpleCart({
3 |
4 | // array representing the format and columns of the cart, see
5 | // the cart columns documentation
6 | cartColumns: [
7 | { attr: "name" , label: "Name" },
8 | { attr: "price" , label: "Price", view: 'currency' },
9 | { view: "decrement" , label: false },
10 | { attr: "quantity" , label: "Qty" },
11 | { view: "increment" , label: false },
12 | { attr: "total" , label: "SubTotal", view: 'currency' },
13 | { view: "remove" , text: "Remove" , label: false }
14 | ],
15 |
16 | // "div" or "table" - builds the cart as a table or collection of divs
17 | cartStyle: "div",
18 |
19 | // how simpleCart should checkout, see the checkout reference for more info
20 | checkout: {
21 | type: "PayPal" ,
22 | email: "you@yours.com"
23 | },
24 |
25 | // set the currency, see the currency reference for more info
26 | currency: "USD",
27 |
28 | // collection of arbitrary data you may want to store with the cart,
29 | // such as customer info
30 | data: {},
31 |
32 | // set the cart langauge (may be used for checkout)
33 | language: "english-us",
34 |
35 | // array of item fields that will not be sent to checkout
36 | excludeFromCheckout: [
37 | 'qty',
38 | 'thumb'
39 | ],
40 |
41 | // custom function to add shipping cost
42 | shippingCustom: null,
43 |
44 | // flat rate shipping option
45 | shippingFlatRate: 0,
46 |
47 | // added shipping based on this value multiplied by the cart quantity
48 | shippingQuantityRate: 0,
49 |
50 | // added shipping based on this value multiplied by the cart subtotal
51 | shippingTotalRate: 0,
52 |
53 | // tax rate applied to cart subtotal
54 | taxRate: 0,
55 |
56 | // true if tax should be applied to shipping
57 | taxShipping: false,
58 |
59 | // event callbacks
60 | beforeAdd : null,
61 | afterAdd : null,
62 | load : null,
63 | beforeSave : null,
64 | afterSave : null,
65 | update : null,
66 | ready : null,
67 | checkoutSuccess : null,
68 | checkoutFail : null,
69 | beforeCheckout : null
70 |
71 | });
72 |
73 | simpleStore.init({
74 |
75 | // brand can be text or image URL
76 | brand : "SimpleStore",
77 |
78 | // numder of products per row (accepts 1, 2 or 3)
79 | numColumns : 3,
80 |
81 | // name of JSON file, located in directory root
82 | JSONFile : "products.json"
83 |
84 | });
85 |
86 | });
87 |
--------------------------------------------------------------------------------
/css/simpleStore.min.css:
--------------------------------------------------------------------------------
1 | /* simplestore | Copyright 2015 Chris Diana (https://github.com/cdmedia/simplestore) | MIT license | v1.5 */
2 | .brand,select{display:block;float:left}.brand,a.hover,a.simpleCart_empty{text-decoration:none}select{margin-right:10px}.simpleStore{margin-top:20px}.brand{color:#000}.brand:hover{color:#777}.brand img{max-height:50px}.simpleCart_shelfItem{border:1px solid #EFEFEF;padding:25px 18px 10px;margin:15px 0}.headerRow,.itemRow{border-bottom:1px solid #E1E1E1}.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}.cart_total,.item_price{font-size:20px}.simpleCart_shelfItem label{display:block;float:left;line-height:40px;margin-right:10px}.detail_thumb,.item_thumb{height:150px;display:block;margin:0 auto;padding-bottom:10px}.item_Quantity{display:inline-block;width:50px}.item_price{font-weight:700;line-height:22px}.item_description{margin-top:1rem;margin-bottom:1.5rem}.headerRow{display:table-row;font-weight:700}.headerRow div{display:table-cell;padding:10px}.itemRow.odd{background:#FAFAFA}.itemRow{display:table-row}.itemRow:last-child{border-bottom:0 solid #fff}.itemRow div{display:table-cell;padding:10px}.itemRow .item-quantity{text-align:center}.cart_info{position:absolute;bottom:0;right:0;margin:22px;text-align:right}.simpleStore_cart,.simpleStore_detailView{position:relative;border:1px solid #EFEFEF}.loader,.notify{text-align:center}.cart_info div{display:inline}.cart_info_item{display:block!important}.simpleStore_getDetail_container{display:block;float:left;width:50%;padding:5px 0}.simpleStore_detailView{margin:15px 0}.simpleStore_detailView.simpleCart_shelfItem{padding:26px 18px 8px}.simpleStore_detailView .item_add{margin:0}.simpleStore_detailView .item_name{margin-bottom:1rem}.simpleStore_detailView .item_price{display:block;margin-bottom:1.5rem}.hidden,.simpleStore_cart_container{display:none}.simpleStore_cart{padding:22px;min-height:230px;margin:15px 0}a.simpleCart_empty{color:#CDCDCD;text-transform:uppercase;font-size:14px}a.simpleCart_empty:hover{color:#777}.simpleCart_checkout{margin:12px 0 0}.loader{display:inline-block;font-size:60px;line-height:50px;color:#ddd;margin:120px auto;width:100%;height:50px;vertical-align:bottom}a.back,a.close{position:absolute;margin:0;line-height:0;padding:0;text-decoration:none}a.back{top:10px;left:18px;font-size:34px;color:#CDCDCD}a.back:hover{color:#777}a.close{top:18px;right:14px;font-size:32px;color:#CDCDCD;font-family:Garamond,"Apple Garamond"}a.close:hover{color:#777}.chevron::before{border-style:solid;border-width:.1em .1em 0 0;content:'';display:inline-block;height:.45em;position:relative;top:.15em;vertical-align:top;width:.45em;left:.25em;transform:rotate(-135deg)}.error{background:#FFE5E5;padding:15px;border-radius:5px;position:relative;margin-bottom:10px}p.error_text{margin-bottom:0}.notify{background:#D9F6FF;background:rgba(81,81,81,.8);color:#fff;position:fixed;padding:14px 0;margin:0 auto;width:100%;font-size:14px;right:0;top:0;z-index:9999}p.notify_text{margin-bottom:0}@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:0}}@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:0;padding-top:15px;text-align:left;border-top:1px solid #E1E1E1}.simpleCart_checkout{width:100%}.simpleCart_items{font-size:12px}.item-decrement,.item-increment,.item-total{display:none!important}}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **UPDATES COMING SOON! [Get notified](http://eepurl.com/dHBK11)**
2 |
3 |
4 | [Sign up](http://eepurl.com/gntUvf) to get updates on new features and releases
5 |
6 | # simpleStore
7 |
8 | [simpleStore](http://chrisdiana.github.io/simplestore) is a clean, responsive
9 | storefront boilerplate with no database you can setup in minutes. simpleStore is built on
10 | [simpleCart.js](http://simplecartjs.org) and [Skeleton](http://getskeleton.com)
11 | CSS Framework for a lightweight, fast, simple to use, and completely
12 | customizable experience.
13 |
14 | 
15 | 
16 | 
17 |
18 | ---
19 |
20 | # Features
21 |
22 | * No Databases, all client-side (just simple HTML, CSS & Javascript)
23 | * Lightweight & Fast
24 | * Tax Rate Calculations
25 | * Unlimited product attributes
26 | * Shipping
27 | * Multiple Currencies
28 | * Payment Gateways (Paypal, Google Checkout, Amazon Payments)
29 | * For more features check out [simpleCart.js](http://simplecartjs.org)
30 |
31 | # Plugins
32 |
33 | * Google Sheets (Control products from a Google Sheet instead of JSON file)
34 |
35 | # Demo
36 |
37 | You can see a working demo [here](http://chrisdiana.github.io/simplestore/demo/)
38 |
39 |
40 | # Installation
41 |
42 | Install with Bower
43 |
44 | ```
45 | bower install
46 | ```
47 |
48 | or manually install using the latest [release](https://github.com/chrisdiana/simplestore/releases/latest)
49 |
50 |
51 | # Setup
52 |
53 | 1.Make sure simpleStore is on a web server (any type will do as long as it can serve static web pages).
54 |
55 | 2.Configure your payment options in `js/config.js`.
56 |
57 | ```
58 | checkout: {
59 | type: "PayPal" ,
60 | email: "you@yours.com"
61 | },
62 | ```
63 |
64 | 3.Edit the `js/config.js` to your liking.
65 |
66 | 4.Add additional products in the `products.json` file.
67 |
68 | # Using Plugins
69 |
70 | To use a plugin, add a reference just before your `config.js` file
71 |
72 | ```
73 |
74 |
75 | ```
76 |
77 | ## HTML Version
78 |
79 | If you are looking for something more basic, check out the [HTML version on this
80 | branch](https://github.com/chrisdiana/simplestore/tree/simplestore-html).
81 | The HTML version uses plain HTML to build the store instead of a JSON
82 | file.
83 |
84 | Add additional products using the `
` tags.
85 |
86 | ## Credit where credit is due
87 |
88 | For further documentation on expanding/tweaking simpleStore, check out the
89 | framework/plugin pages.
90 |
91 | * [Skeleton](http://getskeleton.com)
92 | * [simpleCart.js](http://simplecartjs.org)
93 | * [Normalize.css](http://necolas.github.io/normalize.css)
94 | * [FontAwesome](http://fortawesome.github.io/Font-Awesome)
95 | * [jQuery](https://jquery.com/)
96 |
97 | ### A note about JavaScript shopping carts
98 |
99 | ALL JavaScript shopping carts are NOT fullproof. Because simpleStore is fully
100 | client-side, some users may attempt to alter prices before checkout.
101 | SimpleStore does the best it can to minimize this
102 | kind of activity. Make sure to monitor your sales. Just like in real life, if someone
103 | walks into your store and changes the price tag, you will certainly not honor
104 | those changes.
105 |
106 |
107 | # Contributing
108 |
109 | All forms of contribution are welcome: bug reports, bug fixes, pull requests and simple suggestions.
110 | If you do wish to contribute, please follow the [Airbnb Javascript Style Guide](https://github.com/airbnb/javascript) Thanks!
111 |
112 |
113 | ## List of contributors
114 |
115 | You can find the list of contributors [here](https://github.com/chrisdiana/simplestore/graphs/contributors).
116 |
--------------------------------------------------------------------------------
/plugins/google-sheets/google-sheets.js:
--------------------------------------------------------------------------------
1 | /*
2 | * SimpleStore Google Sheets Plugin
3 | * To use Google spreadsheet as your database, follow the steps below:
4 | * 1. Use the "DemoSpreadsheet.xlsx" as a starting point
5 | * 2. Create a new Google spreadsheet
6 | * 3. Set sharing permissions to either “Public” or set to “Anyone with link can view”
7 | * 4. Publish the sheet (File -> Publish to the web -> Publish)
8 | * 5. Add the spreadsheet ID to your 'config.js' ( spreadsheetID : "XXXXXXXXXXXXXXXXXXXXXXX" )
9 | */
10 |
11 | simpleStore.plugins.google = (function() {
12 |
13 | var storeProducts = verifyProducts = [];
14 |
15 | function getSpreadsheetData(s, verify, callback) {
16 |
17 | verify = typeof verify !== 'undefined' ? verify : false;
18 |
19 | var hostname = "https://spreadsheets.google.com";
20 | var format = "json";
21 | var spreadsheetURL = hostname + "/feeds/worksheets/" + s.spreadsheetID + "/public/full?alt=" + format;
22 | var mainsheetURL = hostname + "/feeds/list/" + s.spreadsheetID + "/od6/public/values?alt=" + format;
23 | var settingsSheetName = "Settings";
24 | var productsSheetName = "Products";
25 | var sheetIDs = {};
26 |
27 | function getSheetInfo (url, callback) {
28 | // Need to do this because od6 is default Google Sheet ID
29 | $.getJSON(url)
30 | .done(function(data) {
31 |
32 | var sheets = data.feed.entry;
33 |
34 | $(sheets).each(function(i, sheet) {
35 |
36 | var title = sheet.title.$t;
37 | var id = sheet.id.$t;
38 | var sheetID = id.substr(id.lastIndexOf('/') + 1);
39 |
40 | if(title == settingsSheetName) {
41 | sheetIDs.settingsSheetID = sheetID;
42 | }
43 | if(title == productsSheetName) {
44 | sheetIDs.productsSheetID = sheetID;
45 | }
46 | });
47 | callback(sheetIDs.settingsSheetID);
48 | loadProductData(sheetIDs.productsSheetID);
49 | });
50 | }
51 |
52 | function loadSiteSettings (id, callback) {
53 |
54 | var settingsSheetURL = hostname + "/feeds/list/" + s.spreadsheetID + "/" + id + "/public/values?alt=" + format;
55 |
56 | $.getJSON(settingsSheetURL)
57 | .done(function(data) {
58 | var data = data.feed.entry;
59 | var s = simpleStore.settings;
60 |
61 | if(data[0]) {
62 |
63 | var siteName = data[0].gsx$sitenametextorimagelink.$t;
64 | var columns = data[0].gsx$columns123.$t;
65 |
66 | if (siteName) {
67 | s.brand = siteName;
68 | }
69 | if (columns) {
70 | s.numColumns = columns;
71 | }
72 |
73 | simpleStore.setLayout(s);
74 | }
75 | });
76 | }
77 |
78 | function loadProductData (id) {
79 |
80 | var productsSheetURL = hostname + "/feeds/list/" + s.spreadsheetID + "/" + id + "/public/values?alt=" + format;
81 |
82 | // Get Main Sheet Products data
83 | $.getJSON(productsSheetURL)
84 | .done(function(data) {
85 |
86 | var productsData = data.feed.entry;
87 |
88 | // Build products
89 | $(productsData).each(function(i) {
90 |
91 | var options = this.gsx$options.$t;
92 | var setOptions = function(options) {
93 | var productOptions = [];
94 | if(options) {
95 | var opts = options.split(";").filter(function(el) {return el.length != 0});
96 | $(opts).each(function(i, option) {
97 | var opt = option.trim().split(":"),
98 | key = opt[0],
99 | val = opt[1],
100 | optObj = {};
101 |
102 | optObj[key] = val;
103 | productOptions.push(optObj);
104 | });
105 | }
106 | return productOptions;
107 | };
108 |
109 | // Get product values
110 | var product = {
111 | name : this.gsx$name.$t,
112 | price : this.gsx$price.$t,
113 | description : this.gsx$description.$t,
114 | options : setOptions(options),
115 | image : this.gsx$image.$t
116 | };
117 |
118 | if (verify) {
119 | verifyProducts.push(product);
120 | } else {
121 | storeProducts.push(product);
122 | }
123 | });
124 | callback();
125 | })
126 | .fail(function(data){
127 | if (verify) {
128 | var errorMsg = 'There was an error validating your cart.';
129 | } else {
130 | var errorMsg = 'Error loading spreadsheet data. Make sure the spreadsheet ID is correct.';
131 | }
132 | setTimeout(function(){ simpleStore.renderError(s, errorMsg); }, 1000);
133 | });
134 | }
135 |
136 | // Get Sheet data
137 | getSheetInfo(spreadsheetURL, loadSiteSettings);
138 | }
139 |
140 | function validatePrices(s, checkoutData) {
141 | verifyProducts = [];
142 |
143 | getSpreadsheetData(s, true, function() {
144 | if(simpleStore.verifyCheckoutData(checkoutData, verifyProducts, true)) {
145 | simpleStore.checkout(s, checkoutData);
146 | } else {
147 | var errorMsg = 'There was an error validating your cart.';
148 | simpleStore.renderError(s, errorMsg);
149 | }
150 | });
151 | }
152 |
153 | return {
154 | init: function(callback) {
155 | var s = simpleStore.settings;
156 |
157 | // Clears out brand to allow for spreadsheet site name
158 | s.brand = "";
159 | simpleStore.setLayout(s);
160 |
161 | getSpreadsheetData(s, false, function(){
162 | callback(storeProducts);
163 | });
164 | },
165 | validate: function(checkoutData) {
166 | validatePrices(simpleStore.settings, checkoutData);
167 | }
168 | };
169 | })();
170 |
--------------------------------------------------------------------------------
/js/simpleStore.min.js:
--------------------------------------------------------------------------------
1 | /* simplestore | Copyright 2015 Chris Diana (https://github.com/cdmedia/simplestore) | MIT license | v1.5 */
2 | var simpleStore={products:[],plugins:{},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(a,b,c){var d;"undefined"==typeof b&&(b=a,a=simpleStore);for(d in b)Object.prototype.hasOwnProperty.call(b,d)&&(a[d]=b[d]);return c(),a},render:function(a,b){var c=a.split("/")[0],d={"":function(){simpleStore.renderProducts(simpleStore.products,b)},"#product":function(){var c=a.split("#product/")[1].trim();simpleStore.renderSingleProduct(c,b)},"#cart":function(){simpleStore.renderCart(b)}};d[c]?d[c]():simpleStore.renderError(b)},insertData:function(a,b){a.find(".item_thumb").attr("src",b.image),a.find(".item_name").text(b.name),a.find(".item_price").text(b.price),a.find(".item_description").text(b.description)},renderProducts:function(a,b){var c,d=1,e=(a.length,Math.ceil(a.length/b.numColumns));b.cartContainer.hide(),b.container.fadeOut(b.fadeSpeed,function(){b.container.html("").fadeIn(b.fadeSpeed);for(var f=0;e>f;f++)b.container.append('');var g=b.columnWidthClasses;for(var h in g)h==b.numColumns&&(c=g[h]);a.forEach(function(a,e){if(!a.soldOut){var f=$("#products-template").html(),g=$(f);g.first().addClass(c),simpleStore.insertData(g,a);var h=g.find(".simpleStore_getDetail");h.on("click",function(b){b.preventDefault(),window.location.hash="product/"+a.id}),0===e&&(e=1),e%b.numColumns===0&&d++,$("."+b.rowClass+d).append(g)}})})},renderProductOptions:function(a,b){var c="";return a.forEach(function(b){if(simpleStore.productPageOptions in b)simpleStore.renderProductPageOptions(b);else{var d="",e=Object.keys(b)[0].trim(),f=b[e].trim().split(",");$(f).each(function(a,b){d+='"}),a.length&&(c+="")}}),c},renderProductPageOptions:function(a){a.OneOfAKind&&$(".qty").hide()},renderSingleProduct:function(a,b){b.container.fadeOut(b.fadeSpeed,function(){var c=$("#product-detail-template").html(),d=$(c);simpleStore.products.forEach(function(c){if(c.id==a){if(simpleStore.insertData(d,c),b.container.html(d),c.options.length){var e=simpleStore.renderProductOptions(c.options,b);$(".simpleStore_options").append(e)}b.container.fadeIn(b.fadeSpeed)}})})},renderCart:function(a){a.container.fadeOut(a.fadeSpeed,function(){a.cartContainer.fadeIn(a.fadeSpeed)})},renderError:function(a,b){var c=$("#error-template").html(),d=$(c);a.container.html("").fadeIn(a.fadeSpeed),b.length&&d.find(".error_text").text(b),a.container.append(d),a.container.fadeIn(a.fadeSpeed),d.find(".alert_close").on("click",function(b){b.preventDefault(),d.fadeOut(a.fadeSpeed,function(){d.remove()})})},handleFailure:function(a,b){setTimeout(function(){simpleStore.renderError(a,b)},1e3)},notifier:function(a){s=this.settings;var b=$("#notify-template").html(),c=$(b);a.length&&(c.find(".notify_text").text(a),s.container.append(c),c.hide(),c.fadeIn(s.fadeSpeed),setTimeout(function(){c.fadeOut(s.fadeSpeed)},1e3))},initJSON:function(a){var b='There was an error loading the JSON file. Please make sure you have "'+a.JSONFile+'" file in your main directory.';$.get(a.JSONFile).success(function(){$.getJSON(a.JSONFile,function(a){simpleStore.setProducts(a.products)}).fail(function(){simpleStore.handleFailure(a,b)})}).fail(function(){simpleStore.handleFailure(a,b)})},checkMode:function(a){(a.hasOwnProperty("spreadsheetID")||a.hasOwnProperty("spreadsheetId"))&&(a.mode="Google")},checkout:function(a,b){$.isEmptyObject(b)||(simpleCart.checkout(),a.cartContainer.fadeOut(a.fadeSpeed,function(){a.container.html(''),a.container.fadeIn(a.fadeSpeed)}))},verifyCheckoutData:function(a,b,c){for(var d in a)if(a.hasOwnProperty(d))for(var e=a[d],f=e.name,g=e.price,h=0;h0&&a.forEach(function(a,b){a.id=b+1,simpleStore.products.push(a)}),$(window).trigger("hashchange")},setLayout:function(a){a.brand.match("^http://")||a.brand.match("^https://")||a.brand.match("^www.")?$(".brand").html('
'):$(".brand").html(""+a.brand+"
"),$("title").html(a.brand)},generateCart:function(a){var b=$("#cart-template").html(),c=$(b);a.cartContainer.html(c)},generateStore:function(){var a=this.settings;switch(this.checkMode(a),$(window).on("hashchange",function(){simpleStore.render(window.location.hash,a)}),a.mode){case"JSON":this.initJSON(a);break;case"Google":if(simpleStore.plugins.google)simpleStore.plugins.google.init(function(b){simpleStore.setProducts(b,a)});else{var b="There was an error loading the Google plugin. Make sure it is installed properly.";simpleStore.renderError(a,b)}break;default:this.initJSON(a)}this.generateCart(a),this.setLayout(a),$(".simpleStore_checkout").on("click",function(b){b.preventDefault(),simpleStore.validatePrices(a)}),$(".simpleStore_viewCart").on("click",function(a){a.preventDefault(),window.location="#cart"}),$(".view_close").on("click",function(a){a.preventDefault(),window.location.hash=""}),simpleCart({afterAdd:function(){simpleStore.notifier("Item added to cart")}})},init:function(a){return $.isPlainObject(a)?this.extend(this.settings,a,function(){simpleStore.generateStore()}):void 0}};
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | simpleStore
9 |
10 |
11 |
12 |
14 |
15 |
16 |
18 |
19 |
20 |
22 |
23 |
24 |
25 |
27 |
28 |
29 |
30 |
31 |
32 |
34 |
44 |
45 |
47 |
48 |
49 |
66 |
67 |
68 |
92 |
93 |
94 |
127 |
128 |
129 |
136 |
137 |
138 |
143 |
144 |
146 |
147 |
148 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/css/simpleStore.css:
--------------------------------------------------------------------------------
1 | /*
2 | * simplestore
3 | * Copyright 2015 Chris Diana
4 | * https://github.com/cdmedia/simplestore
5 | * Free to use under the MIT license.
6 | * http://www.opensource.org/licenses/mit-license.php
7 | */
8 |
9 | select {
10 | display: block;
11 | float: left;
12 | margin-right: 10px;
13 | }
14 |
15 | a.hover {
16 | text-decoration: none;
17 | }
18 |
19 | /* Main Layout */
20 |
21 | .simpleStore {
22 | margin-top: 20px;
23 | }
24 |
25 | .brand {
26 | display: block;
27 | float: left;
28 | text-decoration: none;
29 | color: #000;
30 | }
31 |
32 | .brand:hover {
33 | color: #777;
34 | }
35 |
36 | .brand img {
37 | max-height: 50px;
38 | }
39 |
40 | /* simpleCart */
41 |
42 | .simpleCart_shelfItem {
43 | border: 1px solid #EFEFEF;
44 | padding: 25px 18px 10px 18px;
45 | margin: 15px 0;
46 | }
47 |
48 | .simpleCart_items {
49 | display: table;
50 | border-collapse: collapse;
51 | width: 100%;
52 | min-height: 20px;
53 | margin-bottom: 50px;
54 | }
55 |
56 | .simpleCart_items div:first-child {
57 | width: 100%
58 | }
59 |
60 | .simpleCart_decrement, .simpleCart_increment {
61 | font-size: 18px;
62 | line-height: 5px;
63 | text-decoration: none;
64 | }
65 |
66 | .simpleCart_shelfItem label {
67 | display: block;
68 | float: left;
69 | line-height: 40px;
70 | margin-right: 10px;
71 | }
72 |
73 | .item_thumb, .detail_thumb {
74 | height: 150px;
75 | display: block;
76 | margin: 0 auto;
77 | padding-bottom: 10px;
78 | }
79 |
80 | .item_Quantity {
81 | display: inline-block;
82 | width: 50px;
83 | }
84 |
85 | .item_price {
86 | font-weight: 700;
87 | font-size: 20px;
88 | line-height: 22px;
89 | }
90 |
91 | .item_description {
92 | margin-top: 1rem;
93 | margin-bottom: 1.5rem;
94 | }
95 |
96 | .headerRow {
97 | display: table-row;
98 | font-weight: 700;
99 | border-bottom: 1px solid #E1E1E1;
100 | }
101 |
102 | .headerRow div {
103 | display: table-cell;
104 | padding: 10px;
105 | }
106 |
107 | .itemRow.odd {
108 | background: #FAFAFA;
109 | }
110 |
111 | .itemRow {
112 | display: table-row;
113 | border-bottom: 1px solid #E1E1E1;
114 | }
115 |
116 | .itemRow:last-child {
117 | border-bottom: 0 solid #fff;
118 | }
119 |
120 | .itemRow div {
121 | display: table-cell;
122 | padding: 10px;
123 | }
124 |
125 | .itemRow .item-quantity {
126 | text-align: center;
127 | }
128 |
129 | /* Cart */
130 |
131 | .cart_info {
132 | position: absolute;
133 | bottom: 0px;
134 | right: 0px;
135 | margin: 22px;
136 | text-align: right;
137 | }
138 |
139 | .cart_info div {
140 | display: inline;
141 | }
142 |
143 | .cart_info_item {
144 | display: block !important;
145 | }
146 |
147 | .cart_total {
148 | font-size: 20px;
149 | }
150 |
151 | /* Views */
152 |
153 | .simpleStore_getDetail_container {
154 | display: block;
155 | float: left;
156 | width: 50%;
157 | padding: 5px 0px;
158 | }
159 |
160 | .simpleStore_detailView {
161 | margin: 15px 0px;
162 | border: 1px solid #EFEFEF;
163 | position: relative;
164 | }
165 |
166 | .simpleStore_detailView.simpleCart_shelfItem {
167 | padding: 26px 18px 8px 18px;
168 | }
169 |
170 | .simpleStore_detailView .item_add {
171 | margin: 0px;
172 | margin-bottom: 0px;
173 | }
174 |
175 | .simpleStore_detailView .item_name {
176 | margin-bottom: 1rem;
177 | }
178 |
179 | .simpleStore_detailView .item_price {
180 | display: block;
181 | margin-bottom: 1.5rem;
182 | }
183 |
184 | .simpleStore_cart_container {
185 | display: none;
186 | }
187 |
188 | .simpleStore_cart {
189 | position: relative;
190 | border: 1px solid #EFEFEF;
191 | padding: 22px;
192 | min-height: 230px;
193 | margin: 15px 0px;
194 | }
195 |
196 | a.simpleCart_empty {
197 | color: #CDCDCD;
198 | text-decoration: none;
199 | text-transform: uppercase;
200 | font-size: 14px;
201 | }
202 |
203 | a.simpleCart_empty:hover {
204 | color: #777;
205 | }
206 |
207 | .simpleCart_checkout {
208 | margin: 12px 0px 0px 0px;
209 | }
210 |
211 |
212 | /* Utility */
213 |
214 | .hidden {
215 | display: none;
216 | }
217 |
218 | .loader {
219 | display: inline-block;
220 | font-size: 60px;
221 | line-height: 50px;
222 | color: #ddd;
223 | margin: 120px auto;
224 | width: 100%;
225 | height: 50px;
226 | text-align: center;
227 | vertical-align: bottom;
228 | }
229 |
230 | a.back {
231 | position: absolute;
232 | top: 10px;
233 | left: 18px;
234 | line-height: 0px;
235 | margin: 0;
236 | padding: 0;
237 | font-size: 34px;
238 | color: #CDCDCD;
239 | text-decoration: none;
240 | }
241 |
242 | a.back:hover {
243 | color: #777;
244 | }
245 |
246 | a.close {
247 | position: absolute;
248 | top: 18px;
249 | right: 14px;
250 | margin: 0;
251 | line-height: 0px;
252 | padding: 0;
253 | font-size: 32px;
254 | color: #CDCDCD;
255 | text-decoration: none;
256 | font-family: Garamond, "Apple Garamond";
257 | }
258 |
259 | a.close:hover {
260 | color: #777;
261 | }
262 |
263 | .chevron::before {
264 | border-style: solid;
265 | border-width: 0.1em 0.1em 0 0;
266 | content: '';
267 | display: inline-block;
268 | height: 0.45em;
269 | position: relative;
270 | top: 0.15em;
271 | transform: rotate(-45deg);
272 | vertical-align: top;
273 | width: 0.45em;
274 | left: 0.25em;
275 | transform: rotate(-135deg);
276 | }
277 |
278 | .error {
279 | background: #FFE5E5;
280 | padding: 15px;
281 | border-radius: 5px;
282 | position: relative;
283 | margin-bottom: 10px;
284 | }
285 | p.error_text {
286 | margin-bottom: 0rem;
287 | }
288 |
289 | .notify {
290 | background: #D9F6FF;
291 | background: rgba(81, 81, 81, 0.8);
292 | color: #fff;
293 | position: fixed;
294 | padding: 14px 0;
295 | text-align: center;
296 | margin: 0 auto;
297 | width: 100%;
298 | font-size: 14px;
299 | top: 0;
300 | right: 0;
301 | top: 0;
302 | z-index: 9999;
303 | }
304 | p.notify_text {
305 | margin-bottom: 0rem;
306 | }
307 |
308 | @media (max-width: 920px) {
309 | .simpleStore_getDetail_container {
310 | width: 100%;
311 | }
312 | .simpleCart_shelfItem {
313 | position: relative;
314 | }
315 | .simpleCart_shelfItem .item_name {
316 | font-size: 1.8rem;
317 | }
318 | .simpleStore_getDetail {
319 | position: inherit;
320 | display: block;
321 | width: 100%;
322 | margin: 0px;
323 | margin-bottom: 0px;
324 | }
325 | }
326 |
327 | @media (max-width: 620px) {
328 |
329 | .simpleStore_detailView .item_thumb {
330 | margin-bottom: 10px;
331 | display: block;
332 | width: 100%;
333 | height: auto;
334 | }
335 |
336 | .simpleStore_detailView .item_add {
337 | position: inherit;
338 | display: block;
339 | width: 100%;
340 | }
341 |
342 | .cart_info {
343 | position: inherit;
344 | margin: 0px;
345 | padding-top: 15px;
346 | text-align: left;
347 | border-top: 1px solid #E1E1E1;
348 | }
349 |
350 | .simpleCart_checkout {
351 | width: 100%;
352 | }
353 |
354 | .simpleCart_items {
355 | font-size: 12px;
356 | }
357 |
358 | /* Better mobile support for cart */
359 | .item-decrement, .item-increment, .item-total {
360 | display: none !important;
361 | }
362 | }
363 |
--------------------------------------------------------------------------------
/js/simpleStore.js:
--------------------------------------------------------------------------------
1 | /*
2 | * simplestore
3 | * Copyright 2015 Chris Diana
4 | * https://github.com/cdmedia/simplestore
5 | * Free to use under the MIT license.
6 | * http://www.opensource.org/licenses/mit-license.php
7 | */
8 |
9 | var simpleStore = {
10 |
11 | products: [],
12 | plugins: {},
13 |
14 | // Default settings
15 | settings: {
16 | numColumns: 3,
17 | brand: "SimpleStore",
18 | mode: "JSON",
19 | JSONFile: "products.json",
20 | fadeSpeed: 200,
21 | buttonColor: null,
22 | backgroundColor: null,
23 | textColor: null,
24 | container: $('.simpleStore_container'),
25 | cartContainer: $('.simpleStore_cart_container'),
26 | rowClass: 'simpleStore_row_',
27 | columnWidthClasses: {
28 | 1: "",
29 | 2: "one-half",
30 | 3: "one-third"
31 | }
32 | },
33 |
34 | productPageOptions: [
35 | 'OneOfAKind'
36 | ],
37 |
38 | extend: function (target, opts, callback) {
39 | var next;
40 | if (typeof opts === "undefined") {
41 | opts = target;
42 | target = simpleStore;
43 | }
44 | for (next in opts) {
45 | if (Object.prototype.hasOwnProperty.call(opts, next)) {
46 | target[next] = opts[next];
47 | }
48 | }
49 | callback(); // check user config options
50 | return target;
51 | },
52 |
53 | render: function (url, s) {
54 | var type = url.split('/')[0];
55 |
56 | var map = {
57 | // Main view
58 | '': function () {
59 | simpleStore.renderProducts(simpleStore.products, s);
60 | },
61 | // Detail view
62 | '#product': function () {
63 | var id = url.split('#product/')[1].trim();
64 | simpleStore.renderSingleProduct(id, s);
65 | },
66 | // Cart view
67 | '#cart': function () {
68 | simpleStore.renderCart(s);
69 | }
70 | };
71 |
72 | if (map[type]) {
73 | map[type]();
74 | } else {
75 | simpleStore.renderError(s);
76 | }
77 | },
78 |
79 | insertData: function (tmpl, product) {
80 | tmpl.find('.item_thumb').attr("src", product.image);
81 | tmpl.find('.item_name').text(product.name);
82 | tmpl.find('.item_price').text(product.price);
83 | tmpl.find('.item_description').text(product.description);
84 | },
85 |
86 | renderProducts: function (products, s) {
87 |
88 | var rowCount = 1,
89 | numProducts = products.length,
90 | numRows = Math.ceil(products.length / s.numColumns),
91 | itemWidth;
92 |
93 | s.cartContainer.hide();
94 | s.container.fadeOut(s.fadeSpeed, function () {
95 |
96 | // Empty out main container on load
97 | s.container.html('').fadeIn(s.fadeSpeed);
98 |
99 | // Build rows based on number of products
100 | for (var r = 0; r < numRows; r++) {
101 | s.container.append('');
102 | }
103 |
104 | // Get item column width
105 | var widthClasses = s.columnWidthClasses;
106 | for (var k in widthClasses) {
107 | if (k == s.numColumns) {
108 | itemWidth = widthClasses[k];
109 | }
110 | }
111 |
112 | // List layout
113 | products.forEach(function (product, i) {
114 |
115 | if (!product.soldOut) {
116 | var tmpl = $('#products-template').html(),
117 | $tmpl = $(tmpl);
118 |
119 | // Set item width
120 | $tmpl.first().addClass(itemWidth);
121 |
122 | // Insert data into template
123 | simpleStore.insertData($tmpl, product);
124 |
125 | // Render detail view on hash change
126 | var getDetail = $tmpl.find('.simpleStore_getDetail');
127 | getDetail.on('click', function (e) {
128 | e.preventDefault();
129 | window.location.hash = 'product/' + product.id;
130 | });
131 |
132 | // Check where to add new item based on row
133 | if (i === 0) {
134 | i = 1;
135 | }
136 | if (i % (s.numColumns) === 0) {
137 | rowCount++;
138 | }
139 |
140 | // Append to appropriate container
141 | $('.' + s.rowClass + rowCount).append($tmpl);
142 | }
143 | });
144 | });
145 | },
146 |
147 | renderProductOptions: function (options, s) {
148 |
149 | var optionsLayout = '';
150 |
151 | options.forEach(function (option) {
152 | if (!(simpleStore.productPageOptions in option)) {
153 | var selectItems = '';
154 | var attributeLabel = Object.keys(option)[0].trim();
155 | var attributeValues = option[attributeLabel].trim().split(",");
156 |
157 | // Set attribute values
158 | $(attributeValues).each(function (attribute, attributeValue) {
159 | selectItems += '';
160 | });
161 |
162 | // Build options layout
163 | if (options.length) {
164 | optionsLayout += '';
165 | }
166 | } else {
167 | simpleStore.renderProductPageOptions(option);
168 | }
169 | });
170 |
171 | return optionsLayout;
172 | },
173 |
174 | renderProductPageOptions: function (option) {
175 | if (option.OneOfAKind) {
176 | $('.qty').hide();
177 | }
178 | },
179 |
180 | renderSingleProduct: function (id, s) {
181 |
182 | s.container.fadeOut(s.fadeSpeed, function () {
183 |
184 | var tmpl = $('#product-detail-template').html(),
185 | $tmpl = $(tmpl);
186 |
187 | simpleStore.products.forEach(function (product) {
188 | if (product.id == id) {
189 |
190 | // Insert data into template
191 | simpleStore.insertData($tmpl, product);
192 |
193 | // Load detail view into main container
194 | s.container.html($tmpl);
195 |
196 | // Render product options
197 | if (product.options.length) {
198 | var options = simpleStore.renderProductOptions(product.options, s);
199 | $('.simpleStore_options').append(options);
200 | }
201 | s.container.fadeIn(s.fadeSpeed);
202 | }
203 | });
204 | });
205 | },
206 |
207 | renderCart: function (s) {
208 | s.container.fadeOut(s.fadeSpeed, function () {
209 | s.cartContainer.fadeIn(s.fadeSpeed);
210 | });
211 | },
212 |
213 | renderError: function (s, msg) {
214 | var tmpl = $('#error-template').html(),
215 | $tmpl = $(tmpl);
216 |
217 | // Empty out main container on load
218 | s.container.html('').fadeIn(s.fadeSpeed);
219 |
220 | if (msg.length) {
221 | $tmpl.find('.error_text').text(msg);
222 | }
223 | s.container.append($tmpl);
224 | s.container.fadeIn(s.fadeSpeed);
225 |
226 | $tmpl.find('.alert_close').on('click', function (e) {
227 | e.preventDefault();
228 | $tmpl.fadeOut(s.fadeSpeed, function() {
229 | $tmpl.remove();
230 | });
231 | });
232 | },
233 |
234 | handleFailure: function(s, errorMsg) {
235 | setTimeout(function () {
236 | simpleStore.renderError(s, errorMsg);
237 | }, 1000);
238 | },
239 |
240 | notifier: function(msg) {
241 | s = this.settings;
242 |
243 | var tmpl = $('#notify-template').html(),
244 | $tmpl = $(tmpl);
245 |
246 | if (msg.length) {
247 | $tmpl.find('.notify_text').text(msg);
248 | s.container.append($tmpl);
249 | $tmpl.hide();
250 | $tmpl.fadeIn(s.fadeSpeed);
251 | setTimeout(function () {
252 | $tmpl.fadeOut(s.fadeSpeed);
253 | }, 1000);
254 | }
255 | },
256 |
257 | initJSON: function (s) {
258 | var errorMsg = 'There was an error loading the JSON file.' +
259 | ' Please make sure you have "' + s.JSONFile + '" file in' +
260 | ' your main directory.';
261 |
262 | // Checks to make sure file exists
263 | $.get(s.JSONFile)
264 | .success(function () {
265 | // Get product data from JSON file
266 | $.getJSON(s.JSONFile, function (data) {
267 | simpleStore.setProducts(data.products);
268 | })
269 | .fail(function () { simpleStore.handleFailure(s, errorMsg); });
270 | })
271 | .fail(function () { simpleStore.handleFailure(s, errorMsg); });
272 | },
273 |
274 | checkMode : function (s) {
275 | if (s.hasOwnProperty("spreadsheetID") || s.hasOwnProperty("spreadsheetId")) {
276 | s.mode = "Google";
277 | }
278 | },
279 |
280 | checkout : function (s, checkoutData) {
281 | if (!$.isEmptyObject(checkoutData)) {
282 | simpleCart.checkout();
283 | s.cartContainer.fadeOut(s.fadeSpeed, function () {
284 | s.container.html('');
285 | s.container.fadeIn(s.fadeSpeed);
286 | });
287 | }
288 | },
289 |
290 | verifyCheckoutData : function (cdata, adata, v) {
291 | for (var d in cdata) {
292 | if (cdata.hasOwnProperty(d)) {
293 | var cp = cdata[d], cn = cp.name, cpp = cp.price;
294 | for (var i = 0; i < adata.length; i++) {
295 | var ap = adata[i], an = ap.name, app = ap.price;
296 | if (cn === an) {if (cpp != app) { v = false; }}
297 | }
298 | }
299 | }
300 | return v;
301 | },
302 |
303 | validatePrices : function (s) {
304 | var checkoutData = JSON.parse(localStorage.simpleCart_items),
305 | errorMsg = 'There was an error validating your cart.';
306 |
307 | if (s.mode === "JSON") {
308 | $.get(s.JSONFile)
309 | .success(function () {
310 | $.getJSON(s.JSONFile, function (data) {
311 | var JSONData = data.products;
312 | if (simpleStore.verifyCheckoutData(checkoutData, JSONData, true)) {
313 | simpleStore.checkout(s, checkoutData);
314 | } else {
315 | simpleStore.renderError(s, errorMsg);
316 | }
317 | })
318 | .fail(function () { simpleStore.handleFailure(s, errorMsg); });
319 | })
320 | .fail(function () { simpleStore.handleFailure(s, errorMsg); });
321 | } else {
322 | var plugin = s.mode.toLowerCase();
323 | if(simpleStore.plugins[plugin]) {
324 | simpleStore.plugins[plugin].validate(checkoutData);
325 | }
326 | }
327 | },
328 |
329 | setProducts: function (products, s) {
330 | if (products.length > 0) {
331 | products.forEach(function (product, index) {
332 | product.id = index + 1;
333 | simpleStore.products.push(product);
334 | });
335 | }
336 |
337 | // Manually trigger on initial load
338 | $(window).trigger('hashchange');
339 | },
340 |
341 | setLayout: function (s) {
342 | // Set brand
343 | if (s.brand.match('^http://') || s.brand.match('^https://') || s.brand.match('^www.')) {
344 | $('.brand').html('
');
345 | } else {
346 | $('.brand').html('' + s.brand + '
');
347 | }
348 |
349 | // Set title
350 | $('title').html(s.brand);
351 | },
352 |
353 | generateCart: function (s) {
354 | var tmpl = $('#cart-template').html(),
355 | $tmpl = $(tmpl);
356 | s.cartContainer.html($tmpl);
357 | },
358 |
359 | generateStore: function () {
360 |
361 | var s = this.settings;
362 |
363 | // Set mode
364 | this.checkMode(s);
365 |
366 | // Check for hash changes
367 | $(window).on('hashchange', function () {
368 | simpleStore.render(window.location.hash, s);
369 | });
370 |
371 | // Set products based on mode
372 | switch (s.mode) {
373 | case 'JSON':
374 | this.initJSON(s);
375 | break;
376 | case 'Google':
377 | if(simpleStore.plugins.google) {
378 | simpleStore.plugins.google.init(function (products) {
379 | simpleStore.setProducts(products, s);
380 | });
381 | } else {
382 | var errorMsg = 'There was an error loading the Google plugin. Make sure it is installed properly.';
383 | simpleStore.renderError(s, errorMsg);
384 | }
385 | break;
386 | default:
387 | this.initJSON(s);
388 | }
389 |
390 | // Because simpleCart items appends to cart, set up only once
391 | this.generateCart(s);
392 |
393 | // Setup layout
394 | this.setLayout(s);
395 |
396 | // Handle Checkout
397 | $('.simpleStore_checkout').on('click', function (e) {
398 | e.preventDefault();
399 | simpleStore.validatePrices(s);
400 | });
401 |
402 | // View Cart
403 | $('.simpleStore_viewCart').on('click', function (e) {
404 | e.preventDefault();
405 | window.location = '#cart';
406 | });
407 |
408 | // Go to home on close
409 | $('.view_close').on('click', function (e) {
410 | e.preventDefault();
411 | window.location.hash = '';
412 | });
413 |
414 | // SimpleCart extend
415 | simpleCart({
416 | afterAdd: function() {
417 | simpleStore.notifier('Item added to cart');
418 | }
419 | });
420 | },
421 |
422 | init: function (options) {
423 | if ($.isPlainObject(options)) {
424 | return this.extend(this.settings, options, function () {
425 | simpleStore.generateStore();
426 | });
427 | }
428 | }
429 | };
430 |
--------------------------------------------------------------------------------
/js/simpleCart.min.js:
--------------------------------------------------------------------------------
1 | /* simpleCart(js) | Copyright 2012 Brett Wejrowski Dual (http://simplecartjs.org/) | MIT or GPL licenses | v3.0.5 */
2 | (function(p,f){var s="string",k=function(e,f){return typeof e===f},e=function(e){return k(e,"undefined")},h=function(e){return k(e,"function")},y=function(e){return"object"===typeof HTMLElement?e instanceof HTMLElement:"object"===typeof e&&1===e.nodeType&&"string"===typeof e.nodeName},C=function(q){function E(a){return b.extend({attr:"",label:"",view:"attr",text:"",className:"",hide:!1},a||{})}function F(){if(!b.isReady){try{f.documentElement.doScroll("left")}catch(a){setTimeout(F,1);return}b.init()}}
3 | var t={MooTools:"$$",Prototype:"$$",jQuery:"*"},n=0,r={},x=q||"simpleCart",z={};q={};q={};var v=p.localStorage,l=p.console||{msgs:[],log:function(a){l.msgs.push(a)}},D={USD:{code:"USD",symbol:"$",name:"US Dollar"},AUD:{code:"AUD",symbol:"$",name:"Australian Dollar"},BRL:{code:"BRL",symbol:"R$",name:"Brazilian Real"},CAD:{code:"CAD",symbol:"$",name:"Canadian Dollar"},CZK:{code:"CZK",symbol:" Kč",name:"Czech Koruna",after:!0},DKK:{code:"DKK",symbol:"DKK ",name:"Danish Krone"},
4 | EUR:{code:"EUR",symbol:"€",name:"Euro"},HKD:{code:"HKD",symbol:"$",name:"Hong Kong Dollar"},HUF:{code:"HUF",symbol:"Ft",name:"Hungarian Forint"},ILS:{code:"ILS",symbol:"₪",name:"Israeli New Sheqel"},JPY:{code:"JPY",symbol:"¥",name:"Japanese Yen",accuracy:0},MXN:{code:"MXN",symbol:"$",name:"Mexican Peso"},NOK:{code:"NOK",symbol:"NOK ",name:"Norwegian Krone"},NZD:{code:"NZD",symbol:"$",name:"New Zealand Dollar"},PLN:{code:"PLN",symbol:"PLN ",name:"Polish Zloty"},
5 | GBP:{code:"GBP",symbol:"£",name:"Pound Sterling"},SGD:{code:"SGD",symbol:"$",name:"Singapore Dollar"},SEK:{code:"SEK",symbol:"SEK ",name:"Swedish Krona"},CHF:{code:"CHF",symbol:"CHF ",name:"Swiss Franc"},THB:{code:"THB",symbol:"฿",name:"Thai Baht"},BTC:{code:"BTC",symbol:" BTC",name:"Bitcoin",accuracy:4,after:!0}},m={checkout:{type:"PayPal",email:"you@yours.com"},currency:"USD",language:"english-us",cartStyle:"div",cartColumns:[{attr:"name",label:"Name"},{attr:"price",label:"Price",
6 | view:"currency"},{view:"decrement",label:!1},{attr:"quantity",label:"Qty"},{view:"increment",label:!1},{attr:"total",label:"SubTotal",view:"currency"},{view:"remove",text:"Remove",label:!1}],excludeFromCheckout:["thumb"],shippingFlatRate:0,shippingQuantityRate:0,shippingTotalRate:0,shippingCustom:null,taxRate:0,taxShipping:!1,data:{}},b=function(a){if(h(a))return b.ready(a);if(k(a,"object"))return b.extend(m,a)},A,B;b.extend=function(a,d){var c;e(d)&&(d=a,a=b);for(c in d)Object.prototype.hasOwnProperty.call(d,
7 | c)&&(a[c]=d[c]);return a};b.extend({copy:function(a){a=C(a);a.init();return a}});b.extend({isReady:!1,add:function(a,d){var c=new b.Item(a||{}),g=!0,u=!0===d?d:!1;if(!u&&(g=b.trigger("beforeAdd",[c]),!1===g))return!1;(g=b.has(c))?(g.increment(c.quantity()),c=g):r[c.id()]=c;b.update();u||b.trigger("afterAdd",[c,e(g)]);return c},each:function(a,d){var c,g=0,u,e,w;if(h(a))e=a,w=r;else if(h(d))e=d,w=a;else return;for(c in w)if(Object.prototype.hasOwnProperty.call(w,c)){u=e.call(b,w[c],g,c);if(!1===u)break;
8 | g+=1}},find:function(a){var d=[];if(k(r[a],"object"))return r[a];if(k(a,"object"))return b.each(function(c){var g=!0;b.each(a,function(a,b,d){k(a,s)?a.match(/<=.*/)?(a=parseFloat(a.replace("<=","")),c.get(d)&&parseFloat(c.get(d))<=a||(g=!1)):a.match(/)?(a=parseFloat(a.replace("<","")),c.get(d)&&parseFloat(c.get(d))=/)?(a=parseFloat(a.replace(">=","")),c.get(d)&&parseFloat(c.get(d))>=a||(g=!1)):a.match(/>/)?(a=parseFloat(a.replace(">","")),c.get(d)&&parseFloat(c.get(d))>a||
9 | (g=!1)):c.get(d)&&c.get(d)===a||(g=!1):c.get(d)&&c.get(d)===a||(g=!1);return g});g&&d.push(c)}),d;e(a)&&b.each(function(a){d.push(a)});return d},items:function(){return this.find()},has:function(a){var d=!1;b.each(function(b){b.equals(a)&&(d=b)});return d},empty:function(){var a={};b.each(function(b){!1===b.remove(!0)&&(a[b.id()]=b)});r=a;b.update()},quantity:function(){var a=0;b.each(function(b){a+=b.quantity()});return a},total:function(){var a=0;b.each(function(b){a+=b.total()});return a},grandTotal:function(){return b.total()+
10 | b.tax()+b.shipping()},update:function(){b.save();b.trigger("update")},init:function(){b.load();b.update();b.ready()},$:function(a){return new b.ELEMENT(a)},$create:function(a){return b.$(f.createElement(a))},setupViewTool:function(){var a,d=p,c;for(c in t)if(Object.prototype.hasOwnProperty.call(t,c)&&p[c]&&(a=t[c].replace("*",c).split("."),(a=a.shift())&&(d=d[a]),"function"===typeof d)){A=d;b.extend(b.ELEMENT._,z[c]);break}},ids:function(){var a=[];b.each(function(b){a.push(b.id())});return a},save:function(){b.trigger("beforeSave");
11 | var a={};b.each(function(d){a[d.id()]=b.extend(d.fields(),d.options())});v.setItem(x+"_items",JSON.stringify(a));b.trigger("afterSave")},load:function(){r={};var a=v.getItem(x+"_items");if(a){try{b.each(JSON.parse(a),function(a){b.add(a,!0)})}catch(d){b.error("Error Loading data: "+d)}b.trigger("load")}},ready:function(a){h(a)?b.isReady?a.call(b):b.bind("ready",a):e(a)&&!b.isReady&&(b.trigger("ready"),b.isReady=!0)},error:function(a){var d="";k(a,s)?d=a:k(a,"object")&&k(a.message,s)&&(d=a.message);
12 | try{l.log("simpleCart(js) Error: "+d)}catch(c){}b.trigger("error",a)}});b.extend({tax:function(){var a=m.taxShipping?b.total()+b.shipping():b.total(),d=b.taxRate()*a;b.each(function(a){a.get("tax")?d+=a.get("tax"):a.get("taxRate")&&(d+=a.get("taxRate")*a.total())});return parseFloat(d)},taxRate:function(){return m.taxRate||0},shipping:function(a){if(h(a))b({shippingCustom:a});else{var d=m.shippingQuantityRate*b.quantity()+m.shippingTotalRate*b.total()+m.shippingFlatRate;h(m.shippingCustom)&&(d+=m.shippingCustom.call(b));
13 | b.each(function(a){d+=parseFloat(a.get("shipping")||0)});return parseFloat(d)}}});B={attr:function(a,b){return a.get(b.attr)||""},currency:function(a,d){return b.toCurrency(a.get(d.attr)||0)},link:function(a,b){return""+b.text+""},decrement:function(a,b){return""+(b.text||"-")+""},increment:function(a,b){return""+(b.text||"+")+""},image:function(a,b){return"
"},input:function(a,b){return""},remove:function(a,b){return""+(b.text||"X")+""}};b.extend({writeCart:function(a){var d=m.cartStyle.toLowerCase(),c="table"===d,g=c?"tr":"div",u=c?"th":"div",e=c?"td":"div",w=b.$create(d),d=b.$create(g).addClass("headerRow"),f,h;b.$(a).html(" ").append(w);w.append(d);c=0;for(h=m.cartColumns.length;cc.price&&(c.price=0);k(c.quantity,s)&&(c.quantity=parseInt(c.quantity.replace(b.currency().delimiter,""),10));isNaN(c.quantity)&&(c.quantity=1);0>=c.quantity&&g.remove()}var c={},g=this;k(a,"object")&&b.extend(c,a);n+=1;for(c.id=
17 | c.id||"SCI-"+n;!e(r[c.id]);)n+=1,c.id="SCI-"+n;g.get=function(a,b){var d=!b;return e(a)?a:h(c[a])?c[a].call(g):e(c[a])?h(g[a])&&d?g[a].call(g):!e(g[a])&&d?g[a]:c[a]:c[a]};g.set=function(a,b){e(a)||(c[a.toLowerCase()]=b,"price"!==a.toLowerCase()&&"quantity"!==a.toLowerCase()||d());return g};g.equals=function(a){for(var b in c)if(Object.prototype.hasOwnProperty.call(c,b)&&"quantity"!==b&&"id"!==b&&a.get(b)!==c[b])return!1;return!0};g.options=function(){var a={};b.each(c,function(d,c,e){var f=!0;b.each(g.reservedFields(),
18 | function(a){a===e&&(f=!1);return f});f&&(a[e]=g.get(e))});return a};d()};b.Item._=b.Item.prototype={increment:function(a){a=parseInt(a||1,10);this.quantity(this.quantity()+a);return 1>this.quantity()?(this.remove(),null):this},decrement:function(a){return this.increment(-parseInt(a||1,10))},remove:function(a){if(!1===b.trigger("beforeRemove",[r[this.id()]]))return!1;delete r[this.id()];a||b.update();return null},reservedFields:function(){return"quantity id item_number price name shipping tax taxRate".split(" ")},
19 | fields:function(){var a={},d=this;b.each(d.reservedFields(),function(b){d.get(b)&&(a[b]=d.get(b))});return a},quantity:function(a){return e(a)?parseInt(this.get("quantity",!0)||1,10):this.set("quantity",a)},price:function(a){return e(a)?parseFloat(this.get("price",!0).toString().replace(b.currency().symbol,"").replace(b.currency().delimiter,"")||1):this.set("price",parseFloat(a.toString().replace(b.currency().symbol,"").replace(b.currency().delimiter,"")))},id:function(){return this.get("id",!1)},
20 | total:function(){return this.quantity()*this.price()}};b.extend({checkout:function(){if("custom"===m.checkout.type.toLowerCase()&&h(m.checkout.fn))m.checkout.fn.call(b,m.checkout);else if(h(b.checkout[m.checkout.type])){var a=b.checkout[m.checkout.type].call(b,m.checkout);a.data&&a.action&&a.method&&!1!==b.trigger("beforeCheckout",[a.data])&&b.generateAndSendForm(a)}else b.error("No Valid Checkout Method Specified")},extendCheckout:function(a){return b.extend(b.checkout,a)},generateAndSendForm:function(a){var d=
21 | b.$create("form");d.attr("style","display:none;");d.attr("action",a.action);d.attr("method",a.method);b.each(a.data,function(a,g,e){d.append(b.$create("input").attr("type","hidden").attr("name",e).val(a))});b.$("body").append(d);d.el.submit();d.remove()}});b.extendCheckout({PayPal:function(a){if(!a.email)return b.error("No email provided for PayPal checkout");var d={cmd:"_cart",upload:"1",currency_code:b.currency().code,business:a.email,rm:"GET"===a.method?"0":"2",tax_cart:(1*b.tax()).toFixed(2),
22 | handling_cart:(1*b.shipping()).toFixed(2),charset:"utf-8"},c=a.sandbox?"https://www.sandbox.paypal.com/cgi-bin/webscr":"https://www.paypal.com/cgi-bin/webscr",g="GET"===a.method?"GET":"POST";a.success&&(d["return"]=a.success);a.cancel&&(d.cancel_return=a.cancel);a.notify&&(d.notify_url=a.notify);b.each(function(a,c){var g=c+1,e=a.options(),f=0,h;d["item_name_"+g]=a.get("name");d["quantity_"+g]=a.quantity();d["amount_"+g]=(1*a.price()).toFixed(2);d["item_number_"+g]=a.get("item_number")||g;b.each(e,
23 | function(a,c,e){10>c&&(h=!0,b.each(m.excludeFromCheckout,function(a){a===e&&(h=!1)}),h&&(f+=1,d["on"+c+"_"+g]=e,d["os"+c+"_"+g]=a))});d["option_index_"+c]=Math.min(10,f)});return{action:c,method:g,data:d}},GoogleCheckout:function(a){if(!a.merchantID)return b.error("No merchant id provided for GoogleCheckout");if("USD"!==b.currency().code&&"GBP"!==b.currency().code)return b.error("Google Checkout only accepts USD and GBP");var d={ship_method_name_1:"Shipping",ship_method_price_1:b.shipping(),ship_method_currency_1:b.currency().code,
24 | _charset_:""},c="https://checkout.google.com/api/checkout/v2/checkoutForm/Merchant/"+a.merchantID;a="GET"===a.method?"GET":"POST";b.each(function(a,c){var e=c+1,f=[],h;d["item_name_"+e]=a.get("name");d["item_quantity_"+e]=a.quantity();d["item_price_"+e]=a.price();d["item_currency_ "+e]=b.currency().code;d["item_tax_rate"+e]=a.get("taxRate")||b.taxRate();b.each(a.options(),function(a,d,c){h=!0;b.each(m.excludeFromCheckout,function(a){a===c&&(h=!1)});h&&f.push(c+": "+a)});d["item_description_"+e]=f.join(", ")});
25 | return{action:c,method:a,data:d}},AmazonPayments:function(a){if(!a.merchant_signature)return b.error("No merchant signature provided for Amazon Payments");if(!a.merchant_id)return b.error("No merchant id provided for Amazon Payments");if(!a.aws_access_key_id)return b.error("No AWS access key id provided for Amazon Payments");var d={aws_access_key_id:a.aws_access_key_id,merchant_signature:a.merchant_signature,currency_code:b.currency().code,tax_rate:b.taxRate(),weight_unit:a.weight_unit||"lb"},c="https://payments"+
26 | (a.sandbox?"-sandbox":"")+".amazon.com/checkout/"+a.merchant_id,g="GET"===a.method?"GET":"POST";b.each(function(c,g){var e=g+1,f=[];d["item_title_"+e]=c.get("name");d["item_quantity_"+e]=c.quantity();d["item_price_"+e]=c.price();d["item_sku_ "+e]=c.get("sku")||c.id();d["item_merchant_id_"+e]=a.merchant_id;c.get("weight")&&(d["item_weight_"+e]=c.get("weight"));m.shippingQuantityRate&&(d["shipping_method_price_per_unit_rate_"+e]=m.shippingQuantityRate);b.each(c.options(),function(a,d,c){var g=!0;b.each(m.excludeFromCheckout,
27 | function(a){a===c&&(g=!1)});g&&"weight"!==c&&"tax"!==c&&f.push(c+": "+a)});d["item_description_"+e]=f.join(", ")});return{action:c,method:g,data:d}},SendForm:function(a){if(!a.url)return b.error("URL required for SendForm Checkout");var d={currency:b.currency().code,shipping:b.shipping(),tax:b.tax(),taxRate:b.taxRate(),itemCount:b.find({}).length},c=a.url,g="GET"===a.method?"GET":"POST";b.each(function(a,c){var g=c+1,e=[],f;d["item_name_"+g]=a.get("name");d["item_quantity_"+g]=a.quantity();d["item_price_"+
28 | g]=a.price();b.each(a.options(),function(a,d,c){f=!0;b.each(m.excludeFromCheckout,function(a){a===c&&(f=!1)});f&&e.push(c+": "+a)});d["item_options_"+g]=e.join(", ")});a.success&&(d["return"]=a.success);a.cancel&&(d.cancel_return=a.cancel);a.extra_data&&(d=b.extend(d,a.extra_data));return{action:c,method:g,data:d}}});q={bind:function(a,d){if(!h(d))return this;this._events||(this._events={});var c=a.split(/ +/);b.each(c,function(a){!0===this._events[a]?d.apply(this):e(this._events[a])?this._events[a]=
29 | [d]:this._events[a].push(d)});return this},trigger:function(a,b){var c=!0,g,f;this._events||(this._events={});if(!e(this._events[a])&&h(this._events[a][0]))for(g=0,f=this._events[a].length;ge?"0"+e:e}function f(f){e.lastIndex=0;return e.test(f)?'"'+f.replace(e,function(e){var f=C[e];return"string"===typeof f?f:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+f+'"'}function s(e,k){var t,n,r,p,z=h,v,l=k[e];l&&"object"===typeof l&&"function"===typeof l.toJSON&&(l=l.toJSON(e));"function"===typeof q&&(l=q.call(k,e,l));switch(typeof l){case "string":return f(l);case "number":return isFinite(l)?String(l):"null";case "boolean":case "null":return String(l);
46 | case "object":if(!l)return"null";h+=y;v=[];if("[object Array]"===Object.prototype.toString.apply(l)){p=l.length;for(t=0;t