0% found this document useful (0 votes)
241 views

Project Report On Angular

The document describes a project report for creating a shopping cart application using AngularJS and PHP. It includes sections on the introduction, objectives, literature review, methodology, hardware and software requirements, system flow chart, data flow diagram, data dictionary, entity relationship diagram, and process description. The project report appears to be for a student submission to fulfill the requirements for a degree in computer science.

Uploaded by

Ms Rawat
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
241 views

Project Report On Angular

The document describes a project report for creating a shopping cart application using AngularJS and PHP. It includes sections on the introduction, objectives, literature review, methodology, hardware and software requirements, system flow chart, data flow diagram, data dictionary, entity relationship diagram, and process description. The project report appears to be for a student submission to fulfill the requirements for a degree in computer science.

Uploaded by

Ms Rawat
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 96

LOVELY PROFESSIONAL UNIVERSITY

DEPARTMENT OF SCIENCE
A
PROJECT REPORT
ON
SHOPPING CART IN ANGULAR J AND PHP

Submitted to lovely professional university


In the partial fulfillment of the requirement for the award of BSC degree
Submitted by:

1
ACKNOWLEDGEMENT

It is a great opportunity & pleasure for me to express my profound gratitude towards all the
individuals who directly or indirectly contributed towards completion of this report.
Working on this report was a great fun, excitement, challenges and a new exposure in the field of
Human Resource.

I am greatly indebted to under whose guidance and concern i am able to bring the report into its
real shape.

I am thankful to all faculty members of management department in providing me useful guidance


for the completion of this report.

I convey my gratitude to all those who are directly or indirectly related in the completion of this
project report.

Finally I would be failing in my duty if I don't express my thanks to the respondents whom I
visited and took their valuable time to answer my questionnaire.

2
DECLARATION

I ,student of lovely professional university, here by solemnly declare that the project
titled “SHOPPING CART ” is my original as all the information, facts and figure in
this report is based on my own experience and study during my summer training
procedures.

3
TABLE OF CONTENTS

 INTRODUCTION
 OBJECTIVE OF STUDY
 LITETRATURE REVIEW
 RESEARCH METHODOLOGY
 SAMPLE SIZE
 SCOPE OF STUDY
 LIMITATION OF THE STUDY
 HISTORY OF COMPANY
 TRAINING AND DEVELOPMENT IN LUMAX
 DATA ANALYSIS
 CONCLUSION
 RECOMMENDATION/ SUGGESTIONS
 BIBLIOGRAPHY
 QUESTIONAIRE

4
INTRODUCTION

28. Creating Your Own Shopping Cart


In this chapter you get to look at another practical implementation example of a shopping

cart. The reason that a shopping cart example was chosen is because it is very common

and familiar so the concepts will be easy to pick up on throughout the chapter. Also the

shopping cart example will allow us to look at one way to utilize the ability to switch

between views in AngularJS as you navigate through the checkout process.

The shopping cart you will create provides most of the necessary functionality, however,

it is not intended for production. There is missing validation and error handling in areas

to make the project reasonable to fit inside a book example. However, the project will

take you through the basic steps and provide you with fundamental understanding of how

the Node.JS, MongoDB and AngularJS operates.

5
Hardware & Software Requirement

Hardware Requirements ( MINIMUM )


Central Processing Unit Pentium IV 2.66 GHZ

Hard Disk Capacity 40 GB

Memory 256 MB RAM

Monitor LG

Keyboard TVS GOLD

Mouse Logitech Mouse

Software Requirements ( MINIMUM )


Server – Side:

Front End Microsoft Windows ASP.NET with C# 2005

Back End Microsoft SQL Server 2005

Tool End Microsoft Office 2003, Crystal Reports

Client – Side:

Operating System Windows XP Professional

Run Time Environment .NET Framework 2.0

6
6. System Flow Chart

7
System Flow Chart

8
7. Data Flow Diagram

9
Context Level Diagram:

10
First Level Diagram:

11
Second Level Diagram

12
Second Level Diagram:

13
Second Level Diagram:

14
8. Data Dictionary

15
Table: -Tbl_ User

Description: - This table store information of User like Name, Address, Contact
No, and Email Address. Each User has associated reference in User, which stores
projects belong to User, product which stores Product Information belong to User.

Fields:

Field Name Data Type Description Allow Null

vcID Varchar(50) User Name Primary key

vcPass Varchar(20) Password Not Null

vcFsNm Varchar(50) First Name Not Null

vcLsNm Varchar(50) Last Name Not Null

vcGender Varchar(6) Gender Not Null

vcAdd Varchar(50) Address Not Null

vcCity Varchar(50) City Not Null

vcState Varchar(50) State Not Null

vcZipCode Varchar(10) Zip Code Not Null

vcCnctNo Varchar(20) Contact No Not Null

vcEmailId Varchar(50) Email ID Not Null

vcConPass Varchar(20) ConformPassword Not Null

16
Table: - Tbl_Product

Description: - This table stores Information about Product of each User.

Fields:

Field Name Data Type Description Allow Null

nmID Numeric(4,0) Product ID Primary key

vcNm Varchar(50) Product Name Not Null

txtDes Text Description Not Null

nmPrice Numeric(18,0) Price Not Null

nmQuan Numeric(18,0) Quantity Not Null

nmDis Numeric(18,0) Discount Not Null

Image Image Product image Not Null

nmCtgryID Numeric(3,0) Category ID Foreign key

17
Table: - Tbl_Category

Description: - This table stores Information about Category of each User.

Fields:

Field Name Data Type Description Allow Null

nmCtgryID Numeric(3,0) Category ID Primary key

vcNm Varchar(50) Category Name Not Null

txtDes Text Description Not Null

18
Table: - Tbl_ Shopping Cart

Description: - This table stores Information about Cart.

Fields:

Field Name Data Type Description Allow Null

nmID Numeric(4,0) Product ID primary key

vcNm Varchar(50) Product Name Not Null

nmQuan Numeric(18,0) Quantity Not Null

nmPrice Numeric(18,0) Price Not Null

19
Table: - Tbl_ Order detail

Description: - This table stores Information about Order of each User.

Fields:

Field Name Data Type Description Allow Null

nmNo Numeric(4,0) Order No Primary key

nmID Numeric(4,0) Product ID Foreign key

nmQuan Numeric(18,0) Quantity Not Null

nmPrice Numeric(18,0) Order Price Not Null

nmAmt Numeric(18,0) Total Amount Not Null

20
Table: - Tbl_Admin

Description: - This table stores Information about Admin.

Fields:

Field Name Data Type Description Allow Null

vcID Varchar(50) Admin ID Primary key

vcPass Varchar(20) Admin Password Not Null

vcEmailID Varchar(50) Admin Email ID Not Null

21
9. E-R Diagram

22
23
10. Process Description

24
Brief Description Of Process
Process: Software process as a framework for the tasks that are required to build high quality
software. Or we can say that process defines the approach that is taken as software is
engineered.
Process model used by us:
Spiral model: The Spiral model originally proposed by Boehm , is an evolutionary
software process model that couples the iterative nature of prototyping with the controlled &
systematic aspects of the linear sequential model . It provides the potential for rapid
development of incremental version of the software.
Using the spiral model software is developed in a series of incremental releases.
A spiral model is divided into a number of framework activities also called task regions.
A spiral model contains six task regions:
Customer Communication: Tasks required to establish effective communication between
developer & customer.
Planning: Tasks required to define resources, timeline & other project related information.
Risk analysis: Task required to access.
Engineering: Tasks required to build one or more representation of the application.
Construction & release: Task required to construct, test, install & provide user support
(e.g., documentation & Training)
Customer evaluation: Tasks required to obtain customer feedback based on evolution of the
software representation created during the engineering stage & implemented during the
installation stage.

25
Process Algorithm
Step 1 : Initialization Username and Password.
Step 2 : Check the Username and Password. If Invalid Username and
Password then Go To Step 1 otherwise Go To Step 3.
Step 3 : Check Enter Operator is Admin or User. If Admin Go To
Step 4 otherwise Go To Step 8.
Step 4 : If create a new user Go To step 5 otherwise Go To Step 6.
Step 5 : Fill up the information in registration form and create a
Username and Password and fill up other information of this
new User. Go To Step 9.
Step 6: Written detail of following in the User table.
Username, password, Firstname, Lastname, Gender, Address, City, State,Zip-code
, Contact no, Email id and ISAdmin
Step 7 : If Request Issue then Update user ISAdmin or not information
and entry in the User table. Go To Step 9.
Step 8 : Go To Step 6.
Step 9 : End.

26
11. Input Designs

27
28
/*

AngularJS v1.3.15

(c) 2010-2014 Google, Inc. http://angularjs.org

License: MIT

*/

