Commits

jasonyeo committed 8b28947 Draft

can send email, can search, can now send server payment

Comments (0)

Files changed (12)

 #!/usr/bin/python -tt
 from flask import Flask, url_for, request, redirect, jsonify, Response, render_template
+from simplejson import loads, dumps
+import urllib
+import urllib2
+import requests
 
 app = Flask(__name__)
 
 def checkout3():
     return render_template('checkOutStep3.html')
 
+@app.route('/creditcards', methods=['POST'])
+def creditcard():
+    #print dir(request)
+    url = 'http://lowe.jschua.com/DinoPos/api/creditcards'
+    data = loads(request.data)
+    print data
+    data = urllib.urlencode(data)
+    req = urllib2.Request(url, data)
+    response = urllib2.urlopen(req)
+    return response.read()
+
+@app.route('/orders', methods=['PUT'])
+def orders():
+    data = loads(request.data)
+    print data
+    r = requests.put('http://lowe.jschua.com/DinoPos/api/orders', data)
+    return r.text
+
+@app.route('/mail', methods=['PUT'])
+def mail():
+    data = loads(request.data)
+    print data
+    r = requests.put('http://lowe.jschua.com/DinoPos/api/mail', data)
+    print r.text
+    return r.text
+
 @app.route('/products', methods=['GET', 'POST'])
 def products():
     from urllib import urlopen
     });
     var AekiRouter = new Workspace();
 
+    $.fn.spin = function(opts) {
+        this.each(function() {
+            var $this = $(this),
+            data = $this.data();
+
+        if (data.spinner) {
+            data.spinner.stop();
+            delete data.spinner;
+        }
+        if (opts !== false) {
+            data.spinner = new Spinner($.extend({color: $this.css('color')}, opts)).spin(this);
+        }
+        });
+        return this;
+    };
+
+    app.spinOpts = {
+        lines: 13, // The number of lines to draw
+        length: 30, // The length of each line
+        width: 12, // The line thickness
+        radius: 40, // The radius of the inner circle
+        corners: 1, // Corner roundness (0..1)
+        rotate: 0, // The rotation offset
+        color: '#000', // #rgb or #rrggbb
+        speed: 1, // Rounds per second
+        trail: 60, // Afterglow percentage
+        shadow: false, // Whether to render a shadow
+        hwaccel: false, // Whether to use hardware acceleration
+        className: 'spinner', // The CSS class to assign to the spinner
+        zIndex: 2e9, // The z-index (defaults to 2000000000)
+        top: 'auto', // Top position relative to parent in px
+        left: 'auto' // Left position relative to parent in px
+    };
     Backbone.history.start();
 });
 

static/js/collections/cart.js

             return this.filter(function( product ) {
                 return product.get('Product_ID') === id;
             })[0];
+        },
+
+        getProductIds: function() {
+            var productArr = new Array();
+            this.each(function( product ) {
+                productArr.push(product.get('Product_ID'));
+            });
+            return productArr;
+        },
+
+        getTotalPrice: function() {
+            var total = 0;
+            this.each(function( product ) {
+                total += parseInt(product.get('Pricebuy'), 10);
+            });
+            return total;
+        },
+
+        getQuantities: function() {
+            var qtyArr = new Array();
+            this.each(function( product ) {
+                qtyArr.push(1);
+            });
+            return qtyArr;
         }
     });
 

static/js/collections/products.js

             });
         },
 