(function(Q,W,t){'use strict';function R(b){return function(){var


a=arguments[0],c;c="["+(b?b+":":"")+a+"]
http://errors.angularjs.org/1.3.15/"+(b?b+"/":"")+a;for(a=1;a<arguments.length;a
++){c=c+(1==a?"?":"&")+"p"+(a-1)+"=";var
d=encodeURIComponent,e;e=arguments[a];e="function"==typeof
e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof
e?"undefined":"string"!=typeof e?JSON.stringify(e):e;c+=d(e)}return
Error(c)}}function Sa(b){if(null==b||Ta(b))return!1;var a=b.length;return
b.nodeType===

qa&&a?!0:C(b)||H(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function


r(b,a,c){var d,e;if(b)if(G(b))for(d in
b)"prototype"==d||"length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnPro
perty(d)||a.call(c,b[d],d,b);else if(H(b)||Sa(b)){var f="object"!==typeof
b;d=0;for(e=b.length;d<e;d++)(f||d in b)&&a.call(c,b[d],d,b)}else
if(b.forEach&&b.forEach!==r)b.forEach(a,c,b);else for(d in
b)b.hasOwnProperty(d)&&a.call(c,b[d],d,b);return b}function Ed(b,a,c){for(var
d=Object.keys(b).sort(),e=0;e<d.length;e++)a.call(c,

b[d[e]],d[e]);return d}function mc(b){return function(a,c){b(c,a)}}function


Fd(){return++ob}function nc(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function
w(b){for(var a=b.$$hashKey,c=1,d=arguments.length;c<d;c++){var
e=arguments[c];if(e)for(var f=Object.keys(e),g=0,h=f.length;g<h;g++){var
l=f[g];b[l]=e[l]}}nc(b,a);return b}function aa(b){return parseInt(b,10)}function
Ob(b,a){return w(Object.create(b),a)}function E(){}function ra(b){return
b}function ea(b){return function(){return b}}function x(b){return"undefined"===

typeof b}function y(b){return"undefined"!==typeof b}function J(b){return


null!==b&&"object"===typeof b}function C(b){return"string"===typeof
b}function Y(b){return"number"===typeof b}function ga(b){return"[object
Date]"===Ca.call(b)}function G(b){return"function"===typeof b}function
29
Ua(b){return"[object RegExp]"===Ca.call(b)}function Ta(b){return
b&&b.window===b}function Va(b){return
b&&b.$evalAsync&&b.$watch}function Wa(b){return"boolean"===typeof
b}function oc(b){return!(!b||!(b.nodeName||b.prop&&

b.attr&&b.find))}function Gd(b){var a={};b=b.split(",");var


c;for(c=0;c<b.length;c++)a[b[c]]=!0;return a}function va(b){return
z(b.nodeName||b[0]&&b[0].nodeName)}function Xa(b,a){var
c=b.indexOf(a);0<=c&&b.splice(c,1);return a}function
Da(b,a,c,d){if(Ta(b)||Va(b))throw Ja("cpws");if(a){if(b===a)throw
Ja("cpi");c=c||[];d=d||[];if(J(b)){var e=c.indexOf(b);if(-1!==e)return
d[e];c.push(b);d.push(a)}if(H(b))for(var
f=a.length=0;f<b.length;f++)e=Da(b[f],null,c,d),J(b[f])&&(c.push(b[f]),d.push(e)),a.
push(e);

else{var g=a.$$hashKey;H(a)?a.length=0:r(a,function(b,c){delete a[c]});for(f in


b)b.hasOwnProperty(f)&&(e=Da(b[f],null,c,d),J(b[f])&&(c.push(b[f]),d.push(e)),a[f
]=e);nc(a,g)}}else if(a=b)H(b)?a=Da(b,[],c,d):ga(b)?a=new
Date(b.getTime()):Ua(b)?(a=new
RegExp(b.source,b.toString().match(/[^\/]*$/)[0]),a.lastIndex=b.lastIndex):J(b)&&(
e=Object.create(Object.getPrototypeOf(b)),a=Da(b,e,c,d));return a}function
sa(b,a){if(H(b)){a=a||[];for(var c=0,d=b.length;c<d;c++)a[c]=b[c]}else if(J(b))for(c in
a=a||{},

b)if("$"!==c.charAt(0)||"$"!==c.charAt(1))a[c]=b[c];return a||b}function
ha(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!
0;var c=typeof b,d;if(c==typeof
a&&"object"==c)if(H(b)){if(!H(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;
d++)if(!ha(b[d],a[d]))return!1;return!0}}else{if(ga(b))return
ga(a)?ha(b.getTime(),a.getTime()):!1;if(Ua(b))return
Ua(a)?b.toString()==a.toString():!1;if(Va(b)||Va(a)||Ta(b)||Ta(a)||H(a)||ga(a)||Ua(a))r
eturn!1;c={};for(d in b)if("$"!==

d.charAt(0)&&!G(b[d])){if(!ha(b[d],a[d]))return!1;c[d]=!0}for(d in
a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==t&&!G(a[d]))return!1;
return!0}return!1}function Ya(b,a,c){return b.concat(Za.call(a,c))}function
pc(b,a){var c=2<arguments.length?Za.call(arguments,2):[];return!G(a)||a instanceof
RegExp?a:c.length?function(){return
arguments.length?a.apply(b,Ya(c,arguments,0)):a.apply(b,c)}:function(){return

30
arguments.length?a.apply(b,arguments):a.call(b)}}function Hd(b,a){var
c=a;"string"===typeof b&&

"$"===b.charAt(0)&&"$"===b.charAt(1)?c=t:Ta(a)?c="$WINDOW":a&&W===
a?c="$DOCUMENT":Va(a)&&(c="$SCOPE");return c}function
$a(b,a){if("undefined"===typeof b)return t;Y(a)||(a=a?2:null);return
JSON.stringify(b,Hd,a)}function qc(b){return C(b)?JSON.parse(b):b}function
wa(b){b=A(b).clone();try{b.empty()}catch(a){}var
c=A("<div>").append(b).html();try{return
b[0].nodeType===pb?z(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-
]+)/,function(a,b){return"<"+z(b)})}catch(d){return z(c)}}function rc(b){try{return
decodeURIComponent(b)}catch(a){}}

function sc(b){var
a={},c,d;r((b||"").split("&"),function(b){b&&(c=b.replace(/\+/g,"%20").split("="),
d=rc(c[0]),y(d)&&(b=y(c[1])?rc(c[1]):!0,tc.call(a,d)?H(a[d])?a[d].push(b):a[d]=[a[d]
,b]:a[d]=b))});return a}function Pb(b){var
a=[];r(b,function(b,d){H(b)?r(b,function(b){a.push(Ea(d,!0)+(!0===b?"":"="+Ea(b,
!0)))}):a.push(Ea(d,!0)+(!0===b?"":"="+Ea(b,!0)))});return
a.length?a.join("&"):""}function qb(b){return
Ea(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function
Ea(b,a){return encodeURIComponent(b).replace(/%40/gi,

"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B
/gi,";").replace(/%20/g,a?"%20":"+")}function Id(b,a){var
c,d,e=rb.length;b=A(b);for(d=0;d<e;++d)if(c=rb[d]+a,C(c=b.attr(c)))return
c;return null}function Jd(b,a){var
c,d,e={};r(rb,function(a){a+="app";!c&&b.hasAttribute&&b.hasAttribute(a)&&(c
=b,d=b.getAttribute(a))});r(rb,function(a){a+="app";var
e;!c&&(e=b.querySelector("["+a.replace(":","\\:")+"]"))&&(c=e,d=e.getAttribute(
a))});c&&(e.strictDi=null!==Id(c,"strict-di"),

a(c,d?[d]:[],e))}function uc(b,a,c){J(c)||(c={});c=w({strictDi:!1},c);var
d=function(){b=A(b);if(b.injector()){var d=b[0]===W?"document":wa(b);throw
Ja("btstrpd",d.replace(/</,"&lt;").replace(/>/,"&gt;"));}a=a||[];a.unshift(["$provid
e",function(a){a.value("$rootElement",b)}]);c.debugInfoEnabled&&a.push(["$com
pileProvider",function(a){a.debugInfoEnabled(!0)}]);a.unshift("ng");d=ab(a,c.strict
Di);d.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,
d){a.$apply(function(){b.data("$injector",

31
d);c(b)(a)})}]);return
d},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;Q&&e.te
st(Q.name)&&(c.debugInfoEnabled=!0,Q.name=Q.name.replace(e,""));if(Q&&!f.te
st(Q.name))return
d();Q.name=Q.name.replace(f,"");ca.resumeBootstrap=function(b){r(b,function(b){
a.push(b)});return
d()};G(ca.resumeDeferredBootstrap)&&ca.resumeDeferredBootstrap()}function
Kd(){Q.name="NG_ENABLE_DEBUG_INFO!"+Q.name;Q.location.reload()}funct
ion Ld(b){b=ca.element(b).injector();if(!b)throw Ja("test");return
b.get("$$testability")}

function vc(b,a){a=a||"_";return
b.replace(Md,function(b,d){return(d?a:"")+b.toLowerCase()})}function Nd(){var
b;wc||((ta=Q.jQuery)&&ta.fn.on?(A=ta,w(ta.fn,{scope:Ka.scope,isolateScope:Ka.iso
lateScope,controller:Ka.controller,injector:Ka.injector,inheritedData:Ka.inherited
Data}),b=ta.cleanData,ta.cleanData=function(a){var c;if(Qb)Qb=!1;else for(var
d=0,e;null!=(e=a[d]);d++)(c=ta._data(e,"events"))&&c.$destroy&&ta(e).triggerHan
dler("$destroy");b(a)}):A=T,ca.element=A,wc=!0)}function Rb(b,a,c){if(!b)throw
Ja("areq",

a||"?",c||"required");return b}function sb(b,a,c){c&&H(b)&&(b=b[b.length-


1]);Rb(G(b),a,"not a function, got "+(b&&"object"===typeof
b?b.constructor.name||"Object":typeof b));return b}function
La(b,a){if("hasOwnProperty"===b)throw Ja("badname",a);}function
xc(b,a,c){if(!a)return b;a=a.split(".");for(var
d,e=b,f=a.length,g=0;g<f;g++)d=a[g],b&&(b=(e=b)[d]);return!c&&G(b)?pc(e,b):b}f
unction tb(b){var a=b[0];b=b[b.length-1];var
c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b);return A(c)}function
ia(){return Object.create(null)}

function Od(b){function a(a,b,c){return a[b]||(a[b]=c())}var


c=R("$injector"),d=R("ng");b=a(b,"angular",Object);b.$$minErr=b.$$minErr||R;
return a(b,"module",function(){var b={};return
function(f,g,h){if("hasOwnProperty"===f)throw
d("badname","module");g&&b.hasOwnProperty(f)&&(b[f]=null);return
a(b,f,function(){function a(c,d,e,f){f||(f=b);return
function(){f[e||"push"]([c,d,arguments]);return u}}if(!g)throw c("nomod",f);var
b=[],d=[],e=[],q=a("$injector","invoke","push",d),u={_invokeQueue:b,_configBloc
ks:d,
32
_runBlocks:e,requires:g,name:f,provider:a("$provide","provider"),factory:a("$pr
ovide","factory"),service:a("$provide","service"),value:a("$provide","value"),con
stant:a("$provide","constant","unshift"),animation:a("$animateProvider","regist
er"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","regis
ter"),directive:a("$compileProvider","directive"),config:q,run:function(a){e.push(a
);return this}};h&&q(h);return u})}})}function
Pd(b){w(b,{bootstrap:uc,copy:Da,extend:w,equals:ha,

element:A,forEach:r,injector:ab,noop:E,bind:pc,toJson:$a,fromJson:qc,identity:ra,
isUndefined:x,isDefined:y,isString:C,isFunction:G,isObject:J,isNumber:Y,isElemen
t:oc,isArray:H,version:Qd,isDate:ga,lowercase:z,uppercase:ub,callbacks:{counter:0
},getTestability:Ld,$$minErr:R,$$csp:bb,reloadWithDebugInfo:Kd});cb=Od(Q);tr
y{cb("ngLocale")}catch(a){cb("ngLocale",[]).provider("$locale",Rd)}cb("ng",["ng
Locale"],["$provide",function(a){a.provider({$$sanitizeUri:Sd});a.provider("$com
pile",yc).directive({a:Td,

input:zc,textarea:zc,form:Ud,script:Vd,select:Wd,style:Xd,option:Yd,ngBind:Zd,ng
BindHtml:$d,ngBindTemplate:ae,ngClass:be,ngClassEven:ce,ngClassOdd:de,ngClo
ak:ee,ngController:fe,ngForm:ge,ngHide:he,ngIf:ie,ngInclude:je,ngInit:ke,ngNonBi
ndable:le,ngPluralize:me,ngRepeat:ne,ngShow:oe,ngStyle:pe,ngSwitch:qe,ngSwitch
When:re,ngSwitchDefault:se,ngOptions:te,ngTransclude:ue,ngModel:ve,ngList:we,
ngChange:xe,pattern:Ac,ngPattern:Ac,required:Bc,ngRequired:Bc,minlength:Cc,n
gMinlength:Cc,maxlength:Dc,ngMaxlength:Dc,

ngValue:ye,ngModelOptions:ze}).directive({ngInclude:Ae}).directive(vb).directive(
Ec);a.provider({$anchorScroll:Be,$animate:Ce,$browser:De,$cacheFactory:Ee,$co
ntroller:Fe,$document:Ge,$exceptionHandler:He,$filter:Fc,$interpolate:Ie,$interva
l:Je,$http:Ke,$httpBackend:Le,$location:Me,$log:Ne,$parse:Oe,$rootScope:Pe,$q:
Qe,$$q:Re,$sce:Se,$sceDelegate:Te,$sniffer:Ue,$templateCache:Ve,$templateReque
st:We,$$testability:Xe,$timeout:Ye,$window:Ze,$$rAF:$e,$$asyncCallback:af,$$jq
Lite:bf})}])}function db(b){return b.replace(cf,

function(a,b,d,e){return e?d.toUpperCase():d}).replace(df,"Moz$1")}function
Gc(b){b=b.nodeType;return b===qa||!b||9===b}function Hc(b,a){var
c,d,e=a.createDocumentFragment(),f=[];if(Sb.test(b)){c=c||e.appendChild(a.createEl
ement("div"));d=(ef.exec(b)||["",""])[1].toLowerCase();d=ja[d]||ja._default;c.inner
HTML=d[1]+b.replace(ff,"<$1></$2>")+d[2];for(d=d[0];d--
;)c=c.lastChild;f=Ya(f,c.childNodes);c=e.firstChild;c.textContent=""}else

33
f.push(a.createTextNode(b));e.textContent="";e.innerHTML="";r(f,function(a){e.a
ppendChild(a)});

return e}function T(b){if(b instanceof T)return b;var


a;C(b)&&(b=N(b),a=!0);if(!(this instanceof T)){if(a&&"<"!=b.charAt(0))throw
Tb("nosel");return new T(b)}if(a){a=W;var
c;b=(c=gf.exec(b))?[a.createElement(c[1])]:(c=Hc(b,a))?c.childNodes:[]}Ic(this,b)}fu
nction Ub(b){return b.cloneNode(!0)}function
wb(b,a){a||xb(b);if(b.querySelectorAll)for(var
c=b.querySelectorAll("*"),d=0,e=c.length;d<e;d++)xb(c[d])}function
Jc(b,a,c,d){if(y(d))throw Tb("offargs");var
e=(d=yb(b))&&d.events,f=d&&d.handle;if(f)if(a)r(a.split(" "),

function(a){if(y(c)){var
d=e[a];Xa(d||[],c);if(d&&0<d.length)return}b.removeEventListener(a,f,!1);delete
e[a]});else for(a in e)"$destroy"!==a&&b.removeEventListener(a,f,!1),delete
e[a]}function xb(b,a){var c=b.ng339,d=c&&zb[c];d&&(a?delete
d.data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),Jc(b)),delete
zb[c],b.ng339=t))}function yb(b,a){var
c=b.ng339,c=c&&zb[c];a&&!c&&(b.ng339=c=++hf,c=zb[c]={events:{},data:{},hand
le:t});return c}function Vb(b,a,c){if(Gc(b)){var d=y(c),e=!d&&a&&!J(a),

f=!a;b=(b=yb(b,!e))&&b.data;if(d)b[a]=c;else{if(f)return b;if(e)return
b&&b[a];w(b,a)}}}function Ab(b,a){return b.getAttribute?-1<("
"+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+"
"):!1}function Bb(b,a){a&&b.setAttribute&&r(a.split("
"),function(a){b.setAttribute("class",N((" "+(b.getAttribute("class")||"")+"
").replace(/[\n\t]/g," ").replace(" "+N(a)+" "," ")))})}function
Cb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+"
").replace(/[\n\t]/g," ");

r(a.split(" "),function(a){a=N(a);-1===c.indexOf(" "+a+" ")&&(c+=a+"


")});b.setAttribute("class",N(c))}}function
Ic(b,a){if(a)if(a.nodeType)b[b.length++]=a;else{var
c=a.length;if("number"===typeof c&&a.window!==a){if(c)for(var
d=0;d<c;d++)b[b.length++]=a[d]}else b[b.length++]=a}}function Kc(b,a){return
Db(b,"$"+(a||"ngController")+"Controller")}function
Db(b,a,c){9==b.nodeType&&(b=b.documentElement);for(a=H(a)?a:[a];b;){for(var
d=0,e=a.length;d<e;d++)if((c=A.data(b,a[d]))!==t)return c;b=b.parentNode||

34
11===b.nodeType&&b.host}}function
Lc(b){for(wb(b,!0);b.firstChild;)b.removeChild(b.firstChild)}function
Mc(b,a){a||wb(b);var c=b.parentNode;c&&c.removeChild(b)}function
jf(b,a){a=a||Q;if("complete"===a.document.readyState)a.setTimeout(b);else
A(a).on("load",b)}function Nc(b,a){var c=Eb[a.toLowerCase()];return
c&&Oc[va(b)]&&c}function kf(b,a){var
c=b.nodeName;return("INPUT"===c||"TEXTAREA"===c)&&Pc[a]}function
lf(b,a){var c=function(c,e){c.isDefaultPrevented=function(){return
c.defaultPrevented};var f=

a[e||c.type],g=f?f.length:0;if(g){if(x(c.immediatePropagationStopped)){var
h=c.stopImmediatePropagation;c.stopImmediatePropagation=function(){c.immedia
tePropagationStopped=!0;c.stopPropagation&&c.stopPropagation();h&&h.call(c)}}
c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagation
Stopped};1<g&&(f=sa(f));for(var
l=0;l<g;l++)c.isImmediatePropagationStopped()||f[l].call(b,c)}};c.elem=b;return
c}function bf(){this.$get=function(){return
w(T,{hasClass:function(b,a){b.attr&&(b=b[0]);

return Ab(b,a)},addClass:function(b,a){b.attr&&(b=b[0]);return
Cb(b,a)},removeClass:function(b,a){b.attr&&(b=b[0]);return Bb(b,a)}})}}function
Ma(b,a){var c=b&&b.$$hashKey;if(c)return"function"===typeof
c&&(c=b.$$hashKey()),c;c=typeof b;return
c="function"==c||"object"==c&&null!==b?b.$$hashKey=c+":"+(a||Fd)():c+":"+b}
function eb(b,a){if(a){var
c=0;this.nextUid=function(){return++c}}r(b,this.put,this)}function
mf(b){return(b=b.toString().replace(Qc,"").match(Rc))?"function("+(b[1]||"").repl
ace(/[\s\r\n]+/,

" ")+")":"fn"}function ab(b,a){function c(a){return


function(b,c){if(J(b))r(b,mc(a));else return a(b,c)}}function
d(a,b){La(a,"service");if(G(b)||H(b))b=q.instantiate(b);if(!b.$get)throw
Fa("pget",a);return p[a+"Provider"]=b}function e(a,b){return function(){var
c=s.invoke(b,this);if(x(c))throw Fa("undef",a);return c}}function f(a,b,c){return
d(a,{$get:!1!==c?e(a,b):b})}function g(a){var b=[],c;r(a,function(a){function
d(a){var b,c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=q.get(e[0]);f[e[1]].apply(f,

e[2])}}if(!n.get(a)){n.put(a,!0);try{C(a)?(c=cb(a),b=b.concat(g(c.requires)).concat(c._
runBlocks),d(c._invokeQueue),d(c._configBlocks)):G(a)?b.push(q.invoke(a)):H(a)?b

35
.push(q.invoke(a)):sb(a,"module")}catch(e){throw H(a)&&(a=a[a.length-
1]),e.message&&e.stack&&-
1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Fa("modulerr",a,e.st
ack||e.message||e);}}});return b}function h(b,c){function
d(a,e){if(b.hasOwnProperty(a)){if(b[a]===l)throw Fa("cdep",a+" <- "+k.join(" <-
"));return b[a]}try{return k.unshift(a),

b[a]=l,b[a]=c(a,e)}catch(f){throw b[a]===l&&delete
b[a],f;}finally{k.shift()}}function e(b,c,f,g){"string"===typeof f&&(g=f,f=null);var
k=[],h=ab.$$annotate(b,a,g),l,q,p;q=0;for(l=h.length;q<l;q++){p=h[q];if("string"!=
=typeof p)throw
Fa("itkn",p);k.push(f&&f.hasOwnProperty(p)?f[p]:d(p,g))}H(b)&&(b=b[l]);return
b.apply(c,k)}return{invoke:e,instantiate:function(a,b,c){var
d=Object.create((H(a)?a[a.length-1]:a).prototype||null);a=e(a,d,b,c);return
J(a)||G(a)?a:d},get:d,annotate:ab.$$annotate,has:function(a){return
p.hasOwnProperty(a+

"Provider")||b.hasOwnProperty(a)}}}a=!0===a;var l={},k=[],n=new
eb([],!0),p={$provide:{provider:c(d),factory:c(f),service:c(function(a,b){return
f(a,["$injector",function(a){return a.instantiate(b)}])}),value:c(function(a,b){return
f(a,ea(b),!1)}),constant:c(function(a,b){La(a,"constant");p[a]=b;u[a]=b}),decorator:
function(a,b){var c=q.get(a+"Provider"),d=c.$get;c.$get=function(){var
a=s.invoke(d,c);return
s.invoke(b,null,{$delegate:a})}}}},q=p.$injector=h(p,function(a,b){ca.isString(b)&&
k.push(b);

throw Fa("unpr",k.join(" <- "));}),u={},s=u.$injector=h(u,function(a,b){var


c=q.get(a+"Provider",b);return
s.invoke(c.$get,c,t,a)});r(g(b),function(a){s.invoke(a||E)});return s}function Be(){var
b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location",
"$rootScope",function(a,c,d){function e(a){var
b=null;Array.prototype.some.call(a,function(a){if("a"===va(a))return
b=a,!0});return b}function f(b){if(b){b.scrollIntoView();var
c;c=g.yOffset;G(c)?c=c():oc(c)?(c=c[0],c="fixed"!==

a.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):Y(c)||(c=0);c
&&(b=b.getBoundingClientRect().top,a.scrollBy(0,b-c))}else
a.scrollTo(0,0)}function g(){var
a=c.hash(),b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):

36
"top"===a&&f(null):f(null)}var h=a.document;b&&d.$watch(function(){return
c.hash()},function(a,b){a===b&&""===a||jf(function(){d.$evalAsync(g)})});return
g}]}function af(){this.$get=["$$rAF","$timeout",function(b,a){return
b.supported?function(a){return b(a)}:

function(b){return a(b,0,!1)}}]}function nf(b,a,c,d){function


e(a){try{a.apply(null,Za.call(arguments,1))}finally{if(m--
,0===m)for(;F.length;)try{F.pop()()}catch(b){c.error(b)}}}function f(a,b){(function
da(){r(Z,function(a){a()});L=b(da,a)})()}function g(){h();l()}function
h(){a:{try{B=u.state;break a}catch(a){}B=void
0}B=x(B)?null:B;ha(B,O)&&(B=O);O=B}function
l(){if(D!==n.url()||I!==B)D=n.url(),I=B,r(X,function(a){a(n.url(),B)})}function
k(a){try{return decodeURIComponent(a)}catch(b){return a}}

var
n=this,p=a[0],q=b.location,u=b.history,s=b.setTimeout,M=b.clearTimeout,v={};n.is
Mock=!1;var
m=0,F=[];n.$$completeOutstandingRequest=e;n.$$incOutstandingRequestCount=f
unction(){m++};n.notifyWhenNoOutstandingRequests=function(a){r(Z,function(a){
a()});0===m?a():F.push(a)};var
Z=[],L;n.addPollFn=function(a){x(L)&&f(100,s);Z.push(a);return a};var
B,I,D=q.href,S=a.find("base"),P=null;h();I=B;n.url=function(a,c,e){x(e)&&(e=null)
;q!==b.location&&(q=b.location);u!==b.history&&(u=b.history);if(a){var f=

I===e;if(D===a&&(!d.history||f))return n;var
g=D&&Ga(D)===Ga(a);D=a;I=e;!d.history||g&&f?(g||(P=a),c?q.replace(a):g?(c=q,e
=a.indexOf("#"),a=-
1===e?"":a.substr(e+1),c.hash=a):q.href=a):(u[c?"replaceState":"pushState"](e,""
,a),h(),I=B);return n}return P||q.href.replace(/%27/g,"'")};n.state=function(){return
B};var
X=[],ba=!1,O=null;n.onUrlChange=function(a){if(!ba){if(d.history)A(b).on("popstat
e",g);A(b).on("hashchange",g);ba=!0}X.push(a);return
a};n.$$checkUrlChange=l;n.baseHref=function(){var a=S.attr("href");

return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};var
fa={},y="",ka=n.baseHref();n.cookies=function(a,b){var
d,e,f,g;if(a)b===t?p.cookie=encodeURIComponent(a)+"=;path="+ka+";expires=Th
u, 01 Jan 1970 00:00:00
GMT":C(b)&&(d=(p.cookie=encodeURIComponent(a)+"="+encodeURIComponen

37
t(b)+";path="+ka).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or
overflowed because it was too large ("+d+" > 4096
bytes)!"));else{if(p.cookie!==y)for(y=p.cookie,d=y.split(";
"),fa={},f=0;f<d.length;f++)e=d[f],g=

e.indexOf("="),0<g&&(a=k(e.substring(0,g)),fa[a]===t&&(fa[a]=k(e.substring(g+1)
)));return fa}};n.defer=function(a,b){var c;m++;c=s(function(){delete
v[c];e(a)},b||0);v[c]=!0;return c};n.defer.cancel=function(a){return v[a]?(delete
v[a],M(a),e(E),!0):!1}}function
De(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return
new nf(b,d,a,c)}]}function Ee(){this.$get=function(){function b(b,d){function
e(a){a!=p&&(q?q==a&&(q=a.n):q=a,f(a.n,a.p),f(a,p),p=a,p.n=null)}function
f(a,b){a!=

b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw R("$cacheFactory")("iid",b);var


g=0,h=w({},d,{id:b}),l={},k=d&&d.capacity||Number.MAX_VALUE,n={},p=null,q=
null;return a[b]={put:function(a,b){if(k<Number.MAX_VALUE){var
c=n[a]||(n[a]={key:a});e(c)}if(!x(b))return a in
l||g++,l[a]=b,g>k&&this.remove(q.key),b},get:function(a){if(k<Number.MAX_VAL
UE){var b=n[a];if(!b)return;e(b)}return
l[a]},remove:function(a){if(k<Number.MAX_VALUE){var
b=n[a];if(!b)return;b==p&&(p=b.p);b==q&&(q=b.n);f(b.n,b.p);delete n[a]}delete
l[a];

g--
},removeAll:function(){l={};g=0;n={};p=q=null},destroy:function(){n=h=l=null;delet
e a[b]},info:function(){return w({},h,{size:g})}}}var a={};b.info=function(){var
b={};r(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return
a[b]};return b}}function Ve(){this.$get=["$cacheFactory",function(b){return
b("templates")}]}function yc(b,a){function c(a,b){var
c=/^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/,d={};r(a,function(a,e){var
f=a.match(c);if(!f)throw la("iscp",b,e,a);d[e]={mode:f[1][0],collection:"*"===

f[2],optional:"?"===f[3],attrName:f[4]||e}});return d}var
d={},e=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,f=/(([\w\-
]+)(?:\:([^;]+))?;?)/,g=Gd("ngSrc,ngSrcset,src,srcset"),h=/^(?:(\^\^?)?(\?)?(\^\^?)?)
?/,l=/^(on[a-z]+|formaction)$/;this.directive=function
p(a,e){La(a,"directive");C(a)?(Rb(e,"directiveFactory"),d.hasOwnProperty(a)||(d[a
]=[],b.factory(a+"Directive",["$injector","$exceptionHandler",function(b,e){var

38
f=[];r(d[a],function(d,g){try{var
h=b.invoke(d);G(h)?h={compile:ea(h)}:!h.compile&&h.link&&

(h.compile=ea(h.link));h.priority=h.priority||0;h.index=g;h.name=h.name||a;h.requi
re=h.require||h.controller&&h.name;h.restrict=h.restrict||"EA";J(h.scope)&&(h.$$
isolateBindings=c(h.scope,h.name));f.push(h)}catch(k){e(k)}});return
f}])),d[a].push(e)):r(a,mc(p));return
this};this.aHrefSanitizationWhitelist=function(b){return
y(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.img
SrcSanitizationWhitelist=function(b){return
y(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};

var k=!0;this.debugInfoEnabled=function(a){return
y(a)?(k=a,this):k};this.$get=["$injector","$interpolate","$exceptionHandler","$te
mplateRequest","$parse","$controller","$rootScope","$document","$sce","$ani
mate","$$sanitizeUri",function(a,b,c,s,M,v,m,F,Z,L,B){function
I(a,b){try{a.addClass(b)}catch(c){}}function D(a,b,c,d,e){a instanceof
A||(a=A(a));r(a,function(b,c){b.nodeType==pb&&b.nodeValue.match(/\S+/)&&(a[c]
=A(b).wrap("<span></span>").parent()[0])});var
f=S(a,b,a,c,d,e);D.$$addScopeClass(a);

var g=null;return function(b,c,d){Rb(b,"scope");d=d||{};var


e=d.parentBoundTranscludeFn,h=d.transcludeControllers;d=d.futureParentEleme
nt;e&&e.$$boundTransclude&&(e=e.$$boundTransclude);g||(g=(d=d&&d[0])?"for
eignobject"!==va(d)&&d.toString().match(/SVG/)?"svg":"html":"html");d="html
"!==g?A(Xb(g,A("<div>").append(a).html())):c?Ka.clone.call(a):a;if(h)for(var k in
h)d.data("$"+k+"Controller",h[k].instance);D.$$addScopeInfo(d,b);c&&c(d,b);f&
&f(b,d,d,e);return d}}function S(a,b,c,d,e,f){function g(a,

c,d,e){var
f,k,l,q,p,s,M;if(m)for(M=Array(c.length),q=0;q<h.length;q+=3)f=h[q],M[f]=c[f];else
M=c;q=0;for(p=h.length;q<p;)k=M[h[q++]],c=h[q++],f=h[q++],c?(c.scope?(l=a.$ne
w(),D.$$addScopeInfo(A(k),l)):l=a,s=c.transcludeOnThisElement?P(a,c.transclude,e
,c.elementTranscludeOnThisElement):!c.templateOnThisElement&&e?e:!e&&b?P(
a,b):null,c(f,l,k,d,s)):f&&f(a,k.childNodes,t,e)}for(var
h=[],k,l,q,p,m,s=0;s<a.length;s++){k=new
Yb;l=X(a[s],[],k,0===s?d:t,e);(f=l.length?fa(l,a[s],k,b,c,null,[],[],f):null)&&

39
f.scope&&D.$$addScopeClass(k.$$element);k=f&&f.terminal||!(q=a[s].childNodes)||
!q.length?null:S(q,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.t
ransclude:b);if(f||k)h.push(s,f,k),p=!0,m=m||f;f=null}return p?g:null}function
P(a,b,c,d){return function(d,e,f,g,h){d||(d=a.$new(!1,h),d.$$transcluded=!0);return
b(d,e,{parentBoundTranscludeFn:c,transcludeControllers:f,futureParentElement:g
})}}function X(a,b,c,d,g){var h=c.$attr,k;switch(a.nodeType){case
qa:ka(b,xa(va(a)),"E",d,g);for(var l,

q,p,m=a.attributes,s=0,M=m&&m.length;s<M;s++){var
u=!1,L=!1;l=m[s];k=l.name;q=N(l.value);l=xa(k);if(p=U.test(l))k=k.replace(Sc,"").s
ubstr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});var
B=l.replace(/(Start|End)$/,"");x(B)&&l===B+"Start"&&(u=k,L=k.substr(0,k.lengt
h-5)+"end",k=k.substr(0,k.length-
6));l=xa(k.toLowerCase());h[l]=k;if(p||!c.hasOwnProperty(l))c[l]=q,Nc(a,l)&&(c[l]=!
0);Oa(a,b,q,l,p);ka(b,l,"A",d,g,u,L)}a=a.className;J(a)&&(a=a.animVal);if(C(a)&
&""!==a)for(;k=f.exec(a);)l=xa(k[2]),

ka(b,l,"C",d,g)&&(c[l]=N(k[3])),a=a.substr(k.index+k[0].length);break;case
pb:za(b,a.nodeValue);break;case
8:try{if(k=e.exec(a.nodeValue))l=xa(k[1]),ka(b,l,"M",d,g)&&(c[l]=N(k[2]))}catch(v)
{}}b.sort(da);return b}function ba(a,b,c){var
d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw
la("uterdir",b,c);a.nodeType==qa&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&
&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return A(d)}function
O(a,b,c){return function(d,e,f,g,h){e=ba(e[0],

b,c);return a(d,e,f,g,h)}}function fa(a,d,e,f,g,k,l,p,m){function


s(a,b,c,d){if(a){c&&(a=O(a,c,d));a.require=K.require;a.directiveName=da;if(P===
K||K.$$isolateScope)a=Y(a,{isolateScope:!0});l.push(a)}if(b){c&&(b=O(b,c,d));b.req
uire=K.require;b.directiveName=da;if(P===K||K.$$isolateScope)b=Y(b,{isolateSco
pe:!0});p.push(b)}}function L(a,b,c,d){var
e,f="data",g=!1,k=c,l;if(C(b)){l=b.match(h);b=b.substring(l[0].length);l[3]&&(l[1]?l
[3]=null:l[1]=l[3]);"^"===l[1]?f="inheritedData":"^^"===l[1]&&(f="inheritedDat
a",

k=c.parent());"?"===l[2]&&(g=!0);e=null;d&&"data"===f&&(e=d[b])&&(e=e.inst
ance);e=e||k[f]("$"+b+"Controller");if(!e&&!g)throw la("ctreq",b,a);return
e||null}H(b)&&(e=[],r(b,function(b){e.push(L(a,b,c,d))}));return e}function
B(a,c,f,g,h){function k(a,b,c){var

40
d;Va(a)||(c=b,b=a,a=t);E&&(d=F);c||(c=E?X.parent():X);return h(a,b,d,c,Wb)}var
m,s,u,I,F,gb,X,O;d===f?(O=e,X=e.$$element):(X=A(f),O=new
Yb(X,e));P&&(I=c.$new(!0));h&&(gb=k,gb.$$boundTransclude=h);S&&(Z={},F={
},r(S,function(a){var b={$scope:a===

P||a.$$isolateScope?I:c,$element:X,$attrs:O,$transclude:gb};u=a.controller;"@"==
u&&(u=O[a.name]);b=v(u,b,!0,a.controllerAs);F[a.name]=b;E||X.data("$"+a.name
+"Controller",b.instance);Z[a.name]=b}));if(P){D.$$addScopeInfo(X,I,!0,!(ma&&(
ma===P||ma===P.$$originalDirective)));D.$$addScopeClass(X,!0);g=Z&&Z[P.nam
e];var
ba=I;g&&g.identifier&&!0===P.bindToController&&(ba=g.instance);r(I.$$isolate
Bindings=P.$$isolateBindings,function(a,d){var
e=a.attrName,f=a.optional,g,h,k,l;switch(a.mode){case "@":O.$observe(e,

function(a){ba[d]=a});O.$$observers[e].$$scope=c;O[e]&&(ba[d]=b(O[e])(c));break
;case "=":if(f&&!O[e])break;h=M(O[e]);l=h.literal?ha:function(a,b){return
a===b||a!==a&&b!==b};k=h.assign||function(){g=ba[d]=h(c);throw
la("nonassign",O[e],P.name);};g=ba[d]=h(c);f=function(a){l(a,ba[d])||(l(a,g)?k(c,a=
ba[d]):ba[d]=a);return
g=a};f.$stateful=!0;f=a.collection?c.$watchCollection(O[e],f):c.$watch(M(O[e],f),nul
l,h.literal);I.$on("$destroy",f);break;case "&":h=M(O[e]),ba[d]=function(a){return
h(c,a)}}})}Z&&

(r(Z,function(a){a()}),Z=null);g=0;for(m=l.length;g<m;g++)s=l[g],$(s,s.isolateScope?
I:c,X,O,s.require&&L(s.directiveName,s.require,X,F),gb);var
Wb=c;P&&(P.template||null===P.templateUrl)&&(Wb=I);a&&a(Wb,f.childNodes,
t,h);for(g=p.length-1;0<=g;g--
)s=p[g],$(s,s.isolateScope?I:c,X,O,s.require&&L(s.directiveName,s.require,X,F),gb)
}m=m||{};for(var I=-
Number.MAX_VALUE,F,S=m.controllerDirectives,Z,P=m.newIsolateScopeDirectiv
e,ma=m.templateDirective,fa=m.nonTlbTranscludeDirective,ka=!1,x=!1,E=m.hasEl
ementTranscludeDirective,

w=e.$$element=A(d),K,da,V,fb=f,za,z=0,Q=a.length;z<Q;z++){K=a[z];var
Oa=K.$$start,U=K.$$end;Oa&&(w=ba(d,Oa,U));V=t;if(I>K.priority)break;if(V=K.
scope)K.templateUrl||(J(V)?(Na("new/isolated
scope",P||F,K,w),P=K):Na("new/isolated
scope",P,K,w)),F=F||K;da=K.name;!K.templateUrl&&K.controller&&(V=K.contro
ller,S=S||{},Na("'"+da+"'

41
controller",S[da],K,w),S[da]=K);if(V=K.transclude)ka=!0,K.$$tlb||(Na("transclusio
n",fa,K,w),fa=K),"element"==V?(E=!0,I=K.priority,V=w,w=e.$$element=A(W.crea
teComment(" "+da+": "+

e[da]+"
")),d=w[0],T(g,Za.call(V,0),d),fb=D(V,f,I,k&&k.name,{nonTlbTranscludeDirective:
fa})):(V=A(Ub(d)).contents(),w.empty(),fb=D(V,f));if(K.template)if(x=!0,Na("templ
ate",ma,K,w),ma=K,V=G(K.template)?K.template(w,e):K.template,V=Tc(V),K.repl
ace){k=K;V=Sb.test(V)?Uc(Xb(K.templateNamespace,N(V))):[];d=V[0];if(1!=V.leng
th||d.nodeType!==qa)throw
la("tplrt",da,"");T(g,w,d);Q={$attr:{}};V=X(d,[],Q);var aa=a.splice(z+1,a.length-
(z+1));P&&y(V);a=a.concat(V).concat(aa);R(e,Q);Q=a.length}else
w.html(V);if(K.templateUrl)x=

!0,Na("template",ma,K,w),ma=K,K.replace&&(k=K),B=of(a.splice(z,a.length-
z),w,e,g,ka&&fb,l,p,{controllerDirectives:S,newIsolateScopeDirective:P,templateDir
ective:ma,nonTlbTranscludeDirective:fa}),Q=a.length;else
if(K.compile)try{za=K.compile(w,e,fb),G(za)?s(null,za,Oa,U):za&&s(za.pre,za.post,
Oa,U)}catch(pf){c(pf,wa(w))}K.terminal&&(B.terminal=!0,I=Math.max(I,K.priorit
y))}B.scope=F&&!0===F.scope;B.transcludeOnThisElement=ka;B.elementTransclu
deOnThisElement=E;B.templateOnThisElement=x;B.transclude=fb;

m.hasElementTranscludeDirective=E;return B}function y(a){for(var


b=0,c=a.length;b<c;b++)a[b]=Ob(a[b],{$$isolateScope:!0})}function
ka(b,e,f,g,h,k,l){if(e===h)return null;h=null;if(d.hasOwnProperty(e)){var
q;e=a.get(e+"Directive");for(var
m=0,s=e.length;m<s;m++)try{q=e[m],(g===t||g>q.priority)&&-
1!=q.restrict.indexOf(f)&&(k&&(q=Ob(q,{$$start:k,$$end:l})),b.push(q),h=q)}catc
h(M){c(M)}}return h}function x(b){if(d.hasOwnProperty(b))for(var
c=a.get(b+"Directive"),e=0,f=c.length;e<f;e++)if(b=c[e],b.multiElement)return!0;

return!1}function R(a,b){var
c=b.$attr,d=a.$attr,e=a.$$element;r(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[
e]!==d&&(d+=("style"===e?";":"
")+b[e]),a.$set(e,d,!0,c[e]))});r(b,function(b,f){"class"==f?(I(e,b),a["class"]=(a["clas
s"]?a["class"]+"
":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+"
;":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function

42
of(a,b,c,d,e,f,g,h){var
k=[],l,q,p=b[0],m=a.shift(),M=Ob(m,{templateUrl:null,transclude:null,

replace:null,$$originalDirective:m}),u=G(m.templateUrl)?m.templateUrl(b,c):m.te
mplateUrl,L=m.templateNamespace;b.empty();s(Z.getTrustedResourceUrl(u)).then
(function(s){var
B,v;s=Tc(s);if(m.replace){s=Sb.test(s)?Uc(Xb(L,N(s))):[];B=s[0];if(1!=s.length||B.nod
eType!==qa)throw la("tplrt",m.name,u);s={$attr:{}};T(d,b,B);var
D=X(B,[],s);J(m.scope)&&y(D);a=D.concat(a);R(c,s)}else
B=p,b.html(s);a.unshift(M);l=fa(a,B,c,e,b,m,f,g,h);r(d,function(a,c){a==B&&(d[c]=b
[0])});for(q=S(b[0].childNodes,e);k.length;){s=

k.shift();v=k.shift();var
F=k.shift(),O=k.shift(),D=b[0];if(!s.$$destroyed){if(v!==p){var
Z=v.className;h.hasElementTranscludeDirective&&m.replace||(D=Ub(B));T(F,A(v
),D);I(A(D),Z)}v=l.transcludeOnThisElement?P(s,l.transclude,O):O;l(q,s,D,d,v)}}k=
null});return
function(a,b,c,d,e){a=e;b.$$destroyed||(k?k.push(b,c,d,a):(l.transcludeOnThisEleme
nt&&(a=P(b,l.transclude,e)),l(q,b,c,d,a)))}}function da(a,b){var c=b.priority-
a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-
b.index}function Na(a,

b,c,d){if(b)throw la("multidir",b.name,c.name,a,wa(d));}function za(a,c){var


d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var
b=!!a.length;b&&D.$$addBindingClass(a);return function(a,c){var
e=c.parent();b||D.$$addBindingClass(e);D.$$addBindingInfo(e,d.expressions);a.$wa
tch(d,function(a){c[0].nodeValue=a})}}})}function
Xb(a,b){a=z(a||"html");switch(a){case "svg":case "math":var
c=W.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return
c.childNodes[0].childNodes;default:return b}}

function Q(a,b){if("srcdoc"==b)return Z.HTML;var


c=va(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"n
gSrc"==b))return Z.RESOURCE_URL}function Oa(a,c,d,e,f){var
h=Q(a,e);f=g[e]||f;var
k=b(d,!0,h,f);if(k){if("multiple"===e&&"select"===va(a))throw
la("selmulti",wa(a));c.push({priority:100,compile:function(){return{pre:function(a,
c,g){c=g.$$observers||(g.$$observers={});if(l.test(e))throw la("nodomevents");var
m=g[e];m!==d&&(k=m&&b(m,!0,h,f),d=m);k&&(g[e]=k(a),(c[e]||(c[e]=[])).$$inter=

43
!0,(g.$$observers&&g.$$observers[e].$$scope||a).$watch(k,function(a,b){"class"===
e&&a!=b?g.$updateClass(a,b):g.$set(e,a)}))}}}})}}function T(a,b,c){var
d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){
a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete
a[g];a.length-=e-
1;a.context===d&&(a.context=c);break}f&&f.replaceChild(c,d);a=W.createDocum
entFragment();a.appendChild(d);A(c).data(A(d).data());ta?(Qb=!0,ta.cleanData([d]
)):delete A.cache[d[A.expando]];

d=1;for(e=b.length;d<e;d++)f=b[d],A(f).remove(),a.appendChild(f),delete
b[d];b[0]=c;b.length=1}function Y(a,b){return w(function(){return
a.apply(null,arguments)},a,b)}function
$(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,wa(d))}}var Yb=function(a,b){if(b){var
c=Object.keys(b),d,e,f;d=0;for(e=c.length;d<e;d++)f=c[d],this[f]=b[f]}else
this.$attr={};this.$$element=a};Yb.prototype={$normalize:xa,$addClass:function(a
){a&&0<a.length&&L.addClass(this.$$element,a)},$removeClass:function(a){a&&
0<a.length&&

L.removeClass(this.$$element,a)},$updateClass:function(a,b){var
c=Vc(a,b);c&&c.length&&L.addClass(this.$$element,c);(c=Vc(b,a))&&c.length&&
L.removeClass(this.$$element,c)},$set:function(a,b,d,e){var
f=this.$$element[0],g=Nc(f,a),h=kf(f,a),f=a;g?(this.$$element.prop(a,b),e=g):h&&(t
his[h]=b,f=h);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=vc(a,"-
"));g=va(this.$$element);if("a"===g&&"href"===a||"img"===g&&"src"===a)this
[a]=b=B(b,"src"===a);else if("img"===g&&"srcset"===a){for(var g=

"",h=N(b),k=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,k=/\s/.test(h)?k:/(,)/,h=h.split(k),k=M
ath.floor(h.length/2),l=0;l<k;l++)var q=2*l,g=g+B(N(h[q]),!0),g=g+("
"+N(h[q+1]));h=N(h[2*l]).split(/\s/);g+=B(N(h[0]),!0);2===h.length&&(g+="
"+N(h[1]));this[a]=b=g}!1!==d&&(null===b||b===t?this.$$element.removeAttr(e):t
his.$$element.attr(e,b));(a=this.$$observers)&&r(a[f],function(a){try{a(b)}catch(d){
c(d)}})},$observe:function(a,b){var
c=this,d=c.$$observers||(c.$$observers=ia()),e=d[a]||(d[a]=[]);e.push(b);

m.$evalAsync(function(){!e.$$inter&&c.hasOwnProperty(a)&&b(c[a])});return
function(){Xa(e,b)}}};var
V=b.startSymbol(),ma=b.endSymbol(),Tc="{{"==V||"}}"==ma?ra:function(a){retu
rn a.replace(/\{\{/g,V).replace(/}}/g,ma)},U=/^ngAttr[A-
Z]/;D.$$addBindingInfo=k?function(a,b){var

44
c=a.data("$binding")||[];H(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:E;D.$
$addBindingClass=k?function(a){I(a,"ng-
binding")}:E;D.$$addScopeInfo=k?function(a,b,c,d){a.data(c?d?"$isolateScopeNoT
emplate":"$isolateScope":"$scope",

b)}:E;D.$$addScopeClass=k?function(a,b){I(a,b?"ng-isolate-scope":"ng-
scope")}:E;return D}]}function xa(b){return db(b.replace(Sc,""))}function
Vc(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),f=0;a:for(;f<d.length;f++){for(var
g=d[f],h=0;h<e.length;h++)if(g==e[h])continue a;c+=(0<c.length?" ":"")+g}return
c}function Uc(b){b=A(b);var a=b.length;if(1>=a)return b;for(;a--
;)8===b[a].nodeType&&qf.call(b,a,1);return b}function Fe(){var
b={},a=!1,c=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,c){La(a,

"controller");J(a)?w(b,a):b[a]=c};this.allowGlobals=function(){a=!0};this.$get=["$i
njector","$window",function(d,e){function f(a,b,c,d){if(!a||!J(a.$scope))throw
R("$controller")("noscp",d,b);a.$scope[b]=c}return function(g,h,l,k){var
n,p,q;l=!0===l;k&&C(k)&&(q=k);if(C(g)){k=g.match(c);if(!k)throw
rf("ctrlfmt",g);p=k[1];q=q||k[3];g=b.hasOwnProperty(p)?b[p]:xc(h.$scope,p,!0)||(a?
xc(e,p,!0):t);sb(g,p,!0)}if(l)return l=(H(g)?g[g.length-
1]:g).prototype,n=Object.create(l||null),q&&f(h,q,n,p||g.name),w(function(){d.invok
e(g,

n,h,p);return
n},{instance:n,identifier:q});n=d.instantiate(g,h,p);q&&f(h,q,n,p||g.name);return
n}}]}function Ge(){this.$get=["$window",function(b){return
A(b.document)}]}function He(){this.$get=["$log",function(b){return
function(a,c){b.error.apply(b,arguments)}}]}function Zb(b,a){if(C(b)){var
c=b.replace(sf,"").trim();if(c){var d=a("Content-
Type");(d=d&&0===d.indexOf(Wc))||(d=(d=c.match(tf))&&uf[d[0]].test(c));d&&(b
=qc(c))}}return b}function Xc(b){var a=ia(),c,d,e;if(!b)return a;r(b.split("\n"),

function(b){e=b.indexOf(":");c=z(N(b.substr(0,e)));d=N(b.substr(e+1));c&&(a[c]=a
[c]?a[c]+", "+d:d)});return a}function Yc(b){var a=J(b)?b:t;return
function(c){a||(a=Xc(b));return c?(c=a[z(c)],void 0===c&&(c=null),c):a}}function
Zc(b,a,c,d){if(G(d))return d(b,a,c);r(d,function(d){b=d(b,a,c)});return b}function
Ke(){var
b=this.defaults={transformResponse:[Zb],transformRequest:[function(a){return
J(a)&&"[object File]"!==Ca.call(a)&&"[object Blob]"!==Ca.call(a)&&"[object
FormData]"!==Ca.call(a)?$a(a):

45
a}],headers:{common:{Accept:"application/json, text/plain,
*/*"},post:sa($b),put:sa($b),patch:sa($b)},xsrfCookieName:"XSRF-
TOKEN",xsrfHeaderName:"X-XSRF-
TOKEN"},a=!1;this.useApplyAsync=function(b){return y(b)?(a=!!b,this):a};var
c=this.interceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$r
ootScope","$q","$injector",function(d,e,f,g,h,l){function k(a){function c(a){var
b=w({},a);b.data=a.data?Zc(a.data,a.headers,a.status,e.transformResponse):a.data;
a=a.status;return 200<=a&&300>a?

b:h.reject(b)}function d(a){var
b,c={};r(a,function(a,d){G(a)?(b=a(),null!=b&&(c[d]=b)):c[d]=a});return
c}if(!ca.isObject(a))throw R("$http")("badreq",a);var
e=w({method:"get",transformRequest:b.transformRequest,transformResponse:b.tr
ansformResponse},a);e.headers=function(a){var
c=b.headers,e=w({},a.headers),f,g,c=w({},c.common,c[z(a.method)]);a:for(f in
c){a=z(f);for(g in e)if(z(g)===a)continue a;e[f]=c[f]}return
d(e)}(a);e.method=ub(e.method);var f=[function(a){var
d=a.headers,e=Zc(a.data,Yc(d),

t,a.transformRequest);x(e)&&r(d,function(a,b){"content-type"===z(b)&&delete
d[b]});x(a.withCredentials)&&!x(b.withCredentials)&&(a.withCredentials=b.withC
redentials);return
n(a,e).then(c,c)},t],g=h.when(e);for(r(u,function(a){(a.request||a.requestError)&&f.
unshift(a.request,a.requestError);(a.response||a.responseError)&&f.push(a.respons
e,a.responseError)});f.length;){a=f.shift();var
k=f.shift(),g=g.then(a,k)}g.success=function(a){g.then(function(b){a(b.data,b.status,
b.headers,e)});return g};g.error=

function(a){g.then(null,function(b){a(b.data,b.status,b.headers,e)});return g};return
g}function n(c,f){function l(b,c,d,e){function
f(){m(c,b,d,e)}I&&(200<=b&&300>b?I.put(P,[b,c,Xc(d),e]):I.remove(P));a?g.$apply
Async(f):(f(),g.$$phase||g.$apply())}function
m(a,b,d,e){b=Math.max(b,0);(200<=b&&300>b?L.resolve:L.reject)({data:a,status:b
,headers:Yc(d),config:c,statusText:e})}function
n(a){m(a.data,a.status,sa(a.headers()),a.statusText)}function u(){var
a=k.pendingRequests.indexOf(c);-1!==a&&k.pendingRequests.splice(a,

1)}var
L=h.defer(),B=L.promise,I,D,S=c.headers,P=p(c.url,c.params);k.pendingRequests.p

46
ush(c);B.then(u,u);!c.cache&&!b.cache||!1===c.cache||"GET"!==c.method&&"JSO
NP"!==c.method||(I=J(c.cache)?c.cache:J(b.cache)?b.cache:q);I&&(D=I.get(P),y(D)
?D&&G(D.then)?D.then(n,n):H(D)?m(D[1],D[0],sa(D[2]),D[3]):m(D,200,{},"OK"):I
.put(P,B));x(D)&&((D=$c(c.url)?e.cookies()[c.xsrfCookieName||b.xsrfCookieName]:
t)&&(S[c.xsrfHeaderName||b.xsrfHeaderName]=D),d(c.method,P,f,l,S,c.timeout,c.w
ithCredentials,c.responseType));

return B}function p(a,b){if(!b)return a;var


c=[];Ed(b,function(a,b){null===a||x(a)||(H(a)||(a=[a]),r(a,function(a){J(a)&&(a=ga(a
)?a.toISOString():$a(a));c.push(Ea(b)+"="+Ea(a))}))});0<c.length&&(a+=(-
1==a.indexOf("?")?"?":"&")+c.join("&"));return a}var
q=f("$http"),u=[];r(c,function(a){u.unshift(C(a)?l.get(a):l.invoke(a))});k.pendingRe
quests=[];(function(a){r(arguments,function(a){k[a]=function(b,c){return
k(w(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){r(arg
uments,function(a){k[a]=

function(b,c,d){return
k(w(d||{},{method:a,url:b,data:c}))}})})("post","put","patch");k.defaults=b;return
k}]}function vf(){return new Q.XMLHttpRequest}function
Le(){this.$get=["$browser","$window","$document",function(b,a,c){return
wf(b,vf,b.defer,a.angular.callbacks,c[0])}]}function wf(b,a,c,d,e){function
f(a,b,c){var
f=e.createElement("script"),n=null;f.type="text/javascript";f.src=a;f.async=!0;n=fu
nction(a){f.removeEventListener("load",n,!1);f.removeEventListener("error",n,!1);
e.body.removeChild(f);

f=null;var g=-
1,u="unknown";a&&("load"!==a.type||d[b].called||(a={type:"error"}),u=a.type,g=
"error"===a.type?404:200);c&&c(g,u)};f.addEventListener("load",n,!1);f.addEven
tListener("error",n,!1);e.body.appendChild(f);return n}return
function(e,h,l,k,n,p,q,u){function s(){m&&m();F&&F.abort()}function
M(a,d,e,f,g){L!==t&&c.cancel(L);m=F=null;a(d,e,f,g);b.$$completeOutstandingReq
uest(E)}b.$$incOutstandingRequestCount();h=h||b.url();if("jsonp"==z(e)){var
v="_"+(d.counter++).toString(36);d[v]=function(a){d[v].data=

a;d[v].called=!0};var
m=f(h.replace("JSON_CALLBACK","angular.callbacks."+v),v,function(a,b){M(k,
a,d[v].data,"",b);d[v]=E})}else{var
F=a();F.open(e,h,!0);r(n,function(a,b){y(a)&&F.setRequestHeader(b,a)});F.onload=

47
function(){var a=F.statusText||"",b="response"in
F?F.response:F.responseText,c=1223===F.status?204:F.status;0===c&&(c=b?200:
"file"==Aa(h).protocol?404:0);M(k,c,b,F.getAllResponseHeaders(),a)};e=function(){
M(k,-
1,null,null,"")};F.onerror=e;F.onabort=e;q&&(F.withCredentials=!0);if(u)try{F.res
ponseType=

u}catch(Z){if("json"!==u)throw Z;}F.send(l||null)}if(0<p)var L=c(s,p);else


p&&G(p.then)&&p.then(s)}}function Ie(){var
b="{{",a="}}";this.startSymbol=function(a){return
a?(b=a,this):b};this.endSymbol=function(b){return
b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){fu
nction f(a){return"\\\\\\"+a}function g(f,g,u,s){function M(c){return
c.replace(k,b).replace(n,a)}function v(a){try{var
b=a;a=u?e.getTrusted(u,b):e.valueOf(b);var c;if(s&&!y(a))c=a;else if(null==a)c="";

else{switch(typeof a){case "string":break;case


"number":a=""+a;break;default:a=$a(a)}c=a}return
c}catch(g){c=ac("interr",f,g.toString()),d(c)}}s=!!s;for(var
m,F,r=0,L=[],B=[],I=f.length,D=[],S=[];r<I;)if(-1!=(m=f.indexOf(b,r))&&-
1!=(F=f.indexOf(a,m+h)))r!==m&&D.push(M(f.substring(r,m))),r=f.substring(m+h,
F),L.push(r),B.push(c(r,v)),r=F+l,S.push(D.length),D.push("");else{r!==I&&D.push
(M(f.substring(r)));break}if(u&&1<D.length)throw
ac("noconcat",f);if(!g||L.length){var P=function(a){for(var b=0,c=

L.length;b<c;b++){if(s&&x(a[b]))return;D[S[b]]=a[b]}return D.join("")};return
w(function(a){var b=0,c=L.length,e=Array(c);try{for(;b<c;b++)e[b]=B[b](a);return
P(e)}catch(g){a=ac("interr",f,g.toString()),d(a)}},{exp:f,expressions:L,$$watchDeleg
ate:function(a,b,c){var d;return a.$watchGroup(B,function(c,e){var
f=P(c);G(b)&&b.call(this,f,c!==e?d:f,a);d=f},c)}})}}var h=b.length,l=a.length,k=new
RegExp(b.replace(/./g,f),"g"),n=new
RegExp(a.replace(/./g,f),"g");g.startSymbol=function(){return b};g.endSymbol=

function(){return a};return g}]}function


Je(){this.$get=["$rootScope","$window","$q","$$q",function(b,a,c,d){function
e(e,h,l,k){var
n=a.setInterval,p=a.clearInterval,q=0,u=y(k)&&!k,s=(u?d:c).defer(),M=s.promise;l
=y(l)?l:0;M.then(null,null,e);M.$$intervalId=n(function(){s.notify(q++);0<l&&q>=l
&&(s.resolve(q),p(M.$$intervalId),delete

48
f[M.$$intervalId]);u||b.$apply()},h);f[M.$$intervalId]=s;return M}var
f={};e.cancel=function(b){return b&&b.$$intervalId in
f?(f[b.$$intervalId].reject("canceled"),a.clearInterval(b.$$intervalId),

delete f[b.$$intervalId],!0):!1};return e}]}function


Rd(){this.$get=function(){return{id:"en-
us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{
minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-
",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",pos
Suf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},D
ATETIME_FORMATS:{MONTH:"January February March April May June July
August September October November December".split(" "),SHORTMONTH:"Jan
Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),

DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split("


"),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split("
"),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy
h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d,
y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss
a",shortTime:"h:mm a",ERANAMES:["Before Christ","Anno
Domini"],ERAS:["BC","AD"]},pluralCat:function(b){return
1===b?"one":"other"}}}}function bc(b){b=b.split("/");for(var a=b.length;a--
;)b[a]=qb(b[a]);

return b.join("/")}function ad(b,a){var


c=Aa(b);a.$$protocol=c.protocol;a.$$host=c.hostname;a.$$port=aa(c.port)||xf[c.prot
ocol]||null}function bd(b,a){var c="/"!==b.charAt(0);c&&(b="/"+b);var
d=Aa(b);a.$$path=decodeURIComponent(c&&"/"===d.pathname.charAt(0)?d.pat
hname.substring(1):d.pathname);a.$$search=sc(d.search);a.$$hash=decodeURICo
mponent(d.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}
function ya(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ga(b){var
a=b.indexOf("#");

return-1==a?b:b.substr(0,a)}function Fb(b){return
b.replace(/(#.+)|#$/,"$1")}function cc(b){return
b.substr(0,Ga(b).lastIndexOf("/")+1)}function dc(b,a){this.$$html5=!0;a=a||"";var
c=cc(b);ad(b,this);this.$$parse=function(a){var b=ya(c,a);if(!C(b))throw
Gb("ipthprfx",a,c);bd(b,this);this.$$path||(this.$$path="/");this.$$compose()};this.$
$compose=function(){var

49
a=Pb(this.$$search),b=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$pat
h)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$parseLinkUrl=

function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var
f,g;(f=ya(b,d))!==t?(g=f,g=(f=ya(a,f))!==t?c+(ya("/",f)||f):b+g):(f=ya(c,d))!==t?g=c+f
:c==d+"/"&&(g=c);g&&this.$$parse(g);return!!g}}function ec(b,a){var
c=cc(b);ad(b,this);this.$$parse=function(d){d=ya(b,d)||ya(c,d);var
e;"#"===d.charAt(0)?(e=ya(a,d),x(e)&&(e=d)):e=this.$$html5?d:"";bd(e,this);d=th
is.$$path;var f=/^\/[A-
Z]:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));f.exec(e)||(d=(e=f.exec(d))?e[1]:d);
this.$$path=d;this.$$compose()};

this.$$compose=function(){var
c=Pb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$pat
h)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$parseLink
Url=function(a,c){return Ga(b)==Ga(a)?(this.$$parse(a),!0):!1}}function
cd(b,a){this.$$html5=!0;ec.apply(this,arguments);var
c=cc(b);this.$$parseLinkUrl=function(d,e){if(e&&"#"===e[0])return
this.hash(e.slice(1)),!0;var
f,g;b==Ga(d)?f=d:(g=ya(c,d))?f=b+a+g:c===d+"/"&&(f=c);f&&this.$$parse(f);retu
rn!!f};this.$$compose=

function(){var
c=Pb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$pat
h)+(c?"?"+c:"")+e;this.$$absUrl=b+a+this.$$url}}function Hb(b){return
function(){return this[b]}}function dd(b,a){return function(c){if(x(c))return
this[b];this[b]=a(c);this.$$compose();return this}}function Me(){var
b="",a={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(a){re
turn y(a)?(b=a,this):b};this.html5Mode=function(b){return
Wa(b)?(a.enabled=b,this):J(b)?(Wa(b.enabled)&&(a.enabled=

b.enabled),Wa(b.requireBase)&&(a.requireBase=b.requireBase),Wa(b.rewriteLink
s)&&(a.rewriteLinks=b.rewriteLinks),this):a};this.$get=["$rootScope","$browser",
"$sniffer","$rootElement","$window",function(c,d,e,f,g){function h(a,b,c){var
e=k.url(),f=k.$$state;try{d.url(a,b,c),k.$$state=d.state()}catch(g){throw
k.url(e),k.$$state=f,g;}}function
l(a,b){c.$broadcast("$locationChangeSuccess",k.absUrl(),a,k.$$state,b)}var
k,n;n=d.baseHref();var p=d.url(),q;if(a.enabled){if(!n&&a.requireBase)throw
Gb("nobase");

50
q=p.substring(0,p.indexOf("/",p.indexOf("//")+2))+(n||"/");n=e.history?dc:cd}else
q=Ga(p),n=ec;k=new n(q,"#"+b);k.$$parseLinkUrl(p,p);k.$$state=d.state();var
u=/^\s*(javascript|mailto):/i;f.on("click",function(b){if(a.rewriteLinks&&!b.ctrlKe
y&&!b.metaKey&&!b.shiftKey&&2!=b.which&&2!=b.button){for(var
e=A(b.target);"a"!==va(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var
h=e.prop("href"),l=e.attr("href")||e.attr("xlink:href");J(h)&&"[object
SVGAnimatedString]"===h.toString()&&(h=Aa(h.animVal).href);

u.test(h)||!h||e.attr("target")||b.isDefaultPrevented()||!k.$$parseLinkUrl(h,l)||(b.prev
entDefault(),k.absUrl()!=d.url()&&(c.$apply(),g.angular["ff-684208-
preventDefault"]=!0))}});Fb(k.absUrl())!=Fb(p)&&d.url(k.absUrl(),!0);var
s=!0;d.onUrlChange(function(a,b){c.$evalAsync(function(){var
d=k.absUrl(),e=k.$$state,f;k.$$parse(a);k.$$state=b;f=c.$broadcast("$locationChan
geStart",a,d,b,e).defaultPrevented;k.absUrl()===a&&(f?(k.$$parse(d),k.$$state=e,h
(d,!1,e)):(s=!1,l(d,e)))});c.$$phase||c.$digest()});

c.$watch(function(){var
a=Fb(d.url()),b=Fb(k.absUrl()),f=d.state(),g=k.$$replace,q=a!==b||k.$$html5&&e.hi
story&&f!==k.$$state;if(s||q)s=!1,c.$evalAsync(function(){var
b=k.absUrl(),d=c.$broadcast("$locationChangeStart",b,a,k.$$state,f).defaultPreven
ted;k.absUrl()===b&&(d?(k.$$parse(a),k.$$state=f):(q&&h(b,g,f===k.$$state?null:
k.$$state),l(a,f)))});k.$$replace=!1});return k}]}function Ne(){var
b=!0,a=this;this.debugEnabled=function(a){return
y(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof

Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error:
"+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceU
RL+":"+a.line));return a}function e(a){var
b=c.console||{},e=b[a]||b.log||E;a=!1;try{a=!!e.apply}catch(l){}return
a?function(){var a=[];r(arguments,function(b){a.push(d(b))});return
e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),w
arn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return
function(){b&&c.apply(a,

arguments)}}()}}]}function
ua(b,a){if("__defineGetter__"===b||"__defineSetter__"===b||"__lookupGetter__"=
==b||"__lookupSetter__"===b||"__proto__"===b)throw na("isecfld",a);return
b}function oa(b,a){if(b){if(b.constructor===b)throw
na("isecfn",a);if(b.window===b)throw

51
na("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))thro
w na("isecdom",a);if(b===Object)throw na("isecobj",a);}return b}function
fc(b){return b.constant}function hb(b,a,c,d,e){oa(b,e);oa(a,e);c=c.split(".");for(var f,

g=0;1<c.length;g++){f=ua(c.shift(),e);var
h=0===g&&a&&a[f]||b[f];h||(h={},b[f]=h);b=oa(h,e)}f=ua(c.shift(),e);oa(b[f],e);retu
rn b[f]=d}function Pa(b){return"constructor"==b}function
ed(b,a,c,d,e,f,g){ua(b,f);ua(a,f);ua(c,f);ua(d,f);ua(e,f);var h=function(a){return
oa(a,f)},l=g||Pa(b)?h:ra,k=g||Pa(a)?h:ra,n=g||Pa(c)?h:ra,p=g||Pa(d)?h:ra,q=g||Pa(e)?
h:ra;return function(f,g){var h=g&&g.hasOwnProperty(b)?g:f;if(null==h)return
h;h=l(h[b]);if(!a)return h;if(null==h)return t;h=k(h[a]);if(!c)return h;if(null==

h)return t;h=n(h[c]);if(!d)return h;if(null==h)return t;h=p(h[d]);return


e?null==h?t:h=q(h[e]):h}}function yf(b,a){return function(c,d){return
b(c,d,oa,a)}}function zf(b,a,c){var d=a.expensiveChecks,e=d?Af:Bf,f=e[b];if(f)return
f;var
g=b.split("."),h=g.length;if(a.csp)f=6>h?ed(g[0],g[1],g[2],g[3],g[4],c,d):function(a,b)
{var e=0,f;do
f=ed(g[e++],g[e++],g[e++],g[e++],g[e++],c,d)(a,b),b=t,a=f;while(e<h);return
f};else{var l="";d&&(l+="s = eso(s, fe);\nl = eso(l, fe);\n");var
k=d;r(g,function(a,b){ua(a,

c);var
e=(b?"s":'((l&&l.hasOwnProperty("'+a+'"))?l:s)')+"."+a;if(d||Pa(a))e="eso("+e+",
fe)",k=!0;l+="if(s == null) return undefined;\ns="+e+";\n"});l+="return s;";a=new
Function("s","l","eso","fe",l);a.toString=ea(l);k&&(a=yf(a,c));f=a}f.sharedGetter=
!0;f.assign=function(a,c,d){return hb(a,d,b,c,b)};return e[b]=f}function gc(b){return
G(b.valueOf)?b.valueOf():Cf.call(b)}function Oe(){var
b=ia(),a=ia();this.$get=["$filter","$sniffer",function(c,d){function e(a){var
b=a;a.sharedGetter&&(b=function(b,

c){return a(b,c)},b.literal=a.literal,b.constant=a.constant,b.assign=a.assign);return
b}function f(a,b){for(var c=0,d=a.length;c<d;c++){var
e=a[c];e.constant||(e.inputs?f(e.inputs,b):-1===b.indexOf(e)&&b.push(e))}return
b}function g(a,b){return null==a||null==b?a===b:"object"===typeof
a&&(a=gc(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function
h(a,b,c,d){var e=d.$$inputs||(d.$$inputs=f(d.inputs,[])),h;if(1===e.length){var
k=g,e=e[0];return a.$watch(function(a){var b=e(a);g(b,k)||(h=d(a),k=b&&

52
gc(b));return h},b,c)}for(var l=[],q=0,p=e.length;q<p;q++)l[q]=g;return
a.$watch(function(a){for(var b=!1,c=0,f=e.length;c<f;c++){var
k=e[c](a);if(b||(b=!g(k,l[c])))l[c]=k&&gc(k)}b&&(h=d(a));return h},b,c)}function
l(a,b,c,d){var e,f;return e=a.$watch(function(a){return
d(a)},function(a,c,d){f=a;G(b)&&b.apply(this,arguments);y(a)&&d.$$postDigest(fu
nction(){y(f)&&e()})},c)}function k(a,b,c,d){function e(a){var
b=!0;r(a,function(a){y(a)||(b=!1)});return b}var f,g;return
f=a.$watch(function(a){return d(a)},

function(a,c,d){g=a;G(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)
&&f()})},c)}function n(a,b,c,d){var e;return e=a.$watch(function(a){return
d(a)},function(a,c,d){G(b)&&b.apply(this,arguments);e()},c)}function
p(a,b){if(!b)return a;var c=a.$$watchDelegate,c=c!==k&&c!==l?function(c,d){var
e=a(c,d);return b(e,c,d)}:function(c,d){var e=a(c,d),f=b(e,c,d);return
y(e)?f:e};a.$$watchDelegate&&a.$$watchDelegate!==h?c.$$watchDelegate=a.$$wat
chDelegate:b.$stateful||(c.$$watchDelegate=h,c.inputs=

[a]);return c}var
q={csp:d.csp,expensiveChecks:!1},u={csp:d.csp,expensiveChecks:!0};return
function(d,f,g){var m,r,t;switch(typeof d){case "string":t=d=d.trim();var
L=g?a:b;m=L[t];m||(":"===d.charAt(0)&&":"===d.charAt(1)&&(r=!0,d=d.substri
ng(2)),g=g?u:q,m=new hc(g),m=(new
ib(m,c,g)).parse(d),m.constant?m.$$watchDelegate=n:r?(m=e(m),m.$$watchDelegat
e=m.literal?k:l):m.inputs&&(m.$$watchDelegate=h),L[t]=m);return p(m,f);case
"function":return p(d,f);default:return p(E,f)}}}]}function Qe(){this.$get=

["$rootScope","$exceptionHandler",function(b,a){return
fd(function(a){b.$evalAsync(a)},a)}]}function
Re(){this.$get=["$browser","$exceptionHandler",function(b,a){return
fd(function(a){b.defer(a)},a)}]}function fd(b,a){function c(a,b,c){function
d(b){return function(c){e||(e=!0,b.call(a,c))}}var e=!1;return[d(b),d(c)]}function
d(){this.$$state={status:0}}function e(a,b){return function(c){b.call(a,c)}}function
f(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,b(function(){var
b,d,e;e=c.pending;

c.processScheduled=!1;c.pending=t;for(var
f=0,g=e.length;f<g;++f){d=e[f][0];b=e[f][c.status];try{G(b)?d.resolve(b(c.value)):1==
=c.status?d.resolve(c.value):d.reject(c.value)}catch(h){d.reject(h),a(h)}}}))}function
g(){this.promise=new

53
d;this.resolve=e(this,this.resolve);this.reject=e(this,this.reject);this.notify=e(this,this.
notify)}var h=R("$q",TypeError);d.prototype={then:function(a,b,c){var d=new
g;this.$$state.pending=this.$$state.pending||[];this.$$state.pending.push([d,a,b,c]);0
<this.$$state.status&&

f(this.$$state);return d.promise},"catch":function(a){return
this.then(null,a)},"finally":function(a,b){return this.then(function(b){return
k(b,!0,a)},function(b){return
k(b,!1,a)},b)}};g.prototype={resolve:function(a){this.promise.$$state.status||(a===thi
s.promise?this.$$reject(h("qcycle",a)):this.$$resolve(a))},$$resolve:function(b){var
d,e;e=c(this,this.$$resolve,this.$$reject);try{if(J(b)||G(b))d=b&&b.then;G(d)?(this.p
romise.$$state.status=-1,d.call(b,e[0],e[1],this.notify)):(this.promise.$$state.value=

b,this.promise.$$state.status=1,f(this.promise.$$state))}catch(g){e[1](g),a(g)}},reject:
function(a){this.promise.$$state.status||this.$$reject(a)},$$reject:function(a){this.pr
omise.$$state.value=a;this.promise.$$state.status=2;f(this.promise.$$state)},notify:f
unction(c){var
d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&
b(function(){for(var
b,e,f=0,g=d.length;f<g;f++){e=d[f][0];b=d[f][3];try{e.notify(G(b)?b(c):c)}catch(h){a(
h)}}})}};var l=function(a,b){var c=new g;b?c.resolve(a):

c.reject(a);return c.promise},k=function(a,b,c){var
d=null;try{G(c)&&(d=c())}catch(e){return l(e,!1)}return
d&&G(d.then)?d.then(function(){return l(a,b)},function(a){return
l(a,!1)}):l(a,b)},n=function(a,b,c,d){var e=new g;e.resolve(a);return
e.promise.then(b,c,d)},p=function u(a){if(!G(a))throw h("norslvr",a);if(!(this
instanceof u))return new u(a);var b=new
g;a(function(a){b.resolve(a)},function(a){b.reject(a)});return
b.promise};p.defer=function(){return new g};p.reject=function(a){var b=new g;

b.reject(a);return b.promise};p.when=n;p.all=function(a){var b=new


g,c=0,d=H(a)?[]:{};r(a,function(a,e){c++;n(a).then(function(a){d.hasOwnProperty(e
)||(d[e]=a,--
c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolv
e(d);return b.promise};return p}function
$e(){this.$get=["$window","$timeout",function(b,a){var
c=b.requestAnimationFrame||b.webkitRequestAnimationFrame,d=b.cancelAnimati

54
onFrame||b.webkitCancelAnimationFrame||b.webkitCancelRequestAnimationFram
e,e=!!c,f=e?function(a){var b=

c(a);return function(){d(b)}}:function(b){var c=a(b,16.66,!1);return


function(){a.cancel(c)}};f.supported=e;return f}]}function Pe(){function
b(a){function
b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.
$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++ob;this.$
$ChildScope=null}b.prototype=a;return b}var
a=10,c=R("$rootScope"),d=null,e=null;this.digestTtl=function(b){arguments.length
&&(a=b);return a};this.$get=["$injector","$exceptionHandler",

"$parse","$browser",function(f,g,h,l){function
k(a){a.currentScope.$$destroyed=!0}function
n(){this.$id=++ob;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=thi
s.$$prevSibling=this.$$childHead=this.$$childTail=null;this.$root=this;this.$$destr
oyed=!1;this.$$listeners={};this.$$listenerCount={};this.$$isolateBindings=null}func
tion p(a){if(v.$$phase)throw c("inprog",v.$$phase);v.$$phase=a}function
q(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete
a.$$listenerCount[c];

while(a=a.$parent)}function u(){}function
s(){for(;t.length;)try{t.shift()()}catch(a){g(a)}e=null}function
M(){null===e&&(e=l.defer(function(){v.$apply(s)}))}n.prototype={constructor:n,$n
ew:function(a,c){var d;c=c||this;a?(d=new
n,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=b(this)),d=new
this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$
childTail.$$nextSibling=d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(a||c!=th
is)&&d.$on("$destroy",k);return d},

$watch:function(a,b,c){var e=h(a);if(e.$$watchDelegate)return
e.$$watchDelegate(this,b,c,e);var
f=this.$$watchers,g={fn:b,last:u,get:e,exp:a,eq:!!c};d=null;G(b)||(g.fn=E);f||(f=this.$
$watchers=[]);f.unshift(g);return
function(){Xa(f,g);d=null}},$watchGroup:function(a,b){function
c(){h=!1;k?(k=!1,b(e,e,g)):b(e,d,g)}var
d=Array(a.length),e=Array(a.length),f=[],g=this,h=!1,k=!0;if(!a.length){var
l=!0;g.$evalAsync(function(){l&&b(e,e,g)});return
function(){l=!1}}if(1===a.length)return this.$watch(a[0],

55
function(a,c,f){e[0]=a;d[0]=c;b(e,a===c?e:d,f)});r(a,function(a,b){var
k=g.$watch(a,function(a,f){e[b]=a;d[b]=f;h||(h=!0,g.$evalAsync(c))});f.push(k)});ret
urn function(){for(;f.length;)f.shift()()}},$watchCollection:function(a,b){function
c(a){e=a;var
b,d,g,h;if(!x(e)){if(J(e))if(Sa(e))for(f!==p&&(f=p,u=f.length=0,l++),a=e.length,u!==a
&&(l++,f.length=u=a),b=0;b<a;b++)h=f[b],g=e[b],d=h!==h&&g!==g,d||h===g||(l++,
f[b]=g);else{f!==n&&(f=n={},u=0,l++);a=0;for(b in
e)e.hasOwnProperty(b)&&(a++,g=e[b],h=

f[b],b in f?(d=h!==h&&g!==g,d||h===g||(l++,f[b]=g)):(u++,f[b]=g,l++));if(u>a)for(b
in l++,f)e.hasOwnProperty(b)||(u--,delete f[b])}else f!==e&&(f=e,l++);return
l}}c.$stateful=!0;var
d=this,e,f,g,k=1<b.length,l=0,q=h(a,c),p=[],n={},m=!0,u=0;return
this.$watch(q,function(){m?(m=!1,b(e,e,d)):b(e,g,d);if(k)if(J(e))if(Sa(e)){g=Array(e.l
ength);for(var a=0;a<e.length;a++)g[a]=e[a]}else for(a in
g={},e)tc.call(e,a)&&(g[a]=e[a]);else g=e})},$digest:function(){var
b,f,h,k,q,n,r=a,t,O=[],M,y;p("$digest");l.$$checkUrlChange();

this===v&&null!==e&&(l.defer.cancel(e),s());d=null;do{n=!1;for(t=this;m.length;){
try{y=m.shift(),y.scope.$eval(y.expression,y.locals)}catch(w){g(w)}d=null}a:do{if(k=
t.$$watchers)for(q=k.length;q--
;)try{if(b=k[q])if((f=b.get(t))!==(h=b.last)&&!(b.eq?ha(f,h):"number"===typeof
f&&"number"===typeof
h&&isNaN(f)&&isNaN(h)))n=!0,d=b,b.last=b.eq?Da(f,null):f,b.fn(f,h===u?f:h,t),5>
r&&(M=4-r,O[M]||(O[M]=[]),O[M].push({msg:G(b.exp)?"fn:
"+(b.exp.name||b.exp.toString()):b.exp,newVal:f,oldVal:h}));else if(b===

d){n=!1;break
a}}catch(A){g(A)}if(!(k=t.$$childHead||t!==this&&t.$$nextSibling))for(;t!==this&&
!(k=t.$$nextSibling);)t=t.$parent}while(t=k);if((n||m.length)&&!r--)throw
v.$$phase=null,c("infdig",a,O);}while(n||m.length);for(v.$$phase=null;F.length;)try
{F.shift()()}catch(x){g(x)}},$destroy:function(){if(!this.$$destroyed){var
a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;if(this!==v){for(var
b in
this.$$listenerCount)q(this,this.$$listenerCount[b],b);a.$$childHead==this&&(a.$$
childHead=

this.$$nextSibling);a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$
prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibl

56
ing&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$dig
est=this.$apply=this.$evalAsync=this.$applyAsync=E;this.$on=this.$watch=this.$wa
tchGroup=function(){return
E};this.$$listeners={};this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$chi
ldHead=this.$$childTail=this.$root=this.$$watchers=null}}},$eval:function(a,

b){return
h(a)(this,b)},$evalAsync:function(a,b){v.$$phase||m.length||l.defer(function(){m.leng
th&&v.$digest()});m.push({scope:this,expression:a,locals:b})},$$postDigest:function
(a){F.push(a)},$apply:function(a){try{return
p("$apply"),this.$eval(a)}catch(b){g(b)}finally{v.$$phase=null;try{v.$digest()}catch
(c){throw g(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var
c=this;a&&t.push(b);M()},$on:function(a,b){var
c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do
d.$$listenerCount[a]||

(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var
e=this;return function(){var d=c.indexOf(b);-
1!==d&&(c[d]=null,q(e,1,a))}},$emit:function(a,b){var
c=[],d,e=this,f=!1,h={name:a,targetScope:e,stopPropagation:function(){f=!0},preven
tDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=Ya([h],argumen
ts,1),l,q;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(q=d.length;l<q;l++)if(d[l]
)try{d[l].apply(null,k)}catch(p){g(p)}else d.splice(l,1),l--,q--;if(f)return
h.currentScope=

null,h;e=e.$parent}while(e);h.currentScope=null;return
h},$broadcast:function(a,b){var
c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPreven
ted=!0},defaultPrevented:!1};if(!this.$$listenerCount[a])return e;for(var
f=Ya([e],arguments,1),h,l;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(l=d.l
ength;h<l;h++)if(d[h])try{d[h].apply(null,f)}catch(k){g(k)}else d.splice(h,1),h--,l--
;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!=
=this&&!(d=

c.$$nextSibling);)c=c.$parent}e.currentScope=null;return e}};var v=new


n,m=v.$$asyncQueue=[],F=v.$$postDigestQueue=[],t=v.$$applyAsyncQueue=[];ret
urn v}]}function Sd(){var
b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.a
HrefSanitizationWhitelist=function(a){return

57
y(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return
y(b)?(a=b,this):a};this.$get=function(){return function(c,d){var
e=d?a:b,f;f=Aa(c).href;return""===f||f.match(e)?c:"unsafe:"+

f}}}function Df(b){if("self"===b)return b;if(C(b)){if(-1<b.indexOf("***"))throw


Ba("iwcard",b);b=gd(b).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return
new RegExp("^"+b+"$")}if(Ua(b))return new RegExp("^"+b.source+"$");throw
Ba("imatcher");}function hd(b){var
a=[];y(b)&&r(b,function(b){a.push(Df(b))});return a}function
Te(){this.SCE_CONTEXTS=pa;var
b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&&(b=hd(a
));return b};this.resourceUrlBlacklist=function(b){arguments.length&&

(a=hd(b));return a};this.$get=["$injector",function(c){function
d(a,b){return"self"===a?$c(b):!!a.exec(b.href)}function e(a){var
b=function(a){this.$$unwrapTrustedValue=function(){return
a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return
this.$$unwrapTrustedValue()};b.prototype.toString=function(){return
this.$$unwrapTrustedValue().toString()};return b}var f=function(a){throw
Ba("unsafe");};c.has("$sanitize")&&(f=c.get("$sanitize"));var
g=e(),h={};h[pa.HTML]=e(g);h[pa.CSS]=e(g);h[pa.URL]=

e(g);h[pa.JS]=e(g);h[pa.RESOURCE_URL]=e(h[pa.URL]);return{trustAs:function(
a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw
Ba("icontext",a,b);if(null===b||b===t||""===b)return b;if("string"!==typeof
b)throw Ba("itype",a);return new
c(b)},getTrusted:function(c,e){if(null===e||e===t||""===e)return e;var
g=h.hasOwnProperty(c)?h[c]:null;if(g&&e instanceof g)return
e.$$unwrapTrustedValue();if(c===pa.RESOURCE_URL){var
g=Aa(e.toString()),p,q,u=!1;p=0;for(q=b.length;p<q;p++)if(d(b[p],g)){u=!0;break}if
(u)for(p=

58
Project Description

The implementation in this chapter is a fairly basic shopping cart that allows you to add
items, remove items and then go through the checkout process and view your orders. The
example provides most of the basic functionality that is required for a shopping cart,
however, it is not intended for production use but rather as a teaching aid.

Keep in mind that the cart does not actually link to a credit card verification service to
process payments. That goes beyond the scope of the project. There is still a fair amount
of work to take this project and make it into something that could be used in production.

Also login, authentication and session management are also omitted from the project
since that was already covered in Chapter 26 and would add additional code to sift
through to understand the shopping cart example. The example is hard coded to a user
with a userid of customerA. You will see that places where the authenticated user would
be used customerA is hard coded.

The general logic flow of the example is as follows:

1. The root page contains a list of prints that can be purchased.


2. The user clicks on a print to view details where they can add it to the cart. Multiple
items can be added to the cart. The top link to the cart shows the number of items in the
cart.
3. The user clicks on the cart and can see the items. They can change the quantity,
remove items, continue shopping or checkout.
4. On checkout the customer is presented with shipping information page.
5. Then the customer is presented with billing information including the billing address
and credit card data.
6. When the customer clicks to submit the billing information a review of the validated
transaction is displayed and the user can select the final purchase.
7. After the final purchase is clicked the user is shown a list of their orders including the
recently completed order.

Libraries Used

The project in this chapter uses the following add on Node.js NPM modules. You will
need to install them into your project directory if you intend to follow along with the
example code:

• express: Used as the main web server for the project.


• ejs: Used to render the HTML templates.
• mongodb: Used to access the MongoDB database.
59
• mongoose: Used to provide the structured data model.

This code in this chapter also requires the AngularJS library to be provided as well.

Project Directory Structure

The project is organized into the following directory structure to show you one method of
organizing your code. You do not have to follow this directory structure, however,
directory structure should be part of the overall design of your projects so that you can
easily find the code that you are looking for.

• ./: Project root that contains the base application files and supporting folders.
• ./npm_modules: Created when the NPMs listed above are installed in the system.
• ./controllers: Contains the Express route controllers that provide the interaction
between routes and changes to the MongoDB database.
• ./models: Contains the Mongoose model definitions for objects in the database.
• ./static: Contains any static files that need to be sent such as CSS and AngularJS
JavaScript and AngularJS partial templates.
• ./views: Contains the HTML templates that will be rendered by EJS.
• ../images: Contains any images for the project. This is at a peer level to the project so
that the same images can be used by multiple projects.
• ../lib: Contains the AngularJS libraries so that they can be loaded locally my multiple
projects. In production you may want to use a CDN delivery address instead.

In addition to the directory structure the following code files are included. The following
list is intended to give you an idea of the functionality of each file:

• ./cart_init.js: Standalone initialization code for this example. It will create a single
customer named customerA and add several products to use in the example.
• ./cart_server.js: Main application file that loads the necessary libraries, creates a
connection to MongoDB and starts the Express server.
• ./cart_routes.js: Defines the routes for the Express server. Functionality that does not
interact with the database is also handled directly in this file.
• ./controllers/customers_controller.js: Defines the functionality for the routes that
require interaction with the MongoDB database to get and update customer data such as
cart, shipping and billing information.
• ./controllers/orders_controller.js: Defines the functionality for the routes that require
interaction with the MongoDB database to get the order objects.
• ./controllers/products_controller.js: Defines the functionality for the routes that
require interaction with the MongoDB database to get the one or all product objects.
• ./models/cart_model.js: Defines the customer, product, order, and supporting models
for the cart example.

60
• ./views/shopping.html: Provides the main shopping page for the application that will
allow the user to view products and place them into the cart.
• ./static/billing.html: This is an AngularJS partial that implements the billing
information view.
• ./static/cart.html: This is an AngularJS partial that implements the shopping cart view.
• ./static/orders.html: This is an AngularJS partial that implements a view with a list of
orders.
• ./static/products.html: This is an AngularJS partial that implements a list of products
view.
• ./static/product.html: This is an AngularJS partial that implements a view for a single
product with an add to cart link.
• ./static/review.html: This is an AngularJS partial that implements a view to allow the
user to review the order before placing it.
• ./static/shipping.html: This is an AngularJS partial that implements the shipping
information view.
• ./static/js/cart_app.js: Provides the AngularJS module and controller definitions to
handle all of the shopping cart interaction between the AngularJS code and the
webserver.
• ./static/css/cart_styles.css: Provides the CSS styling for the AngularJS HTML pages.

Defining the Customer, Product and Orders Models

As always you should look at the object model needs as the first step in implementing
your applications. In this case the object of the exercise is to provide a shopping cart. To
do so we will need a customer model as a container for the shopping cart. Also we will
need products to place into the shopping cart. To checkout we will need billing
information and shipping information and then once the order is placed we will need a
way to store the order.

Therefore we need a model for the customer, product, order billing and shipping data to
support the shopping cart. The following sections discuss the design of each of the model
schemas implemented in the project. The schemas are all contained in a file
name cart_model.js that implements all of the schemas. The first two lines and the final
line of that file are as follows to load mongoose Schema:

01 var mongoose = require('mongoose'),


02 Schema = mongoose.Schema;

The rest of the lines will be given in each section below as they are described.

Defining the Address Schema

61
We begin by defining an address schema that will be generic so that it can be used in both
the shipping information and as part of the billing information. The code in Listing
28.1 implements theAddressSchema containing the standard address information. Notice
that the following code is used to disable the _id property since we will not need to look
up addresses by id:

{ _id: false }

Listing 28.1. cart_model.js-AddressSchema Defining a Schema for Shipping and


Billing Addresses

03 var AddressSchema = new Schema({


04 name: String,
05 address: String,
06 city: String,
07 state: String,
08 zip: String
09 }, { _id: false });
10 mongoose.model('Address', AddressSchema);

Defining the Billing Schema

With the AddressSchema defined we can now define the billing schema to keep track of
credit card information as well as billing information. The code in Listing
28.2 implements the BillingSchemacontaining standard credit card data. Notice that the
following implementation for cardtype will require that an entry
of Visa, MasterCard or Amex is used, no other values are allowed:

cardtype: { type: String, enum: ['Visa', 'MasterCard', 'Amex'] },

Also notice that the address field is assigned the AddressSchema object type. Mongoose
requires that nesting schemas in this way be included in an array. This is used in more
than one place and you will see that through the example that the address will be access
using address[0] to get the first item in the array.

Listing 28.2. cart_model.js-BillingSchema Defining a Schema for Billing Credit


Card and Address

11 var BillingSchema = new Schema({


12 cardtype: { type: String, enum: ['Visa', 'MasterCard', 'Amex'] },
13 name: String,
14 number: String,
15 expiremonth: Number,
16 expireyear: Number,
62
17 address: [AddressSchema]
18 }, { _id: false });
19 mongoose.model('Billing', BillingSchema);

Defining the Product Schema

Next we define the schema to store product information the product model for this
example is a print with a name, imagefile, description, price and instock counter. Listing
28.3 shows the full definition of the ProductSchema.

Listing 28.3. cart_model.js-ProductSchema Defining a Basic Product Schema for


Prints

20 var ProductSchema = new Schema({


21 name: String,
22 imagefile: String,
23 description: String,
24 price: Number,
25 instock: Number
26 });
27 mongoose.model('Product', ProductSchema);

Defining the Quantity Schema

For orders and the shopping cart we can include the products in an array, however, we
also need the ability to store a quantity. One method of doing this is to define a quantity
schema with quantity and product fields. Listing 28.4 implements
the ProductQantitySchema that does just that. Notice that since we are embedding
the ProductSchema we include it as an array and through the example will access it
using product[0].

Listing 28.4. cart_model.js-QuantitySchema Defining a Basic Quantity Schema for


Quantity of Products in Orders and the Cart

27 mongoose.model('Product', ProductSchema);
28 var ProductQuantitySchema = new Schema({
29 quantity: Number,
30 product: [ProductSchema]
31 }, { _id: false });
32 mongoose.model('ProductQuantity', ProductQuantitySchema);

Defining the Order Schema

63
Next is the order schema that keeps track of the items ordered, shipping information and
billing information. Listing 28.5 implements the OrderSchema model that stores the
necessary information about the order. Notice that a Date type is assigned to
the timestamp field to keep track of when the order was placed. Also the items field is an
array of QuantityShema sub documents.

Listing 28.5. cart_model.js-OrderSchema Defining the Order Schema to Store the


Order Information

33 var OrderSchema = new Schema({


34 userid: String,
35 items: [ProductQuantitySchema],
36 shipping: [AddressSchema],
37 billing: [BillingSchema],
38 status: {type: String, default: "Pending"},
39 timestamp: { type: Date, default: Date.now }
40 });
41 mongoose.model('Order', OrderSchema);

Defining the Customer Schema

The final schema is the customer schema. Listing 28.6 implements


the CustomerSchema that contains a unique userid field to identify the customer and
associate orders with the customer. This field would normally map to an authenticated
session userid.

Notice that the shipping, billing and cart are all nested schemas. That makes is simple to
define the model. You will see that each of these will be accessed in the JavaScript using
the shipping[0],billing[0] and cart[0] array indexing. No cart object schema is necessary
because it is inherently implemented by the array
of ProductQuantitySchema subdocuments.

Listing 28.6. cart_model.js-CustomerSchema Defining the Customer Schema to


Store the Shipping, Billing and Cart

42 var CustomerSchema = new Schema({


43 userid: { type: String, unique: true, required: true },
44 shipping: [AddressSchema],
45 billing: [BillingSchema],
46 cart: [ProductQuantitySchema]
47 });
48 mongoose.model('Customer', CustomerSchema);

64
Creating the Shopping Cart Server

With the model defined you can begin implementing the shopping cart server. The code
in Listing 28.7implements the Express server for the shopping cart application. This code
should be familiar to you. It includes the express and mongoose libraries and then
connects to MongoDB via Mongoose.

Notice that there is a require statement for the model definition to build
the Schema object within Mongoose. Also the ./cart_routes files is included to initialize
the routes for the server before listening on port 80.

Listing 28.7. cart_model.js Implementing the Shopping Cart Application Server


Using Express and Connecting to MongoDB

01 var express = require('express');


02 var mongoose = require('mongoose');
03 var db = mongoose.connect('mongodb://localhost/cart');
04 require('./models/cart_model.js');
05 var app = express();
06 app.engine('.html', require('ejs').__express);
07 app.set('views', __dirname + '/views');
08 app.set('view engine', 'html');
09 app.use(express.cookieParser());
10 app.use(express.bodyParser());
11 require('./cart_routes')(app);
12 app.listen(80);

Implementing Routes to Support Product, Cart and Order Requests

As part of the Express server configuration the ./cart_routes.js file shown in Listing
28.7 was loaded. The code in Listing 28.8 provides the routes necessary to get the
customer, product and order objects. They also provide routes to add orders to the
database and update the customer shipping, billing and cart information.

Lines 6-9 implement the static routes to support getting the AngularJS, CSS, JavaScript,
images and AngularJS template partials used in this example. The images and AngularJS
lib folders are located in a sibling directory to the project. The other static files are in
the ./static folder inside the project.

Notice that when the user access the root location of / for the site that
the shopping.html template is rendered on line 11. The remaining routes on lines 13-19
all involve interaction with the MongoDB database and will be handled in the controller
route handlers described in the next section.

65
Listing 28.8. cart_routes.js Implementing the Routes for Shopping Cart Web
Requests from the Client

01 var express = require('express');


02 module.exports = function(app) {
03 var customers = require('./controllers/customers_controller');
04 var products = require('./controllers/products_controller');
05 var orders = require('./controllers/orders_controller');
06 app.use('/static', express.static( './static')).
07 use('/images', express.static( '../images')).
08 use('/lib', express.static( '../lib')
09 );
10 app.get('/', function(req, res){
11 res.render('shopping');
12 });
13 app.get('/products/get', products.getProducts);
14 app.get('/orders/get', orders.getOrders);
15 app.post('/orders/add', orders.addOrder);
16 app.get('/customers/get', customers.getCustomer);
17 app.post('/customers/update/shipping', customers.updateShipping);
18 app.post('/customers/update/billing', customers.updateBilling);
19 app.post('/customers/update/cart', customers.updateCart);
20 }

Implementing the Model Based Controller Routes

In addition to the standard route implementation you also need to implement route
handling that interact with the database. These route handlers are broken out of the
standard cart_route.js file and into their own files based on model to keep the code clean
and ensure a good division of responsibilities.

The following sections cover the implementation of the model specific controllers for
the Customer,Order and Product models.

Implementing the Product Model Controller

The code in Listing 28.9 implements the route handling code for the Product model.
There are only two routes. The getProduct() route finds a single product based on
the productId included in the query. The getProducts() route finds all products. If
successful the product or all products are returned to the client as JSON strings. If the
requests fail then a 404 error is returned.

66
Listing 28.9. products_controller.js Implementing the Get Product and Get
Products Routes for the Express Server

01 var mongoose = require('mongoose'),


02 Product = mongoose.model('Product');
03 exports.getProduct = function(req, res) {
04 Product.findOne({ _id: req.query.productId })
05 .exec(function(err, product) {
06 if (!product){
07 res.json(404, {msg: 'Photo Not Found.'});
08 } else {
09 res.json(product);
10 }
11 });
12 };
13 exports.getProducts = function(req, res) {
14 Product.find()
15 .exec(function(err, products) {
16 if (!products){
17 res.json(404, {msg: 'Products Not Found.'});
18 } else {
19 res.json(products);
20 }
21 });
22 };

Implementing the Order Model Controller

The code in Listing 28.10 implements the route handling code for the Order model. There
are three routes. The getOrder() route finds a single order based on the orderId included
in the query. ThegetOrders() route finds all orders that belong to the current user. In the
case of the example theuserid of customerA is hard coded. If successful the order or all of
this customer’s orders are returned to the client as JSON strings. If the requests fail then a
404 error is returned.

The addOrder() route handler builds a new Order object by getting


the updatedShipping,updatedBilling and orderItems parameters from the POST request.
If the order saves successfully then the cart field in the customer object is reset to empty
using the following code and a success is returned otherwise a 500 or 404 error is
returned.

37 Customer.update({ userid: 'customerA' },


38 {$set:{cart:[]}})

67
Listing 28.10. orders_controller.js Implementing the Get Order(s) and Add Order
Routes for the Express Server

01 var mongoose = require('mongoose'),


02 Customer = mongoose.model('Customer'),
03 Order = mongoose.model('Order'),
04 Address = mongoose.model('Address'),
05 Billing = mongoose.model('Billing');
06 exports.getOrder = function(req, res) {
07 Order.findOne({ _id: req.query.orderId })
08 .exec(function(err, order) {
09 if (!order){
10 res.json(404, {msg: 'Order Not Found.'});
11 } else {
12 res.json(order);
13 }
14 });
15 };
16 exports.getOrders = function(req, res) {
17 Order.find({userid: 'customerA'})
18 .exec(function(err, orders) {
19 if (!orders){
20 res.json(404, {msg: 'Orders Not Found.'});
21 } else {
22 res.json(orders);
23 }
24 });
25 };
26 exports.addOrder = function(req, res){
27 var orderShipping = new Address(req.body.updatedShipping);
28 var orderBilling = new Billing(req.body.updatedBilling);
29 var orderItems = req.body.orderItems;
30 var newOrder = new Order({userid: 'customerA',
31 items: orderItems, shipping: orderShipping,
32 billing: orderBilling});
33 newOrder.save(function(err, results){
34 if(err){
35 res.json(500, "Failed to save Order.");
36 } else {
37 Customer.update({ userid: 'customerA' },
38 {$set:{cart:[]}})
39 .exec(function(err, results){
40 if (err || results < 1){

68
41 res.json(404, {msg: 'Failed to update Cart.'});
42 } else {
43 res.json({msg: "Order Saved."});
44 }
45 });
46 }
47 });
48 );

Implementing the Customer Model Controller

The code in Listing 28.11 implements the route handling code for the Customer model.
There are four routes.

The getCustomer() route finds a customer order based on the hard


coded customerA value. If successful the customer object is returned to the client as
JSON strings. If the requests fails then a 404 error is returned.

The updateShipping() route will create a new Address object from


the updatedShippingparameter in the POST request and then use an update() method to
update the customer object with the new shipping data. If successful a success message is
returned, if it fails then a 404 error is returned.

The updateBilling() route will create a new Billing object from


the updatedBilling parameter in the POST request and then use an update() method to
update the customer object with the new billing data. If successful a success message is
returned, if it fails then a 404 error is returned.

The updateCart() route will use the update() method to update the cart field of the
customer with the updatedCart object sent in the POST request. If successful a success
message is returned, if it fails then a 404 error is returned.

Listing 28.11. customers_controller.js Implementing the Customer Get and Update


Routes for the Express Server

01 var mongoose = require('mongoose'),


02 Customer = mongoose.model('Customer'),
03 Address = mongoose.model('Address'),
04 Billing = mongoose.model('Billing');
05 exports.getCustomer = function(req, res) {
06 Customer.findOne({ userid: 'customerA' })
07 .exec(function(err, customer) {
08 if (!customer){

69
09 res.json(404, {msg: 'Customer Not Found.'});
10 } else {
11 res.json(customer);
12 }
13 });
14 };
15 exports.updateShipping = function(req, res){
16 var newShipping = new Address(req.body.updatedShipping);
17 Customer.update({ userid: 'customerA' },
18 {$set:{shipping:[newShipping.toObject()]}})
19 .exec(function(err, results){
20 if (err || results < 1){
21 res.json(404, {msg: 'Failed to update Shipping.'});
22 } else {
23 res.json({msg: "Customer Shipping Updated"});
24 }
25 });
26 };
27 exports.updateBilling = function(req, res){
28 // This is where you could verify the credit card information
29 // and halt the checkout if it is invalid.
30 var newBilling = new Billing(req.body.updatedBilling);
31 Customer.update({ userid: 'customerA' },
32 {$set:{billing:[newBilling.toObject()]}})
33 .exec(function(err, results){
34 if (err || results < 1){
35 res.json(404, {msg: 'Failed to update Billing.'});
36 } else {
37 res.json({msg: "Customer Billing Updated"});
38 }
39 });
40 };
41 exports.updateCart = function(req, res){
42 Customer.update({ userid: 'customerA' },
43 {$set:{cart:req.body.updatedCart}})
44 .exec(function(err, results){
45 if (err || results < 1){
46 res.json(404, {msg: 'Failed to update Cart.'});
47 } else {
48 res.json({msg: "Customer Cart Updated"});
49 }
50 });
51 };

70
Implementing Shopping Cart and Checkout Views

Now that the routes are setup and configured you are ready to implement the views that
are rendered by the routes and AngularJS templates. The following sections discuss the
main shopping.html view rendered by EJS as well as the various partial views that make
up the cart, shipping, billing,review and orders pages.

Implementing the Shopping View

The shopping view shown in Listing 28.10 is the main view for the shopping application.
In fact the user will never actually leave this page, only the content will change using the
partial views described in the following sections.

The header of the view registers the <html ng-app="myApp"> element with the
AngularJS myAppapplication and loads the cart_styles.css file. The <body> element
initializes the AngularJS ng-controller="shoppingControler" to provide the functionality
to interact with the products, shopping cart, checkout and orders.

The page content changing works using the following ng-include directive that maps
to$scope.content. The content variable in the scope can then be set to which every
template file on the server that you want.

<div ng-include="content"></div>

An example of this is shown in the orders and shopping cart clickable links, shown
below, that callsetConent() with the name of the template file to load.

<span class="orders"
ng-click="setContent('orders.html')">Orders</span>
<span id="cartLink" ng-click="setContent('cart.html')">
{{customer.cart.length}} items
<img src="/images/cart.png" />
</span>

Also notice that the number of items in the cart is taken directly from the scope
variablecustomer.cart.length. The customer object in the scope is downloaded directly
from the web server when the controller is initialized. Figure 28.1 shows the full rendered
shopping.html page.

Listing 28.12. shopping.html Implementing the Main Shopping Page AngularJS


Template Code

01 <!doctype html>
02 <html ng-app="myApp">
71
03 <head>
04 <title>Shopping Cart</title>
05 <link rel="stylesheet" type="text/css"
06 href="/static/css/cart_styles.css" />
07 </head>
08 <body>
09 <div ng-controller="shoppingController">
10 <div id="banner">
11 <div id="title">My Store</div>
12 <div id="bar">
13 <span class="orders"
14 ng-click="setContent('orders.html')">Orders</span>
15 <span id="cartLink" ng-click="setContent('cart.html')">
16 {{customer.cart.length}} items
17 <img src="/images/cart.png" />
18 </span>
19 </div>
20 </div>
21 <div id="main">
22 <div ng-include="content"></div>
23 </div>
24 </div>
25 <script src="/lib/angular/angular.js"></script>
26 <script src="/static/js/cart_app.js"></script>
27 </body>
28 </html>

Figure 28.1. Rendered shopping page with the shopping cart link and orders link as
well as a list of prints to shop for.

Implementing the Products View

Next we implement the products view to provide the user with a list of products to choose
from. In this example the shopping page is very basic only a single page with a list of

72
prints to buy. This is basic, but it is enough to demonstrate the implementation of the
shopping cart and it keeps the code simple.

The code in Listing 28.12 is an AngularJS partial that is loaded into the view when
the$scope.content is set to products.html. The code uses an ng-repeat on the products that
are initialized when the shoppingController is initialized. Notice that the product
information is displayed on the page using expressions such as {{product.name}}.

Also notice that when the user clicks on the <img> element that the setProduct() function
is called in the controller. That function will set the current $scope.product value and
change the$scope.content value to product.html. Figure 28.2 shows the rendered products
view.

Listing 28.13. products.html Implementing the Product Listing Template Partial

01 <div id="productsContainer">
02 <div class="listItem" ng-repeat="product in products">
03 <img class="listImg" ng-click="setProduct(product._id)"
04 ng-src="../images/{{product.imagefile}}" />
05 <span class="prodName">{{product.name}}</span>
06 <span class="price">{{product.price|currency}}</span>
07 </div>
08 </div>

Figure 28.2. Product listing view showing prints available.

Implementing the Product View

When the user clicks on the image in the products list the product page view is
rendered. Listing 28.14shows the AngularJS code used for the product page view. Notice
that the product information is displayed using AngularJS expressions that are accessing
he $scope.product value. The add to cart button sends the product._id to
the addToCart() function in the controller which will add the print to the cart. Figure
28.3 shows the rendered product view that shows
the image, name,description, quantity and price as well as the add to cart button.

73
Listing 28.14. product.html Implementing the Product Details Template Partial with
Add to Cart Button

01 <div id="productsContainer">
02 <div class="listItem" ng-repeat="product in products">
03 <img class="listImg" ng-click="setProduct(product._id)"
04 ng-src="../images/{{product.imagefile}}" />
05 <span class="prodName">{{product.name}}</span>
06 <span class="price">{{product.price|currency}}</span>
07 </div>
08 </div>

Figure 28.3. Product details view showing description, available and add to cart
button.

Implementing the Cart View

Once the user clicks on the add to cart button the item is added to the cart and the view is
changed to the cart view. The cart view, shown in Listing 28.14, provides a list of
products that are currently existing in the cart, a price total and buttons to check out or go
back shopping.

The following code implements a delete link in the cart to remove the item. It calls a
functiondeleteFromCart() located in the controller an passes the product._id to identify
which item to delete.

<span class="delete"
ng-click="deleteFromCart(product._id)">Remove</span>

Also a quantity field is provided that links directly to the item.quantity element where
item comes from the ng-repeat of the customer.shopping cart array.

The shipping value and total value are calculated by a controller function that is linked by
the following expression code. The |currency filter is used to format the values of price,
shipping and total:

74
<span class="price">{{cartTotal()|currency}}</span>

The cool part about AngularJS is that because the quantity fields are linked directly to the
scope as you change them the shipping and total values change as well. Figure
28.4 shows the rendered shopping cart view.

Listing 28.15. cart.html Implementing the Shopping Cart Template Partial

01 <div id="cartsContainer">
02 <div class="listItem" ng-repeat="item in customer.cart"
03 ng-init="product=item.product[0]">
04 <img class="listImg" ng-click="setProduct(product._id)"
05 ng-src="../images/{{product.imagefile}}" />
06 <span class="prodName">{{product.name}}</span>
07 <span >
08 <span class="price">{{product.price|currency}}</span>
09 <input class="quantity" type="text" ng-model="item.quantity" />
10 <label class="quantity">Quantity</label>
11 <span class="delete"
12 ng-click="deleteFromCart(product._id)">Remove</span>
13 </span>
14 </div>
15 <hr>
16 <div>
17 <span>Shipping</span>
18 <span class="price">{{shipping|currency}}</span>
19 </div>
20 <hr>
21 <div>
22 <span>Total</span>
23 <span class="price">{{cartTotal()|currency}}</span>
24 </div>
25 <hr>
26 <div>
27 <span class="button" ng-click="checkout()"
28 ng-hide="customer.cart.length==0">
29 Checkout
30 </span>
31 <span class="button" ng-click="setContent('products.html')">
32 Continue Shopping
33 </span>
34 </div>
35 </div>

75
Figure 28.4. Shopping cart view allowing the customer to change quantity, remove
items and checkout.

Implementing the Shipping View

When the user clicks on the checkout button in the shopping cart they will be directed to
the shipping view that allows them to enter shipping information. The shipping view,
shown in Listing 28.16, provides a series of input fields to input the shipping information.

The shipping template code is very straightforward. There are a series of text inputs wit
labels for each shipping field value. The fields are linked to
the customer.shipping[0] object in the scope model using the ng-model directive. That
way when the user changes the field the scope is automatically changed. You will see that
this is very useful when sending customer changes back to the database. When the user
clicks on the Continue to Billing button the shipping data will be updated on the server as
well and they will be taken to the billing view. The rendered shipping view is shown
in Figure 28.5.

Listing 28.16. shipping.html Implementing the Shipping Template Partial

01 <div id="shippingContainer">
02 <h2>Ship To:</h2>
03 <label>Name</label>
04 <input type="text" ng-model="customer.shipping[0].name" /><br>
05 <label>Address</label>
06 <input type="text" ng-model="customer.shipping[0].address" /><br>
07 <label>City</label>
08 <input type="text" ng-model="customer.shipping[0].city" /><br>
09 <label>State</label>
10 <input type="text" ng-model="customer.shipping[0].state" /><br>
11 <label>Zipcode</label>
12 <input type="text" ng-model="customer.shipping[0].zip" />
13 <hr>
14 <div>
15 <span class="button" ng-click="setShipping()">
76
16 Continue to Billing
17 </span>
18 <span class="button" ng-click="setContent('products.html')">
19 Continue Shopping
20 </span>
21 </div>
22 </div>

Figure 28.5. Shipping view allowing the user to enter in the address to ship the
payment to.

Implementing the Billing View

When the user clicks on the Continue to Billing button in the shipping view they will be
directed to the billing view that allows them to billing information. The billing view,
shown in Listing 28.16, provides a series of input fields to input the billing information.

The billing template code is similar to the shipping template with the addition of a few
new fields. The card radio buttons to select the credit card are bound to
the customer.billing[0].cardtypevalue in the scope. When you change the radio button
selection the model is also changed.

The values for the <select> dropdown options come from simple arrays defined in the
scope and are bound to the customer.billing[0] data as well. For example the following
lines use an array of number named months in the scope and binds the <select> value to
thecustomer.billing[0].expiremonth value in the scope:

<select ng-model="customer.billing[0].expiremonth"
ng-options="m for m in months"></select>

One other thing to note is that the CCV value is passed to the verifyBilling(ccv) function
when verifying the credit card. The CCV number is not supposed to be stored locally on
the customer site so it is kept separate here and passed as its’ own parameter. The
rendered billing view is shown in Figure 28.6.

77
Listing 28.17. billing.html Implementing the Billing Template Partial

01 <div id="shippingContainer">
02 <h2>Card Info: </h2>
03 <label>Card</label>
04 <input type="radio" ng-model="customer.billing[0].cardtype"
05 value="Visa"> Visa
06 <input type="radio" ng-model="customer.billing[0].cardtype"
07 value="Amex"> Amex
08 <input type="radio" ng-model="customer.billing[0].cardtype"
09 value="MasterCard"> MasterCard
10 <br><label>Name on Card</label>
11 <input type="text" ng-model="customer.billing[0].name" />
12 <br><label>Card Number</label>
13 <input type="text" ng-model="customer.billing[0].number" />
14 <br><label>Expires</label>
15 <select ng-model="customer.billing[0].expiremonth"
16 ng-options="m for m in months"></select>
17 <select ng-model="customer.billing[0].expireyear"
18 ng-options="m for m in years"></select>
19 <label>Card CCV</label>
20 <input class="security" type=text ng-model="ccv" />
21 <h2>Billing Address:</h2>
22 <label>Name</label>
23 <input type="text"
24 ng-model="customer.billing[0].address[0].name" />
25 <br><label>Address</label>
26 <input type="text"
27 ng-model="customer.billing[0].address[0].address" />
28 <br><label>City</label>
29 <input type="text"
30 ng-model="customer.billing[0].address[0].city" />
31 <br><label>State</label>
32 <input type="text"
33 ng-model="customer.billing[0].address[0].state" />
34 <br><label>Zipcode</label>
35 <input type="text"
36 ng-model="customer.billing[0].address[0].zip" />
37 <hr>
38 <div>
39 <span class="button" ng-click="verifyBilling(ccv)">
40 Verify Billing
41 </span>

78
42 <span class="button" ng-click="setContent('products.html')">
43 Continue Shopping
44 </span>
45 </div>
46 </div>

Figure 28.6. Billing information view allowing the user to enter in a credit card and
billing address.

Implementing the Review View

When the user clicks on the Verify Billing button in the billing view they will be taken to
the review view so they can review the order along with shipping and billing information.
The review view, shown in Listing 28.17, shows the ordered items with totals as well as
the shipping and billing information.Figure 28.7 shows the rendered review view.

Figure 28.7. Review view allowing the customer to review their purchase.

Notice that all the information displayed is still coming from the customer object inside
the scope. The shipping information comes from customer.shipping[0], the billing
information comes fromcustomer.billing[0] and the product list comes
from customer.cart.

When the customer clicks on the Make Purchase button this information will be sent to
the webserver and a new order object will be created. The view will then be changed to
the order view.

79
Listing 28.18. review.html Implementing the Order Review Template Partial

01 <div id="reviewContainer">
02 <div class="listItem" ng-repeat="item in customer.cart"
03 ng-init="product=item.product[0]">
04 <img class="listImg" ng-click="setProduct(product._id)"
05 ng-src="../images/{{product.imagefile}}" />
06 <span class="prodName">{{product.name}}</span>
07 <span >
08 <span class="price">{{product.price|currency}}</span>
09 <label class="quantity">{{item.quantity}}</label>
10 <label class="quantity">Quantity</label>
11 </span>
12 </div><hr>
13 <div>
14 <span>Shipping</span>
15 <span class="price">{{shipping|currency}}</span>
16 </div><hr>
17 <div>
18 <span>Total</span>
19 <span class="price">{{cartTotal()|currency}}</span>
20 </div><hr>
21 <div>
22 <div class="review">
23 Shipping:<br>
24 {{customer.shipping[0].name}}<br>
25 {{customer.shipping[0].address}}<br>
26 {{customer.shipping[0].city}},
27 {{customer.shipping[0].state}}
28 {{customer.shipping[0].zip}}<br>
29 </div>
30 <div class="review">
31 Billing:<br>
32 {{customer.billing[0].cardtype}} ending in
33 {{customer.billing[0].number.slice(-5,-1)}}<br>
34 {{customer.billing[0].address[0].name}}<br>
35 {{customer.billing[0].address[0].address}}<br>
36 {{customer.billing[0].address[0].city}},
37 {{customer.billing[0].address[0].state}}
38 {{customer.billing[0].address[0].zip}}<br>
39 </div>
40 </div>
41 <div>

80
42 <span class="button" ng-click="makePurchase()">
43 Make Purchase
44 </span>
45 <span class="button" ng-click="setContent('products.html')">
46 Continue Shopping
47 </span>
48 </div>
49 </div>

Implementing the Orders View

When the order is complete the user will be taken to the orders view to see their
completed purchases. This is only one method, you could add another page to display the
completed order, or you could just send the user back to the shopping page. However, for
the purpose of just giving an example this method seems as good as any.

The orders view, shown in Listing 28.19, displays a list of orders that this customer has
completed. The list comes from an ng-repeat directive on the $scope.orders value. Within
the ng-repeatiteration the date placed, status and items bought are listed as shown
in Figure 28.8.

Listing 28.19. orders.html Implementing the Orders View Template Partial

01 <div id="reviewContainer">
02 <div class="listItem" ng-repeat="order in orders">
03 <p class="itemTitle">Order #{{$index+1}}</p>
04 <p class="prodDesc">Placed {{order.timestamp|date}}</p>
05 <p class="status">{{order.status}}</p>
06 <div class="listItem" ng-repeat="item in order.items"
07 ng-init="product=item.product[0]">
08 <img class="listImg" ng-click="setProduct(product._id)"
09 ng-src="../images/{{product.imagefile}}" />
10 <span class="prodName">{{product.name}}</span>
11 <span >
12 <span class="price">{{product.price|currency}}</span>
13 <label class="quantity">{{item.quantity}}</label>
14 <label class="quantity">Quantity</label>
15 </span>
16 </div>
17 </div>
18 <div>
19 <span class="button" ng-click="setContent('products.html')">
20 Continue Shopping

81
21 </span>
22 </div>
23 </div>

Figure 28.8. List of orders that have been placed by this customer.

Adding CSS to Stylize the Views

Listing 28.20 shows the CSS code that is used to style the elements in the shopping cart
so that you can see why things look and act the way they do. The CSS is condensed as
much as possible to make it fit into the book. Also the titles, buttons and such are
implemented large to make it display better in the book’s figures.

Listing 28.20. styles.css Implementing the CSS Styles for the View HTML Files

01 p{margin:0}
02 label {width:100px; display:inline-block; text-align:right; }
03 input[type="text"]{ border: 2px ridge blue; padding:3px;
04 border-radius:5px; width:400px; }
05 #banner{ border-bottom: 2px blue ridge; height:100px }
06 #title { text-align:center; background-color:#a0d0ff;
07 font:italic bold 48px/60px Georgia, serif; border-radius: 5px }
08 #bar { background-color:#a0d0ff; }
09 #cartLink { float:right; text-align:right; cursor:pointer }
10 #cartLink img { height:25px; }
11 #main {clear:both;}
12 .listItem{border-bottom: 1px solid black; clear:both;
13 margin-top:10px }
14 .listImg { height:50px; vertical-align:top }
15 .fullImg { width:300px; vertical-align:top }
16 .prodName {font: bold 16px/20px Arial, Sans-serif; }
17 .price{ float:right; color:red; width:75px; text-align:right;
18 display:inline-block}
19 .prodInfo{ display:inline-block; }
20 .itemTitle {font: bold 32px/40px Arial, Sans-serif; }
82
21 .fullPrice { color:red; font: bold 20px/24px Arial, Sans-serif;
22 text-align:right}
23 .status {color:green; font: bold 14px/18px Arial, Sans-serif;}
24 .prodDesc { font-style: italic; }
25 .button,
26 .cartButton{ font: 18px/24px Arial, Sans-serif; border-radius: 10px;
27 padding:10px; margin-top:35px; cursor: pointer; width:170px;
28 background-image: -webkit-linear-gradient(top, #FFCC66, #FFFF99);
29 text-align:center}
30 .cartButton img { height:20px; float:right}
31 .button{ display:inline-block; margin:10px;}
32 input.quantity { display:inline-block; float:right; width:30px; }
33 label.quantity { display:inline-block; float:right; width:60px;
34 margin-right:8px; }
35 span.orders,
36 span.delete { cursor:pointer; display:inline-block; float:right;
37 background-color:#FF5858; border-radius: 8px; text-align:center;
38 font: bold 13px/20px Arial, Sans-serif;
39 margin-right:20px; width:80px; }
40 span.orders{ margin-top:5px; margin-right:10px;
41 background-image: -webkit-linear-gradient(top, #FFCC66, #FFFF99);}
42 input.security { width:30px }
43 div.review{ display:inline-block; width:45%; vertical-align:top; }

Implementing the AngularJS Module and Controller to support Shopping Cart


Views

With the views finished you need to implement the AngularJS controller code that will
support them. The views need the ability to get the customer, products and orders
documents from the webserver. They also need the ability to update the customer
shipping, billing and cart as well as the ability to process new orders.

In the shopping cart example everything was built into a single module and controller.
The controller code is a bit long for a single section in the book, so the following sections
break down the various components of the controller code so they can be described in
smaller code chunks. The full Angular JS code in for the controller is shown in Listing
28.28.

Initializing the Shopping Scope

The first step in implementing the shoppingController is to initialize the scope values that
we need. The code in Listing 28.21 initializes the shopping scope.
The $scope.months and$scope.years arrays are used to populate the credit card form. The

83
$scope.content determines which AngularJS partial is rendered in the view. It is
initialized to products.html so the user can begin shopping.

Next there are three $http requests that get the products, customer and orders and use the
results to set
the $scope.products, $scope.product, $scope.customer and $scope.orders objects that are
utilized in the AngularJS views.

Listing 28.21. cart_app.js-initialize Implementing the CSS Styles for the View
HTML Files

004 $scope.months = [1,2,3,4,5,6,7,8,9,10,11,12];


005 $scope.years = [2014,2015,2016,2017,2018,2019,2020];
006 $scope.content = '/static/products.html';
007 $http.get('/products/get')
008 .success(function(data, status, headers, config) {
009 $scope.products = data;
010 $scope.product = data[0];
011 })
012 .error(function(data, status, headers, config) {
013 $scope.products = [];
014 });
015 $http.get('/customers/get')
016 .success(function(data, status, headers, config) {
017 $scope.customer = data;
018 })
019 .error(function(data, status, headers, config) {
020 $scope.customer = [];
021 });
022 $http.get('/orders/get')
023 .success(function(data, status, headers, config) {
024 $scope.orders = data;
025 })
026 .error(function(data, status, headers, config) {
027 $scope.orders = [];
028 });

Implementing Helper Functions

Next we add the helper functions, shown in Listing 28.22, that provide functionality for
the AngularJS templates. The setContent() function sets the $scope.content value
effectively changing the view. The setProduct() function is called when a user clicks on a
print image and sets the$scope.product used in the product view. The cartTotal() function

84
will iterate through the products in the users cart and update the $scope.shipping and
return a total used in the cart and review views.

Listing 28.22. cart_app.js-helpers Implementing the CSS Styles for the View HTML
Files

029 $scope.setContent = function(filename){


030 $scope.content = '/static/'+ filename;
031 };
032 $scope.setProduct = function(productId){
033 $scope.product = this.product;
034 $scope.content = '/static/product.html';
035 };
036 $scope.cartTotal = function(){
037 var total = 0;
038 for(var i=0; i<$scope.customer.cart.length; i++){
039 var item = $scope.customer.cart[i];
040 total += item.quantity * item.product[0].price;
041 }
042 $scope.shipping = total*.05;
043 return total+$scope.shipping;
044 };

Adding Items to the Cart

The addToCart() function, shown in Listing 28.23, is called from the template when the
user clicks on the add to cart button. The first thing it does is iterate through the items in
the customer.cartand if it finds the item is there it increments the quantity otherwise it
adds the item to thecustomer.cart array with a quantity of 1.

Once the $scope.customer is updated, an $http POST is called to


the/customers/update/cart route to update the cart. That way the cart is persistent and will
be there even if you user closes the browser or navigates away. On success the view is
switched to cart.html. On failure an alert window is displayed.

Listing 28.23. cart_app.js-addToCart styles.css Implementing the CSS Styles for the
View HTML Files

045 $scope.addToCart = function(productId){


046 var found = false;
047 for(var i=0; i<$scope.customer.cart.length; i++){
048 var item = $scope.customer.cart[i];
049 if (item.product[0]._id == productId){

85
050 item.quantity += 1;
051 found = true;
052 }
053 }
054 if (!found){
055 $scope.customer.cart.push({quantity: 1,
056 product: [this.product]});
057 }
058 $http.post('/customers/update/cart',
059 { updatedCart: $scope.customer.cart })
060 .success(function(data, status, headers, config) {
061 $scope.content = '/static/cart.html';
062 })
063 .error(function(data, status, headers, config) {
064 $window.alert(data);
065 });
066 };

Deleting Items from the Cart

The deleteFromCart() function, shown in Listing 28.24, is called from the cart template
when the user clicks on the remove button. The first thing it does is iterate through the
items in thecustomer.cart and if it finds the item it uses the array.slice(index,1) method to
delete it from the array.

Once the item is removed from $scope.customer.cart, an $http POST is called to


the/customers/update/cart route to update the cart. That way the cart is persistent and will
be there even if you user closes the browser or navigates away. On success the view is
switched to cart.html. On failure an alert window is displayed.

Listing 28.24. cart_app.js-deleteFromCart Implementing the CSS Styles for the


View HTML Files

067 $scope.deleteFromCart = function(productId){


068 for(var i=0; i<$scope.customer.cart.length; i++){
069 var item = $scope.customer.cart[i];
070 if (item.product[0]._id == productId){
071 $scope.customer.cart.splice(i,1);
072 break;
073 }
074 }
075 $http.post('/customers/update/cart',
076 { updatedCart: $scope.customer.cart })

86
077 .success(function(data, status, headers, config) {
078 $scope.content = '/static/cart.html';
079 })
080 .error(function(data, status, headers, config) {
081 $window.alert(data);
082 });
083 };

Checking Out

The checkout() function, shown in Listing 28.25, is called when the user clicks on the
checkout button in the cart view. This illustrates how useful AngularJS data binding
really is. Since the customer information is always kept up to date, all that is necessary is
to send an $http POST request with the parameter {updatedCart:$scope.customer.cart} to
update the cart.

The cart is updated to ensure that any quantity changes made in the cart page will also be
persistent later on if the user backs out of the purchase. If the request is successful then
the view is switched toshipping.html.

Listing 28.25. cart_app.js-checkout Implementing the CSS Styles for the View
HTML Files

084 $scope.checkout = function(){


085 $http.post('/customers/update/cart',
086 { updatedCart: $scope.customer.cart })
087 .success(function(data, status, headers, config) {
088 $scope.content = '/static/shipping.html';
089 })
090 .error(function(data, status, headers, config) {
091 $window.alert(data);
092 });
093 };

Setting Shipping Information

The setShipping() function, shown in Listing 28.26, is called when the user clicks on the
continue to billing button in the cart view. The shipping information needs to be updated
in the database to ensure that it is persistent when the customer leaves the website.
An $http POST method is called to the /customers/update/shipping route. The POST
includes the parameter{updatedShipping:$scope.customer.shipping[0]} in the body. If
the request is successful then the view is switched to billing.html. Otherwise an alert is
displayed.

87
Listing 28.26. cart_app.js-setShipping Implementing the CSS Styles for the View
HTML Files

094 $scope.setShipping = function(){


095 $http.post('/customers/update/shipping',
096 { updatedShipping: $scope.customer.shipping[0] })
097 .success(function(data, status, headers, config) {
098 $scope.content = '/static/billing.html';
099 })
100 .error(function(data, status, headers, config) {
101 $window.alert(data);
102 });
103 };

Verifying Billing

The verifyBilling() function, shown in Listing 28.27, is called when the user clicks on the
verify billing button in the shipping view. The billing information needs to be updated in
the database to ensure that it is persistent when the customer leaves the website. Also the
credit card information can be validated at this point on the server. An $http
POST method is called to the/customers/update/billing route. If the request is successful
then the view is switched toreview.html. Otherwise an alert is displayed.

Listing 28.27. cart_app.js-verifyBilling Implementing the CSS Styles for the View
HTML Files

104 $scope.verifyBilling = function(ccv){


105 $http.post('/customers/update/billing',
106 { updatedBilling: $scope.customer.billing[0], ccv: ccv})
107 .success(function(data, status, headers, config) {
108 $scope.content = '/static/review.html';
109 })
110 .error(function(data, status, headers, config) {
111 $window.alert(data);
112 });
113 };

Making the Purchase

The makePurchase() function, shown in Listing 28.28, is called when the user clicks on
the make purchase button in the billing view. This method sends an $http POST method
to the /orders/addroute on the server.
The orderBilling, orderShipping and orderItems parameters for the POST request. If the

88
request is successful the $scope.customer.cart is initialized to [] since the create order
code will have done that on customer document in MongoDB.

Also, if the request is successful then a new order document will have been created in the
MongoDB database. Therefore another $http request is made this time to
the /orders/get to get the full list of orders including the new one. Then the view is
switched to orders.html.

Listing 28.28. cart_app.js-makePurchase Implementing the CSS Styles for the View
HTML Files

114 $scope.makePurchase = function(){


115 $http.post('/orders/add',
116 { orderBilling: $scope.customer.billing[0],
117 orderShipping: $scope.customer.shipping[0],
118 orderItems: $scope.customer.cart })
119 .success(function(data, status, headers, config) {
120 $scope.customer.cart = [];
121 $http.get('/orders/get')
122 .success(function(data, status, headers, config) {
123 $scope.orders = data;
124 $scope.content = '/static/orders.html';
125 })
126 .error(function(data, status, headers, config) {
127 $scope.orders = [];
128 });
129 })
130 .error(function(data, status, headers, config) {
131 $window.alert(data);
132 });
133 };

The full Controller

The code in Listing 28.29 shows the full myApp code with
the shoppingController initialization and all of the controller code together so you can see
how thing fit together. Notice that theshoppingController definition includes
dependencies on $scope, $http and $window. The$window dependency allows us to add
the browser alert message on processing errors.

89
Listing 28.29. cart_app.js-full Implementing the CSS Styles for the View HTML
Files

001 var app = angular.module('myApp', []);


002 app.controller('shoppingController', ['$scope', '$http', '$window',
003 function($scope, $http, $window) {
004 $scope.months = [1,2,3,4,5,6,7,8,9,10,11,12];
005 $scope.years = [2014,2015,2016,2017,2018,2019,2020];
006 $scope.content = '/static/products.html';
007 $http.get('/products/get')
008 .success(function(data, status, headers, config) {
009 $scope.products = data;
010 $scope.product = data[0];
011 })
012 .error(function(data, status, headers, config) {
013 $scope.products = [];
014 });
015 $http.get('/customers/get')
016 .success(function(data, status, headers, config) {
017 $scope.customer = data;
018 })
019 .error(function(data, status, headers, config) {
020 $scope.customer = [];
021 });
022 $http.get('/orders/get')
023 .success(function(data, status, headers, config) {
024 $scope.orders = data;
025 })
026 .error(function(data, status, headers, config) {
027 $scope.orders = [];
028 });
029 $scope.setContent = function(filename){
030 $scope.content = '/static/'+ filename;
031 };
032 $scope.setProduct = function(productId){
033 $scope.product = this.product;
034 $scope.content = '/static/product.html';
035 };
036 $scope.cartTotal = function(){
037 var total = 0;
038 for(var i=0; i<$scope.customer.cart.length; i++){
039 var item = $scope.customer.cart[i];
040 total += item.quantity * item.product[0].price;

90
041 }
042 $scope.shipping = total*.05;
043 return total+$scope.shipping;
044 };
045 $scope.addToCart = function(productId){
046 var found = false;
047 for(var i=0; i<$scope.customer.cart.length; i++){
048 var item = $scope.customer.cart[i];
049 if (item.product[0]._id == productId){
050 item.quantity += 1;
051 found = true;
052 }
053 }
054 if (!found){
055 $scope.customer.cart.push({quantity: 1,
056 product: [this.product]});
057 }
058 $http.post('/customers/update/cart',
059 { updatedCart: $scope.customer.cart })
060 .success(function(data, status, headers, config) {
061 $scope.content = '/static/cart.html';
062 })
063 .error(function(data, status, headers, config) {
064 $window.alert(data);
065 });
066 };
067 $scope.deleteFromCart = function(productId){
068 for(var i=0; i<$scope.customer.cart.length; i++){
069 var item = $scope.customer.cart[i];
070 if (item.product[0]._id == productId){
071 $scope.customer.cart.splice(i,1);
072 break;
073 }
074 }
075 $http.post('/customers/update/cart',
076 { updatedCart: $scope.customer.cart })
077 .success(function(data, status, headers, config) {
078 $scope.content = '/static/cart.html';
079 })
080 .error(function(data, status, headers, config) {
081 $window.alert(data);
082 });
083 };

91
084 $scope.checkout = function(){
085 $http.post('/customers/update/cart',
086 { updatedCart: $scope.customer.cart })
087 .success(function(data, status, headers, config) {
088 $scope.content = '/static/shipping.html';
089 })
090 .error(function(data, status, headers, config) {
091 $window.alert(data);
092 });
093 };
094 $scope.setShipping = function(){
095 $http.post('/customers/update/shipping',
096 { updatedShipping :$scope.customer.shipping[0] })
097 .success(function(data, status, headers, config) {
098 $scope.content = '/static/billing.html';
099 })
100 .error(function(data, status, headers, config) {
101 $window.alert(data);
102 });
103 };
104 $scope.verifyBilling = function(ccv){
105 $http.post('/customers/update/billing',
106 { updatedBilling: $scope.customer.billing[0], ccv: ccv})
107 .success(function(data, status, headers, config) {
108 $scope.content = '/static/review.html';
109 })
110 .error(function(data, status, headers, config) {
111 $window.alert(data);
112 });
113 };
114 $scope.makePurchase = function(){
115 $http.post('/orders/add',
116 { orderBilling: $scope.customer.billing[0],
117 orderShipping: $scope.customer.shipping[0],
118 orderItems: $scope.customer.cart })
119 .success(function(data, status, headers, config) {
120 $scope.customer.cart = [];
121 $http.get('/orders/get')
122 .success(function(data, status, headers, config) {
123 $scope.orders = data;
124 $scope.content = '/static/orders.html';
125 })
126 .error(function(data, status, headers, config) {

92
127 $scope.orders = [];
128 });
129 })
130 .error(function(data, status, headers, config) {
131 $window.alert(data);
132 });
133 };
134 }]);

93
Initializing the Application

With the application done you need to create the


initial Customer, Order and Product documents in the database. There are several
different methods of doing this such as using a database script, creating an admin
interface for you application, etc. To make it simple this example includes a basic
Node.js script to generate the data that you’ve seen so far.

The code in Listing 28.30 shows a basic Node.js script that first removes the customers,
orders and products collections to clean up if you’ve already been playing around. It then
creates a Customerdocument and an Order document and then adds
several Product documents. The Productdocuments are added to
the Customer document’s cart and the Order document’s items.

Listing 28.30. cart_init.jsl Initializing the Shopping Cart Application Data in


MongoDB

01 var mongoose = require('mongoose');


02 var db = mongoose.connect('mongodb://localhost/cart');
03 require('./models/cart_model.js');
04 var Address = mongoose.model('Address');
05 var Billing = mongoose.model('Billing');
06 var Product = mongoose.model('Product');
07 var ProductQuantity = mongoose.model('ProductQuantity');
08 var Order = mongoose.model('Order');
09 var Customer = mongoose.model('Customer');
10 function addProduct(customer, order, name, imagefile,
11 price, description, instock){
12 var product = new Product({name:name, imagefile:imagefile,
13 price:price, description:description,
14 instock:instock});
15 product.save(function(err, results){
16 order.items.push(new ProductQuantity({quantity: 1,
17 product: [product]}));
18 order.save();
19 customer.save();
20 console.log("Product " + name + " Saved.");
21 });
22 }
23 Product.remove().exec(function(){
24 Order.remove().exec(function(){
25 Customer.remove().exec(function(){
26 var shipping = new Address({

94
27 name: 'Customer A',
28 address: 'Somewhere',
29 city: 'My Town',
30 state: 'CA',
31 zip: '55555'
32 });
33 var billing = new Billing({
34 cardtype: 'Visa',
35 name: 'Customer A',
36 number: '1234567890',
37 expiremonth: 1,
38 expireyear: 2020,
39 address: shipping
40 });
41 var customer = new Customer({
42 userid: 'customerA',
43 shipping: shipping,
44 billing: billing,
45 cart: []
46 });
47 customer.save(function(err, result){
48 var order = new Order({
49 userid: customer.userid,
50 items: [],
51 shipping: customer.shipping,
52 billing: customer.billing
53 });
54 order.save(function(err, result){
55 addProduct(customer, order, 'Delicate Arch Print',
56 'arch.jpg', 12.34,
57 'View of the breathtaking Delicate Arch in Utah',
58 Math.floor((Math.random()*10)+1));
59 addProduct(customer, order, 'Volcano Print',
60 'volcano.jpg', 45.45,
61 'View of a tropical lake backset by a volcano',
62 Math.floor((Math.random()*10)+1));
63 addProduct(customer, order, 'Tikal Structure Print',
64 'pyramid.jpg', 38.52,
65 'Look at the amazing architecture of early America.',
66 Math.floor((Math.random()*10)+1));
67 addProduct(customer, order, 'Glacial Lake Print',
68 'lake.jpg', 77.45,
69 'Vivid color, crystal clear water from glacial runoff.',

95
70 Math.floor((Math.random()*10)+1));
71 });
72 });
73 });
74 });
75 });;

Summary

In this chapter you got to go through the process of implementing a basic shopping cart
using the Node.js, MongoDB and AngularJS web application stack. You were able to
define a solid model in Mongoose to support customers, products, orders and the full
checkout process.

Also in this chapter you should have recognized the benefits of being able to easily
switch between HTML template views in AngularJS and yet have all of your data bound
to that page automatically. The data binding value also became very apparent when we
were able to update the MongoDB database with only a simple function calls because
the $scope data was consistently kept up to data as we added items to the cart and change
shipping and billing information.

Next

In the next chapter you get a bit of different look as you look at some rich internet
application concepts.

96

You might also like