+        searchProducts: function( searchTerm ) {
+            return this.filter(function( product ) {
+                return product.get('Name').indexOf( searchTerm ) !== -1 || 
+                    product.get('Brand').indexOf( searchTerm ) !== -1 ||
+                    product.get('Description').indexOf( searchTerm ) !== -1;
+            });
+        },
+
         getProductById: function( id ) {
             return this.filter(function( product ) {
                 return product.get('Product_ID') === id;

static/js/lib/json2js/json2.js

+/*
+    json2.js
+    2012-10-08
+
+    Public Domain.
+
+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+    See http://www.JSON.org/js.html
+
+
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
+
+
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
+
+        JSON.stringify(value, replacer, space)
+            value       any JavaScript value, usually an object or array.
+
+            replacer    an optional parameter that determines how object
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
+
+            space       an optional parameter that specifies the indentation
+                        of nested structures. If it is omitted, the text will
+                        be packed without extra whitespace. If it is a number,
+                        it will specify the number of spaces to indent at each
+                        level. If it is a string (such as '\t' or ' '),
+                        it contains the characters used to indent at each level.
+
+            This method produces a JSON text from a JavaScript value.
+
+            When an object value is found, if the object contains a toJSON
+            method, its toJSON method will be called and the result will be
+            stringified. A toJSON method does not serialize: it returns the
+            value represented by the name/value pair that should be serialized,
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the value
+
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
+
+                    return this.getUTCFullYear()   + '-' +
+                         f(this.getUTCMonth() + 1) + '-' +
+                         f(this.getUTCDate())      + 'T' +
+                         f(this.getUTCHours())     + ':' +
+                         f(this.getUTCMinutes())   + ':' +
+                         f(this.getUTCSeconds())   + 'Z';
+                };
+
+            You can provide an optional replacer method. It will be passed the
+            key and value of each member, with this bound to the containing
+            object. The value that is returned from your method will be
+            serialized. If your method returns undefined, then the member will
+            be excluded from the serialization.
+
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
+            stringified.
+
+            Values that do not have JSON representations, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped; in arrays they will be replaced with null. You can use
+            a replacer function to replace those with JSON values.
+            JSON.stringify(undefined) returns undefined.
+
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
+
+            If the space parameter is a non-empty string, then that string will
+            be used for indentation. If the space parameter is a number, then
+            the indentation will be that many spaces.
+
+            Example:
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
+
+        JSON.parse(text, reviver)
+            This method parses a JSON text to produce an object or array.
+            It can throw a SyntaxError exception.
+
+            The optional reviver parameter is a function that can filter and
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. Values that look like ISO date strings will
+            // be converted to Date objects.
+
+            myData = JSON.parse(text, function (key, value) {
+                var a;
+                if (typeof value === 'string') {
+                    a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+                    if (a) {
+                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+                            +a[5], +a[6]));
+                    }
+                }
+                return value;
+            });
+
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
+
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+*/
+
+/*jslint evil: true, regexp: true */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
+*/
+
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (typeof JSON !== 'object') {
+    JSON = {};
+}
+
+(function () {
+    'use strict';
+
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
+
+    if (typeof Date.prototype.toJSON !== 'function') {
+
+        Date.prototype.toJSON = function (key) {
+
+            return isFinite(this.valueOf())
+                ? this.getUTCFullYear()     + '-' +
+                    f(this.getUTCMonth() + 1) + '-' +
+                    f(this.getUTCDate())      + 'T' +
+                    f(this.getUTCHours())     + ':' +
+                    f(this.getUTCMinutes())   + ':' +
+                    f(this.getUTCSeconds())   + 'Z'
+                : null;
+        };
+
+        String.prototype.toJSON      =
+            Number.prototype.toJSON  =
+            Boolean.prototype.toJSON = function (key) {
+                return this.valueOf();
+            };
+    }
+
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
+
+
+    function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+        escapable.lastIndex = 0;
+        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
+            var c = meta[a];
+            return typeof c === 'string'
+                ? c
+                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+        }) + '"' : '"' + string + '"';
+    }
+
+
+    function str(key, holder) {
+
+// Produce a string from holder[key].
+
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
+
+// What happens next depends on the value's type.
+
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
+
+        case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+            return isFinite(value) ? String(value) : 'null';
+
+        case 'boolean':
+        case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+            return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+        case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+            if (!value) {
+                return 'null';
+            }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+            gap += indent;
+            partial = [];
+
+// Is the value an array?
+
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                v = partial.length === 0
+                    ? '[]'
+                    : gap
+                    ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
+                    : '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    if (typeof rep[i] === 'string') {
+                        k = rep[i];
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                for (k in value) {
+                    if (Object.prototype.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+            v = partial.length === 0
+                ? '{}'
+                : gap
+                ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
+                : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
+        }
+    }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+            var i;
+            gap = '';
+            indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
+                }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                    typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+            return str('', {'': value});
+        };
+    }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+            var j;
+
+            function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.prototype.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
+                            }
+                        }
+                    }
+                }
+                return reviver.call(holder, key, value);
+            }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+            text = String(text);
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+            if (/^[\],:{}\s]*$/
+                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
+                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
+                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                return typeof reviver === 'function'
+                    ? walk({'': j}, '')
+                    : j;
+            }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+            throw new SyntaxError('JSON.parse');
+        };
+    }
+}());

static/js/lib/spin/spin.min.js

+//fgnass.github.com/spin.js#v1.2.7
+!function(e,t,n){function o(e,n){var r=t.createElement(e||"div"),i;for(i in n)r[i]=n[i];return r}function u(e){for(var t=1,n=arguments.length;t<n;t++)e.appendChild(arguments[t]);return e}function f(e,t,n,r){var o=["opacity",t,~~(e*100),n,r].join("-"),u=.01+n/r*100,f=Math.max(1-(1-e)/t*(100-u),e),l=s.substring(0,s.indexOf("Animation")).toLowerCase(),c=l&&"-"+l+"-"||"";return i[o]||(a.insertRule("@"+c+"keyframes "+o+"{"+"0%{opacity:"+f+"}"+u+"%{opacity:"+e+"}"+(u+.01)+"%{opacity:1}"+(u+t)%100+"%{opacity:"+e+"}"+"100%{opacity:"+f+"}"+"}",a.cssRules.length),i[o]=1),o}function l(e,t){var i=e.style,s,o;if(i[t]!==n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(o=0;o<r.length;o++){s=r[o]+t;if(i[s]!==n)return s}}function c(e,t){for(var n in t)e.style[l(e,n)||n]=t[n];return e}function h(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)e[i]===n&&(e[i]=r[i])}return e}function p(e){var t={x:e.offsetLeft,y:e.offsetTop};while(e=e.offsetParent)t.x+=e.offsetLeft,t.y+=e.offsetTop;return t}var r=["webkit","Moz","ms","O"],i={},s,a=function(){var e=o("style",{type:"text/css"});return u(t.getElementsByTagName("head")[0],e),e.sheet||e.styleSheet}(),d={lines:12,length:7,width:5,radius:10,rotate:0,corners:1,color:"#000",speed:1,trail:100,opacity:.25,fps:20,zIndex:2e9,className:"spinner",top:"auto",left:"auto",position:"relative"},v=function m(e){if(!this.spin)return new m(e);this.opts=h(e||{},m.defaults,d)};v.defaults={},h(v.prototype,{spin:function(e){this.stop();var t=this,n=t.opts,r=t.el=c(o(0,{className:n.className}),{position:n.position,width:0,zIndex:n.zIndex}),i=n.radius+n.length+n.width,u,a;e&&(e.insertBefore(r,e.firstChild||null),a=p(e),u=p(r),c(r,{left:(n.left=="auto"?a.x-u.x+(e.offsetWidth>>1):parseInt(n.left,10)+i)+"px",top:(n.top=="auto"?a.y-u.y+(e.offsetHeight>>1):parseInt(n.top,10)+i)+"px"})),r.setAttribute("aria-role","progressbar"),t.lines(r,t.opts);if(!s){var f=0,l=n.fps,h=l/n.speed,d=(1-n.opacity)/(h*n.trail/100),v=h/n.lines;(function m(){f++;for(var e=n.lines;e;e--){var i=Math.max(1-(f+e*v)%h*d,n.opacity);t.opacity(r,n.lines-e,i,n)}t.timeout=t.el&&setTimeout(m,~~(1e3/l))})()}return t},stop:function(){var e=this.el;return e&&(clearTimeout(this.timeout),e.parentNode&&e.parentNode.removeChild(e),this.el=n),this},lines:function(e,t){function i(e,r){return c(o(),{position:"absolute",width:t.length+t.width+"px",height:t.width+"px",background:e,boxShadow:r,transformOrigin:"left",transform:"rotate("+~~(360/t.lines*n+t.rotate)+"deg) translate("+t.radius+"px"+",0)",borderRadius:(t.corners*t.width>>1)+"px"})}var n=0,r;for(;n<t.lines;n++)r=c(o(),{position:"absolute",top:1+~(t.width/2)+"px",transform:t.hwaccel?"translate3d(0,0,0)":"",opacity:t.opacity,animation:s&&f(t.opacity,t.trail,n,t.lines)+" "+1/t.speed+"s linear infinite"}),t.shadow&&u(r,c(i("#000","0 0 4px #000"),{top:"2px"})),u(e,u(r,i(t.color,"0 0 1px rgba(0,0,0,.1)")));return e},opacity:function(e,t,n){t<e.childNodes.length&&(e.childNodes[t].style.opacity=n)}}),function(){function e(e,t){return o("<"+e+' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">',t)}var t=c(o("group"),{behavior:"url(#default#VML)"});!l(t,"transform")&&t.adj?(a.addRule(".spin-vml","behavior:url(#default#VML)"),v.prototype.lines=function(t,n){function s(){return c(e("group",{coordsize:i+" "+i,coordorigin:-r+" "+ -r}),{width:i,height:i})}function l(t,i,o){u(a,u(c(s(),{rotation:360/n.lines*t+"deg",left:~~i}),u(c(e("roundrect",{arcsize:n.corners}),{width:r,height:n.width,left:n.radius,top:-n.width>>1,filter:o}),e("fill",{color:n.color,opacity:n.opacity}),e("stroke",{opacity:0}))))}var r=n.length+n.width,i=2*r,o=-(n.width+n.length)*2+"px",a=c(s(),{position:"absolute",top:o,left:o}),f;if(n.shadow)for(f=1;f<=n.lines;f++)l(f,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(f=1;f<=n.lines;f++)l(f);return u(t,a)},v.prototype.opacity=function(e,t,n,r){var i=e.firstChild;r=r.shadow&&r.lines||0,i&&t+r<i.childNodes.length&&(i=i.childNodes[t+r],i=i&&i.firstChild,i=i&&i.firstChild,i&&(i.opacity=n))}):s=l(t,"animation")}(),typeof define=="function"&&define.amd?define(function(){return v}):e.Spinner=v}(window,document);

static/js/views/checkout.js

                 $('#order-summary').append( template( product.toJSON() ) );
             });
             var total = 0;
-            app.Cart.each(function(product) {
-                total += parseInt(product.get('Pricebuy'), 10);
-            });
+            total = app.Cart.getTotalPrice();
             console.log(total);
             $('#total-price').append(total);
             return this;
         },
 
+        submitPayment: function() {
+            var checkoutView = this;
+            console.log('submitting payment info');
+
+            var creditCardInfo = {
+                "Order_ID": 123,
+                "cardno": $('#inputCC').val(),
+                "code": $('#inputCW2').val(),
+                "expiryDate": $('#inputExpiryDate').val()
+            };
+
+            $.ajax({
+                type: "POST",
+                url: "/creditcards",
+                data: JSON.stringify(creditCardInfo),
+                contentType: "application/json; charset=utf-8",
+                statusCode: {
+                    202: function () {
+                        console.log("202 (Accepted)");
+                    },
+                    406: function () {
+                        console.log("406 (Not Acceptable)");
+                    },
+                },
+                success: function (data) {
+                    var str = 'Receipt No: ' + data;
+                    app.receipt = data;
+                    app.creditCardInfo = creditCardInfo;
+                    console.log(str);
+                    checkoutView.showStep2();
+                },
+                dataType: "text",
+                error: function (jqXHR, textStatus, errorThrown) {
+                    $('#result').text("The following error occured: " + textStatus, errorThrown);
+                }
+            }); 
+        },
+
+        submitDetails: function() {
+            var checkoutView = this;
+            var userInfo = {
+                "Name": $('#inputName').val(),
+                "Address": $('#inputAdd').val(),
+                "Tel": $('#inputContact').val(),
+                "Email": $('#inputEmail').val()
+            };
+            app.userInfo = userInfo;
+            app.userInfo.cardno = app.creditCardInfo.cardno;
+            var orderInfo = {
+                Branch_ID: 1,
+                Employee_ID: 1,
+                Product_ID: app.Cart.getProductIds(),
+                Quantity: app.Cart.getQuantities()
+            };
+            app.orderInfo = orderInfo;
+             $.ajax({
+                 type: "PUT",
+                 url: "/orders",
+                 data: JSON.stringify(orderInfo),
+                 contentType: "application/json; charset=utf-8",
+                 success: function (data) {
+                    var str = 'Order_ID: ' + data;
+                    app.orderId = data;
+                    console.log(str);
+                    checkoutView.sendMail();
+                    checkoutView.showStep3();
+                 },
+                 dataType: "text",
+                 error: function (jqXHR, textStatus, errorThrown) {
+                     $('#result').text("The following error occured: " + textStatus, errorThrown);
+                 }
+             }); 
+        },
+
+        sendMail: function() {
+            console.log('sending mail');
+            var mailTemplate = _.template( $('#mail-template').html() );
+            var ordersTemplate = _.template( $('#orders-template').html() );
+            $('#mail-text').html('');
+            $('#mail-text').html( mailTemplate( app.userInfo ) );
+
+            app.Cart.each(function( product ) {
+                $('#mail-text #orders-table').append( ordersTemplate( product.toJSON() ) );
+            });
+
+            var payload = $('#mail-text').html();
+            var mail = {
+                email: app.userInfo.Email,
+                payload: payload
+            };
+
+            $('#loading-overlay').addClass('modal-backdrop fade in');
+            $('#aekiapp').spin( app.spinOpts );
+
+            $.ajax({
+                type: "PUT",
+                url: "/mail",
+                data: JSON.stringify(mail),
+                contentType: "application/json; charset=utf-8",
+                success: function(data) {
+                    var str = 'Sent: ' + data
+                    console.log(str);
+                    $('#loading-overlay').removeClass('modal-backdrop fade in');
+                    $('#aekiapp .spinner').empty();
+                },
+                dataType: "text",
+                error: function (jqXHR, textStatus, errorThrown) {
+                    alert("The following error occured: " + textStatus, errorThrown);
+                }
+            });
+        },
+
         showStep2: function() {
             console.log('showing step2');
             $('#form-container').load('checkout2 #delivery-form');
         },
 
         events: {
-            'click #step1finish': 'showStep2',
-            'click #step2finish': 'showStep3'
+            'click #step1finish': 'submitPayment',
+            'click #step2finish': 'submitDetails'
         }
     });
 });

static/js/views/checkout3.js

         },
 
         render: function() {
+            var ordersTemplate = _.template( $('#orders-template').html() );
+            var custInfoTemplate = _.template( $('#cust-info-template').html() );
+            app.Cart.each(function( product ) {
+                $('#orders-table').append( ordersTemplate( product.toJSON() ) );
+            });
+            $('h5#total').append( app.Cart.getTotalPrice() );
+            $('#cust-info').append( custInfoTemplate( app.userInfo ) );
             return this;
         },
 

static/js/views/purchase.js

             console.log('Init purchase view');
             this.$el.load('purchase', this.initCartPopover);
             window.app.Products.on( 'reset', this.addAll, this );
-            //window.app.Products.on( 'add', this.addOne, this );
             window.app.Products.fetch();
             console.log('fetched products');
         },
 
         initCartPopover: function() {
+            $('#aekiapp').spin( app.spinOpts );
             new app.CartView();
         },
 
 
         addAll: function() {
             console.log('adding all');
+            $('#aekiapp .spinner').empty();
             this.$('#products-list').empty();
             app.Products.each(this.addOne, this);
         },
             });
         },
 
+        searchProduct: function( event ) {
+            if (event.keyCode === 13) {
+                var searchTerm = $('#product-search').val();
+                console.log( searchTerm );
+                var results = app.Products.searchProducts( searchTerm );
+                console.log( results );
+                this.$('#products-list').empty();
+                var that = this;
+                _.each(results, function(product) {
+                    that.addOne(product);
+                });
+
+            }
+        },
 
         showAll: function() {
             console.log("showing all");
             'click #trolley': 'showTrolleys',
             'click #storage': 'showStorage',
             'click #cord-set': 'showCordSet',
-            'click #all': 'showAll'
+            'click #all': 'showAll',
+            'keyup #product-search': 'searchProduct'
         }
     });
 });

templates/checkOutStep3.html

                     <th>Total Price </th>
                 </tr>
             </thead>
-            <tbody>
-                <tr>
-                    <td>Table lamp base (1) </td>
-                    <td>$19.90 </td>
-                    <tr>
-                    </tbody>
-                </table>
+            <tbody id="orders-table">
+            </tbody>
+        </table>
                 <div class="row pull-right">
-                    <h5>Total: $150</h5>
+                    <h5 id="total">Total: $</h5>
                 </div>
             </div>
 
 
             <div class="row well" style="margin-left:10px">
                 <legend>Your Information</legend>
-                Name: Steve Ng
-                <br/>Address: Blk 501, Hougang hehee ave 9, #01-11 (530501)
-                <br/>Contact No: 96210234
-                <br/>Email : xxx
-
-                <br/><br/>
-                Credit Card No: xxxxxxxxxxxx1234
-                </br>Cw2/Cv2: 9xx
-                </br>Expiry date: 27/xx/xx
+                <div id="cust-info">
+               </div>
 
                 <button class="btn   btn-info pull-right" id="btnFinish" type="button">Click here to return home and clear your information</button>
             </div>
 
         </div>
 
+        <div style="display: none" id="mail-text">
+        </div>
+        <div id="loading-overlay">
+        </div>
         <script type="text/template" id="cart-template">
             <div class="container">
                 <div class="row">
             </div>
             <br/>
         </script>
+        <script type="text/template" id="cust-info-template">
+                Name: <%= Name %>
+                <br/>Address: <%= Address %>
+                <br/>Contact No: <%= Tel %>
+                <br/>Email : <%= Email %>
+
+                <br/><br/>
+                Credit Card No: <%= cardno %>
+        </script>
+        <script type="text/template" id="orders-template">
+            <tr>
+                <td><%= Brand %> <%=Name%> (1) </td>
+                <td>$<%= Pricebuy %></td>
+            <tr>
+        </script>
+        <script type="text/template" id="mail-template">
+            Hi <%= Name %>,
+            <br/>
+            <br/>
+            You have ordered the following from us:
+            <table>
+                <thead>
+                    <tr>
+                        <th>Item (Qty) </th>
+                        <th>Total Price </th>
+                    </tr>
+                </thead>
+                <tbody id="orders-table">
+                </tbody>
+            </table>
+            </br>
+            </br>
+            Our gophers are busy packing it up and it will be sent to <%= Address %> soon.
+
+            </br>
+            </br>
+            Yours Sincerely,</br>
+            Delivery Gopher
+        </script>
+
+        </script>
         <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
         <script src="../static/js/lib/jquery/noty/jquery.noty.js"></script>
         <script src="../static/js/lib/jquery/noty/topCenter.js"></script>
         <script src="../static/js/lib/jquery/noty/default.js"></script>
+        <script src="../static/js/lib/json2js/json2.js"></script>
+        <script src="../static/js/lib/spin/spin.min.js"></script>
         <script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/js/bootstrap.min.js"></script>
         <script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.2/underscore-min.js"></script>
         <script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
         <script src="../static/js/views/product.js"></script>
         <script src="../static/js/views/productthumbnail.js"></script>
         <script src="../static/js/views/checkout3.js"></script>
-        <script src="../static/js/views/checkout2.js"></script>
         <script src="../static/js/views/checkout.js"></script>
         <script src="../static/js/views/purchase.js"></script>
         <script src="../static/js/app.js"></script>

templates/purchase.html

             <a class="brand" style="margin-left: 250px">&nbsp;&nbsp; Browse Product
             </a>
             <form class="navbar-search">
-                &nbsp;&nbsp; <input class="search-query" placeholder="Search Product" style="width: 180px" type="text"> &nbsp;&nbsp
+                &nbsp;&nbsp; <input id="product-search" class="search-query" placeholder="Search Product" style="width: 180px" type="text"> &nbsp;&nbsp
             </form>
             <!-- Shopping cart popover -->
             <a id="shopping-cart-btn" class="btn btn-info pull-right" style="display: inline-block; margin-right: 20px" rel="popover">