├── .gitignore ├── README.md ├── section-2 ├── css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ └── style.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── index.html ├── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ └── npm.js ├── npm-debug.log ├── package.json ├── page1.html ├── page2.html └── server.js ├── section-3 ├── app.js ├── bin │ └── www ├── npm-debug.log ├── package.json ├── public │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── images │ │ └── bg.jpg │ ├── javascripts │ │ └── bootstrap.js │ └── stylesheets │ │ ├── bootstrap.css │ │ └── style.css ├── routes │ ├── about.js │ ├── contact.js │ ├── index.js │ └── users.js └── views │ ├── about.jade │ ├── contact.jade │ ├── error.jade │ ├── index.jade │ └── layout.jade ├── section-4 ├── app.js ├── bin │ └── www ├── models │ └── user.js ├── npm-debug.log ├── package.json ├── public │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── javascripts │ │ └── bootstrap.js │ └── stylesheets │ │ ├── bootstrap.css │ │ └── style.css ├── routes │ ├── index.js │ └── users.js └── views │ ├── error.jade │ ├── index.jade │ ├── layout.jade │ ├── login.jade │ └── register.jade ├── section-5 ├── app.js ├── bin │ └── www ├── package.json ├── public │ ├── ckeditor │ │ ├── CHANGES.md │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── adapters │ │ │ └── jquery.js │ │ ├── build-config.js │ │ ├── ckeditor.js │ │ ├── config.js │ │ ├── contents.css │ │ ├── lang │ │ │ ├── af.js │ │ │ ├── ar.js │ │ │ ├── bg.js │ │ │ ├── bn.js │ │ │ ├── bs.js │ │ │ ├── ca.js │ │ │ ├── cs.js │ │ │ ├── cy.js │ │ │ ├── da.js │ │ │ ├── de.js │ │ │ ├── el.js │ │ │ ├── en-au.js │ │ │ ├── en-ca.js │ │ │ ├── en-gb.js │ │ │ ├── en.js │ │ │ ├── eo.js │ │ │ ├── es.js │ │ │ ├── et.js │ │ │ ├── eu.js │ │ │ ├── fa.js │ │ │ ├── fi.js │ │ │ ├── fo.js │ │ │ ├── fr-ca.js │ │ │ ├── fr.js │ │ │ ├── gl.js │ │ │ ├── gu.js │ │ │ ├── he.js │ │ │ ├── hi.js │ │ │ ├── hr.js │ │ │ ├── hu.js │ │ │ ├── id.js │ │ │ ├── is.js │ │ │ ├── it.js │ │ │ ├── ja.js │ │ │ ├── ka.js │ │ │ ├── km.js │ │ │ ├── ko.js │ │ │ ├── ku.js │ │ │ ├── lt.js │ │ │ ├── lv.js │ │ │ ├── mk.js │ │ │ ├── mn.js │ │ │ ├── ms.js │ │ │ ├── nb.js │ │ │ ├── nl.js │ │ │ ├── no.js │ │ │ ├── pl.js │ │ │ ├── pt-br.js │ │ │ ├── pt.js │ │ │ ├── ro.js │ │ │ ├── ru.js │ │ │ ├── si.js │ │ │ ├── sk.js │ │ │ ├── sl.js │ │ │ ├── sq.js │ │ │ ├── sr-latn.js │ │ │ ├── sr.js │ │ │ ├── sv.js │ │ │ ├── th.js │ │ │ ├── tr.js │ │ │ ├── tt.js │ │ │ ├── ug.js │ │ │ ├── uk.js │ │ │ ├── vi.js │ │ │ ├── zh-cn.js │ │ │ └── zh.js │ │ ├── plugins │ │ │ ├── a11yhelp │ │ │ │ └── dialogs │ │ │ │ │ ├── a11yhelp.js │ │ │ │ │ └── lang │ │ │ │ │ ├── _translationstatus.txt │ │ │ │ │ ├── af.js │ │ │ │ │ ├── ar.js │ │ │ │ │ ├── bg.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── cy.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── el.js │ │ │ │ │ ├── en-gb.js │ │ │ │ │ ├── en.js │ │ │ │ │ ├── eo.js │ │ │ │ │ ├── es.js │ │ │ │ │ ├── et.js │ │ │ │ │ ├── fa.js │ │ │ │ │ ├── fi.js │ │ │ │ │ ├── fr-ca.js │ │ │ │ │ ├── fr.js │ │ │ │ │ ├── gl.js │ │ │ │ │ ├── gu.js │ │ │ │ │ ├── he.js │ │ │ │ │ ├── hi.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── hu.js │ │ │ │ │ ├── id.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── km.js │ │ │ │ │ ├── ko.js │ │ │ │ │ ├── ku.js │ │ │ │ │ ├── lt.js │ │ │ │ │ ├── lv.js │ │ │ │ │ ├── mk.js │ │ │ │ │ ├── mn.js │ │ │ │ │ ├── nb.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── no.js │ │ │ │ │ ├── pl.js │ │ │ │ │ ├── pt-br.js │ │ │ │ │ ├── pt.js │ │ │ │ │ ├── ro.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── si.js │ │ │ │ │ ├── sk.js │ │ │ │ │ ├── sl.js │ │ │ │ │ ├── sq.js │ │ │ │ │ ├── sr-latn.js │ │ │ │ │ ├── sr.js │ │ │ │ │ ├── sv.js │ │ │ │ │ ├── th.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── tt.js │ │ │ │ │ ├── ug.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── vi.js │ │ │ │ │ ├── zh-cn.js │ │ │ │ │ └── zh.js │ │ │ ├── about │ │ │ │ └── dialogs │ │ │ │ │ ├── about.js │ │ │ │ │ ├── hidpi │ │ │ │ │ └── logo_ckeditor.png │ │ │ │ │ └── logo_ckeditor.png │ │ │ ├── clipboard │ │ │ │ └── dialogs │ │ │ │ │ └── paste.js │ │ │ ├── dialog │ │ │ │ └── dialogDefinition.js │ │ │ ├── icons.png │ │ │ ├── icons_hidpi.png │ │ │ ├── image │ │ │ │ ├── dialogs │ │ │ │ │ └── image.js │ │ │ │ └── images │ │ │ │ │ └── noimage.png │ │ │ ├── link │ │ │ │ ├── dialogs │ │ │ │ │ ├── anchor.js │ │ │ │ │ └── link.js │ │ │ │ └── images │ │ │ │ │ ├── anchor.png │ │ │ │ │ └── hidpi │ │ │ │ │ └── anchor.png │ │ │ ├── magicline │ │ │ │ └── images │ │ │ │ │ ├── hidpi │ │ │ │ │ ├── icon-rtl.png │ │ │ │ │ └── icon.png │ │ │ │ │ ├── icon-rtl.png │ │ │ │ │ └── icon.png │ │ │ ├── pastefromword │ │ │ │ └── filter │ │ │ │ │ └── default.js │ │ │ ├── scayt │ │ │ │ ├── LICENSE.md │ │ │ │ ├── README.md │ │ │ │ └── dialogs │ │ │ │ │ ├── options.js │ │ │ │ │ └── toolbar.css │ │ │ ├── specialchar │ │ │ │ └── dialogs │ │ │ │ │ ├── lang │ │ │ │ │ ├── _translationstatus.txt │ │ │ │ │ ├── af.js │ │ │ │ │ ├── ar.js │ │ │ │ │ ├── bg.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── cy.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── el.js │ │ │ │ │ ├── en-gb.js │ │ │ │ │ ├── en.js │ │ │ │ │ ├── eo.js │ │ │ │ │ ├── es.js │ │ │ │ │ ├── et.js │ │ │ │ │ ├── fa.js │ │ │ │ │ ├── fi.js │ │ │ │ │ ├── fr-ca.js │ │ │ │ │ ├── fr.js │ │ │ │ │ ├── gl.js │ │ │ │ │ ├── he.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── hu.js │ │ │ │ │ ├── id.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── km.js │ │ │ │ │ ├── ku.js │ │ │ │ │ ├── lt.js │ │ │ │ │ ├── lv.js │ │ │ │ │ ├── nb.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── no.js │ │ │ │ │ ├── pl.js │ │ │ │ │ ├── pt-br.js │ │ │ │ │ ├── pt.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── si.js │ │ │ │ │ ├── sk.js │ │ │ │ │ ├── sl.js │ │ │ │ │ ├── sq.js │ │ │ │ │ ├── sv.js │ │ │ │ │ ├── th.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── tt.js │ │ │ │ │ ├── ug.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── vi.js │ │ │ │ │ ├── zh-cn.js │ │ │ │ │ └── zh.js │ │ │ │ │ └── specialchar.js │ │ │ ├── table │ │ │ │ └── dialogs │ │ │ │ │ └── table.js │ │ │ ├── tabletools │ │ │ │ └── dialogs │ │ │ │ │ └── tableCell.js │ │ │ └── wsc │ │ │ │ ├── LICENSE.md │ │ │ │ ├── README.md │ │ │ │ └── dialogs │ │ │ │ ├── ciframe.html │ │ │ │ ├── tmpFrameset.html │ │ │ │ ├── wsc.css │ │ │ │ ├── wsc.js │ │ │ │ └── wsc_ie.js │ │ ├── samples │ │ │ ├── ajax.html │ │ │ ├── api.html │ │ │ ├── appendto.html │ │ │ ├── assets │ │ │ │ ├── inlineall │ │ │ │ │ └── logo.png │ │ │ │ ├── outputxhtml │ │ │ │ │ └── outputxhtml.css │ │ │ │ ├── posteddata.php │ │ │ │ ├── sample.jpg │ │ │ │ └── uilanguages │ │ │ │ │ └── languages.js │ │ │ ├── datafiltering.html │ │ │ ├── divreplace.html │ │ │ ├── index.html │ │ │ ├── inlineall.html │ │ │ ├── inlinebycode.html │ │ │ ├── inlinetextarea.html │ │ │ ├── jquery.html │ │ │ ├── plugins │ │ │ │ ├── dialog │ │ │ │ │ ├── assets │ │ │ │ │ │ └── my_dialog.js │ │ │ │ │ └── dialog.html │ │ │ │ ├── enterkey │ │ │ │ │ └── enterkey.html │ │ │ │ ├── htmlwriter │ │ │ │ │ ├── assets │ │ │ │ │ │ └── outputforflash │ │ │ │ │ │ │ ├── outputforflash.fla │ │ │ │ │ │ │ ├── outputforflash.swf │ │ │ │ │ │ │ └── swfobject.js │ │ │ │ │ ├── outputforflash.html │ │ │ │ │ └── outputhtml.html │ │ │ │ ├── magicline │ │ │ │ │ └── magicline.html │ │ │ │ ├── toolbar │ │ │ │ │ └── toolbar.html │ │ │ │ └── wysiwygarea │ │ │ │ │ └── fullpage.html │ │ │ ├── readonly.html │ │ │ ├── replacebyclass.html │ │ │ ├── replacebycode.html │ │ │ ├── sample.css │ │ │ ├── sample.js │ │ │ ├── sample_posteddata.php │ │ │ ├── tabindex.html │ │ │ ├── uicolor.html │ │ │ ├── uilanguages.html │ │ │ └── xhtmlstyle.html │ │ ├── skins │ │ │ └── moono │ │ │ │ ├── dialog.css │ │ │ │ ├── dialog_ie.css │ │ │ │ ├── dialog_ie7.css │ │ │ │ ├── dialog_ie8.css │ │ │ │ ├── dialog_iequirks.css │ │ │ │ ├── editor.css │ │ │ │ ├── editor_gecko.css │ │ │ │ ├── editor_ie.css │ │ │ │ ├── editor_ie7.css │ │ │ │ ├── editor_ie8.css │ │ │ │ ├── editor_iequirks.css │ │ │ │ ├── icons.png │ │ │ │ ├── icons_hidpi.png │ │ │ │ ├── images │ │ │ │ ├── arrow.png │ │ │ │ ├── close.png │ │ │ │ ├── hidpi │ │ │ │ │ ├── close.png │ │ │ │ │ ├── lock-open.png │ │ │ │ │ ├── lock.png │ │ │ │ │ └── refresh.png │ │ │ │ ├── lock-open.png │ │ │ │ ├── lock.png │ │ │ │ └── refresh.png │ │ │ │ └── readme.md │ │ └── styles.js │ ├── images │ │ ├── nodebloglogo.png │ │ └── uploads │ │ │ └── noimage.png │ └── stylesheets │ │ └── style.css ├── routes │ ├── categories.js │ ├── index.js │ └── posts.js └── views │ ├── addcategory.jade │ ├── addpost.jade │ ├── error.jade │ ├── index.jade │ ├── layout.jade │ └── show.jade └── section-6 ├── .jshintrc-client ├── .jshintrc-server ├── Gruntfile.js ├── LICENSE ├── README.md ├── app.js ├── config.js ├── layouts ├── account.jade ├── admin.jade └── default.jade ├── models.js ├── npm-debug.log ├── package.json ├── passport.js ├── public ├── favicon.ico ├── layouts │ ├── admin.js │ ├── admin.less │ ├── core.js │ ├── core.less │ └── ie-sucks.js ├── less │ ├── bootstrap-build.less │ ├── bootstrap-theme.less │ ├── bootstrap-vars.less │ ├── font-awesome-build.less │ └── font-awesome-vars.less ├── media │ ├── ajax-pulse.gif │ ├── logo-symbol-32x32.png │ └── logo-symbol-64x64.png └── views │ ├── about │ └── index.less │ ├── account │ ├── index.js │ ├── index.less │ ├── settings │ │ └── index.js │ └── verification │ │ ├── index.js │ │ └── index.less │ ├── admin │ ├── accounts │ │ ├── details.js │ │ ├── details.less │ │ ├── index.js │ │ └── index.less │ ├── admin-groups │ │ ├── details.js │ │ ├── details.less │ │ ├── index.js │ │ └── index.less │ ├── administrators │ │ ├── details.js │ │ ├── details.less │ │ ├── index.js │ │ └── index.less │ ├── categories │ │ ├── details.js │ │ ├── index.js │ │ └── index.less │ ├── index.less │ ├── statuses │ │ ├── details.js │ │ ├── index.js │ │ └── index.less │ └── users │ │ ├── details.js │ │ ├── index.js │ │ └── index.less │ ├── contact │ ├── index.js │ └── index.less │ ├── index.less │ ├── login │ ├── forgot │ │ └── index.js │ ├── index.js │ └── reset │ │ └── index.js │ └── signup │ ├── index.js │ ├── index.less │ └── social.js ├── routes.js ├── schema ├── Account.js ├── Admin.js ├── AdminGroup.js ├── Category.js ├── LoginAttempt.js ├── Note.js ├── Status.js ├── StatusLog.js ├── User.js └── plugins │ └── pagedFind.js ├── util ├── sendmail │ └── index.js ├── slugify │ └── index.js └── workflow │ └── index.js └── views ├── about ├── index.jade └── index.js ├── account ├── index.jade ├── index.js ├── settings │ ├── index.jade │ └── index.js └── verification │ ├── email-html.jade │ ├── email-text.jade │ ├── index.jade │ └── index.js ├── admin ├── accounts │ ├── details.jade │ ├── index.jade │ └── index.js ├── admin-groups │ ├── details.jade │ ├── index.jade │ └── index.js ├── administrators │ ├── details.jade │ ├── index.jade │ └── index.js ├── categories │ ├── details.jade │ ├── index.jade │ └── index.js ├── index.jade ├── index.js ├── search │ └── index.js ├── statuses │ ├── details.jade │ ├── index.jade │ └── index.js └── users │ ├── details.jade │ ├── index.jade │ └── index.js ├── contact ├── email-html.jade ├── email-text.jade ├── index.jade └── index.js ├── http ├── 404.jade ├── 500.jade └── index.js ├── index.jade ├── index.js ├── login ├── forgot │ ├── email-html.jade │ ├── email-text.jade │ ├── index.jade │ └── index.js ├── index.jade ├── index.js └── reset │ ├── index.jade │ └── index.js ├── logout └── index.js └── signup ├── email-html.jade ├── email-text.jade ├── index.jade ├── index.js └── social.jade /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### WebStorm ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 5 | 6 | *.iml 7 | 8 | ## Directory-based project format: 9 | .idea/ 10 | # if you remove the above rule, at least ignore the following: 11 | 12 | # User-specific stuff: 13 | # .idea/workspace.xml 14 | # .idea/tasks.xml 15 | # .idea/dictionaries 16 | 17 | # Sensitive or high-churn files: 18 | # .idea/dataSources.ids 19 | # .idea/dataSources.xml 20 | # .idea/sqlDataSources.xml 21 | # .idea/dynamic.xml 22 | # .idea/uiDesigner.xml 23 | 24 | # Gradle: 25 | # .idea/gradle.xml 26 | # .idea/libraries 27 | 28 | # Mongo Explorer plugin: 29 | # .idea/mongoSettings.xml 30 | 31 | ## File-based project format: 32 | *.ipr 33 | *.iws 34 | 35 | ## Plugin-specific files: 36 | 37 | # IntelliJ 38 | /out/ 39 | 40 | # mpeltonen/sbt-idea plugin 41 | .idea_modules/ 42 | 43 | # JIRA plugin 44 | atlassian-ide-plugin.xml 45 | 46 | # Crashlytics plugin (for Android Studio and IntelliJ) 47 | com_crashlytics_export_strings.xml 48 | crashlytics.properties 49 | crashlytics-build.properties 50 | 51 | 52 | # Additional file formats 53 | *.DS_Store 54 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # learning-nodejs-10-projects 2 | Following the "Learning NodeJS By Building 10 Projects" on Udemy 3 | 4 | 5 | Section 2 - Simple Web Server 6 | 7 | Section 3 - Basic Express Website 8 | 9 | Section 4 - User Login System 10 | 11 | Section 5 - Node Blog System 12 | 13 | Section 6 - Community Events 14 | 15 | Section 7 - Bookstore 16 | 17 | Section 8 - ChatIO 18 | 19 | Section 9 - FindaDoc Directory 20 | 21 | Section 10 - Portfolio App 22 | 23 | Section 11 - eLearning System 24 | -------------------------------------------------------------------------------- /section-2/css/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background:#f4f4f4; 3 | font-family:arial; 4 | } 5 | 6 | .clr{ 7 | clear:both; 8 | } 9 | 10 | .container{ 11 | margin:20px auto; 12 | overflow:auto; 13 | width:900px; 14 | background:#fff; 15 | border:1px solid #ccc; 16 | padding:15px; 17 | } 18 | 19 | header{ 20 | background:#ccc; 21 | padding:20px; 22 | } 23 | 24 | header h1{ 25 | padding:0; 26 | margin:0; 27 | } 28 | 29 | footer{ 30 | text-align: center; 31 | } 32 | 33 | ul{ 34 | padding:0; 35 | margin:0; 36 | margin-top:15px; 37 | padding:10px; 38 | background: #f4f4f4; 39 | border:1px #ccc solid; 40 | overflow:auto; 41 | } 42 | 43 | ul li{ 44 | float:left; 45 | padding:0 20px 0 0; 46 | list-style: none; 47 | } 48 | 49 | a{ 50 | color:#666; 51 | text-decoration: none; 52 | } 53 | 54 | a:hover{ 55 | color:#000; 56 | } -------------------------------------------------------------------------------- /section-2/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-2/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /section-2/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-2/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /section-2/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-2/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /section-2/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-2/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /section-2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Index Page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Node.js Project

18 |
19 | 24 |
25 |
26 |

Welcome To The Homepage!

27 | 28 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed hendrerit tristique turpis, vel mollis leo 29 | faucibus at. Phasellus vulputate, nisl a lobortis pharetra, justo lorem maximus leo, ut fringilla sem urna 30 | sed nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse sed ligula viverra, sagittis 31 | ex non, cursus dui. Suspendisse et risus pretium massa efficitur vulputate. Nulla finibus massa quis nulla 32 | eleifend, at lacinia lacus dictum. In hac habitasse platea dictumst. 33 |

34 | 35 |

36 | In non diam et libero sodales porta. Donec eleifend posuere convallis. Nulla id pharetra ex, sit amet semper 37 | elit. Nulla dapibus vel purus quis iaculis. Nulla facilisi. Nulla facilisi. Proin ultricies id felis non 38 | maximus. Vestibulum auctor vel arcu eu lobortis. 39 |

40 | 41 |

42 | Integer aliquet mattis mi vel sodales. Sed ultrices elementum eros, et euismod metus pretium id. Nam vitae 43 | neque vel ipsum tincidunt eleifend id cursus enim. Pellentesque habitant morbi tristique senectus et netus 44 | et malesuada fames ac turpis egestas. Praesent placerat quam ac augue viverra, a vehicula turpis eleifend. 45 | Sed iaculis congue odio nec tristique. Nulla finibus facilisis neque, id hendrerit massa consequat 46 | vitae. 47 |

48 |
49 |
50 | 53 | 54 | -------------------------------------------------------------------------------- /section-2/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /section-2/npm-debug.log: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'start' ] 3 | 2 info using npm@1.4.28 4 | 3 info using node@v0.10.35 5 | 4 verbose run-script [ 'prestart', 'start', 'poststart' ] 6 | 5 info prestart section-2@1.0.0 7 | 6 info start section-2@1.0.0 8 | 7 verbose unsafe-perm in lifecycle true 9 | 8 info section-2@1.0.0 Failed to exec start script 10 | 9 error section-2@1.0.0 start: `node server.js` 11 | 9 error Exit status 8 12 | 10 error Failed at the section-2@1.0.0 start script. 13 | 10 error This is most likely a problem with the section-2 package, 14 | 10 error not with npm itself. 15 | 10 error Tell the author that this fails on your system: 16 | 10 error node server.js 17 | 10 error You can get their info via: 18 | 10 error npm owner ls section-2 19 | 10 error There is likely additional logging output above. 20 | 11 error System Darwin 13.4.0 21 | 12 error command "/usr/local/bin/node" "/usr/local/bin/npm" "start" 22 | 13 error cwd /Users/aprovis/dev/personal-dev/learning-nodejs-10-projects/section-2 23 | 14 error node -v v0.10.35 24 | 15 error npm -v 1.4.28 25 | 16 error code ELIFECYCLE 26 | 17 verbose exit [ 1, true ] 27 | -------------------------------------------------------------------------------- /section-2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "section-2", 3 | "version": "1.0.0", 4 | "description": "This is a very simple web server", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "author": "Andrew Provis", 11 | "license": "GPL" 12 | } 13 | -------------------------------------------------------------------------------- /section-2/page1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Page 1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Node.js Project

18 |
19 | 24 |
25 |
26 |

Welcome To Page 1

27 | 28 |

Suspendisse sed ligula viverra, sagittis ex non, cursus dui. Suspendisse et risus pretium massa efficitur 29 | vulputate. Nulla finibus massa quis nulla eleifend, at lacinia lacus dictum. In hac habitasse platea 30 | dictumst. 31 |

32 | 33 |

34 | Nulla dapibus vel purus quis iaculis. Nulla facilisi. Nulla facilisi. Proin ultricies id felis non maximus. 35 | Vestibulum auctor vel arcu eu lobortis. 36 |

37 | 38 |

39 | A vehicula turpis eleifend. Sed iaculis congue odio nec tristique. Nulla finibus facilisis neque, id 40 | hendrerit massa consequat vitae. 41 |

42 |
43 |
44 | 47 | 48 | -------------------------------------------------------------------------------- /section-2/page2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Page 2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Node.js Project

18 |
19 | 24 |
25 |
26 |

Welcome To Page 2

27 | 28 |

29 | In non diam et libero sodales porta. Donec eleifend posuere convallis. Nulla id pharetra ex, sit amet semper 30 | elit. Nulla dapibus vel purus quis iaculis. Nulla facilisi. Nulla facilisi. Proin ultricies id felis non 31 | maximus. Vestibulum auctor vel arcu eu lobortis. 32 |

33 | 34 |

35 | Integer aliquet mattis mi vel sodales. Sed ultrices elementum eros, et euismod metus pretium id. Nam vitae 36 | neque vel ipsum tincidunt eleifend id cursus enim. Pellentesque habitant morbi tristique senectus et netus 37 | et malesuada fames ac turpis egestas. Praesent placerat quam ac augue viverra, a vehicula turpis eleifend. 38 | Sed iaculis congue odio nec tristique. Nulla finibus facilisis neque, id hendrerit massa consequat 39 | vitae. 40 |

41 |
42 |
43 | 46 | 47 | -------------------------------------------------------------------------------- /section-2/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by aprovis on 05/05/2015. 3 | */ 4 | 5 | var http = require('http'); 6 | var url = require('url'); 7 | var path = require('path'); 8 | var fs = require('fs'); 9 | 10 | // Array of Mime Types 11 | var mimeTypes = { 12 | "html" : "text/html", 13 | "jpeg" : "image/jpeg", 14 | "jpg" : "image/jpeg", 15 | "png" : "image/png", 16 | "js" : "text/javascript", 17 | "css" : "text/css" 18 | }; 19 | 20 | // Create Server 21 | 22 | http.createServer(function(req, res){ 23 | var uri = url.parse(req.url).pathname; 24 | var fileName = path.join(process.cwd(), unescape(uri)); 25 | console.log('Loading ' + uri); 26 | var stats; 27 | 28 | try{ 29 | stats = fs.lstatSync(fileName); 30 | } catch (e) { 31 | res.writeHead(404, {'Content-type' : 'text/plain'}); 32 | res.write('404 Not Found\n'); 33 | res.end(); 34 | return; 35 | } 36 | 37 | // Check if file/directory 38 | if(stats.isFile()){ 39 | var mimeType = mimeTypes[path.extname(fileName).split(".").reverse()[0]]; 40 | res.writeHead(200, {'Content-type' : mimeType}); 41 | 42 | var fileStream = fs.createReadStream(fileName); 43 | 44 | fileStream.pipe(res); 45 | } else if(stats.isDirectory()){ 46 | res.writeHead(302, { 47 | 'Location' : 'index.html' 48 | }); 49 | res.end(); 50 | } else { 51 | res.writeHead(500, {'Content-type' : 'text/plain'}); 52 | res.write('500 Internal Error'); 53 | res.end(); 54 | } 55 | }).listen(3000); -------------------------------------------------------------------------------- /section-3/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | var nodemailer = require('nodemailer'); 8 | 9 | var routes = require('./routes/index'); 10 | var about = require('./routes/about'); 11 | var contact = require('./routes/contact'); 12 | 13 | var app = express(); 14 | 15 | // view engine setup 16 | app.set('views', path.join(__dirname, 'views')); 17 | app.set('view engine', 'jade'); 18 | 19 | // uncomment after placing your favicon in /public 20 | //app.use(favicon(__dirname + '/public/favicon.ico')); 21 | app.use(logger('dev')); 22 | app.use(bodyParser.json()); 23 | app.use(bodyParser.urlencoded({ extended: false })); 24 | app.use(cookieParser()); 25 | app.use(express.static(path.join(__dirname, 'public'))); 26 | 27 | app.use('/', routes); 28 | app.use('/about', about); 29 | app.use('/contact', contact); 30 | 31 | // catch 404 and forward to error handler 32 | app.use(function(req, res, next) { 33 | var err = new Error('Not Found'); 34 | err.status = 404; 35 | next(err); 36 | }); 37 | 38 | // error handlers 39 | 40 | // development error handler 41 | // will print stacktrace 42 | if (app.get('env') === 'development') { 43 | app.use(function(err, req, res, next) { 44 | res.status(err.status || 500); 45 | res.render('error', { 46 | message: err.message, 47 | error: err 48 | }); 49 | }); 50 | } 51 | 52 | // production error handler 53 | // no stacktraces leaked to user 54 | app.use(function(err, req, res, next) { 55 | res.status(err.status || 500); 56 | res.render('error', { 57 | message: err.message, 58 | error: {} 59 | }); 60 | }); 61 | 62 | 63 | module.exports = app; 64 | -------------------------------------------------------------------------------- /section-3/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('section-3:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /section-3/npm-debug.log: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'start' ] 3 | 2 info using npm@1.4.28 4 | 3 info using node@v0.10.35 5 | 4 verbose run-script [ 'prestart', 'start', 'poststart' ] 6 | 5 info prestart section-3@1.0.0 7 | 6 info start section-3@1.0.0 8 | 7 verbose unsafe-perm in lifecycle true 9 | 8 info section-3@1.0.0 Failed to exec start script 10 | 9 error section-3@1.0.0 start: `node ./bin/www` 11 | 9 error Exit status 8 12 | 10 error Failed at the section-3@1.0.0 start script. 13 | 10 error This is most likely a problem with the section-3 package, 14 | 10 error not with npm itself. 15 | 10 error Tell the author that this fails on your system: 16 | 10 error node ./bin/www 17 | 10 error You can get their info via: 18 | 10 error npm owner ls section-3 19 | 10 error There is likely additional logging output above. 20 | 11 error System Darwin 13.4.0 21 | 12 error command "/usr/local/bin/node" "/usr/local/bin/npm" "start" 22 | 13 error cwd /Users/aprovis/dev/personal-dev/learning-nodejs-10-projects/section-3 23 | 14 error node -v v0.10.35 24 | 15 error npm -v 1.4.28 25 | 16 error code ELIFECYCLE 26 | 17 verbose exit [ 1, true ] 27 | -------------------------------------------------------------------------------- /section-3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "section-3", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.12.0", 10 | "cookie-parser": "~1.3.4", 11 | "debug": "~2.1.1", 12 | "express": "~4.12.2", 13 | "jade": "~1.9.2", 14 | "morgan": "~1.5.1", 15 | "serve-favicon": "~2.2.0", 16 | "nodemailer" : "*" 17 | } 18 | } -------------------------------------------------------------------------------- /section-3/public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-3/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /section-3/public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-3/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /section-3/public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-3/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /section-3/public/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-3/public/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /section-3/public/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-3/public/images/bg.jpg -------------------------------------------------------------------------------- /section-3/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | 5 | .jumbotron { 6 | background: url('../images/bg.jpg') no-repeat; 7 | } 8 | 9 | footer { 10 | background: #f4f4f4; 11 | height: 40px; 12 | text-align: center; 13 | margin-top: 30px; 14 | padding-top: 10px; 15 | overflow: auto; 16 | } -------------------------------------------------------------------------------- /section-3/routes/about.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by aprovis on 05/05/2015. 3 | */ 4 | 5 | var express = require('express'); 6 | var router = express.Router(); 7 | 8 | /* GET home page. */ 9 | router.get('/', function(req, res, next) { 10 | res.render('about', { title: 'About' }); 11 | }); 12 | 13 | module.exports = router; 14 | -------------------------------------------------------------------------------- /section-3/routes/contact.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by aprovis on 05/05/2015. 3 | */ 4 | 5 | var express = require('express'); 6 | var router = express.Router(); 7 | var nodemailer = require('nodemailer'); 8 | 9 | /* GET home page. */ 10 | router.get('/', function(req, res, next) { 11 | res.render('contact', { title: 'Contact' }); 12 | }); 13 | 14 | router.post('/send', function(req, res, next) { 15 | var transporter = nodemailer.createTransport({ 16 | service: 'GMail', 17 | auth: { 18 | user: 'techguyinfo@gmail.com', 19 | pass: 'something' 20 | } 21 | }); 22 | 23 | var mailOptions = { 24 | from: 'Example ', 25 | to: 'example@example.com', 26 | subject: 'Website Submission', 27 | text: 'You have a new submission with the following details... Name: ' + req.body.name + ', Email: ' + req.body.email + ' Message: ' +req.body.message , 28 | html: '

You got a new submission with the following details...

' 29 | }; 30 | 31 | transporter.sendMail(mailOptions, function(error, info){ 32 | if (error) { 33 | console.log(error); 34 | res.redirect('/'); 35 | } else { 36 | console.log('Message Sent: ' + info.response); 37 | res.redirect('/'); 38 | } 39 | }); 40 | }); 41 | 42 | module.exports = router; 43 | -------------------------------------------------------------------------------- /section-3/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Home' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /section-3/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /section-3/views/about.jade: -------------------------------------------------------------------------------- 1 | // 2 | Created by aprovis on 05/05/2015. 3 | 4 | extends layout 5 | 6 | block content 7 | .container 8 | h1 About Us 9 | p Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec fringilla lacus sed faucibus sodales. Suspendisse interdum elit a augue vulputate sodales. Quisque id mattis nibh. Vestibulum nec suscipit dui. Vivamus laoreet odio vel massa suscipit hendrerit. Fusce interdum velit euismod justo ultrices imperdiet. Ut mattis justo est, a faucibus lacus suscipit congue. 10 | ul 11 | li Vivamus nec scelerisque 12 | li Cras efficitur 13 | li Nam lectus elit 14 | p Etiam aliquam, justo iaculis tempor tempor, risus nisl volutpat augue, ac mollis lorem neque ac tortor. Phasellus libero sapien, ornare in risus vel, posuere ornare purus. Duis in molestie sem. Mauris condimentum posuere velit, consequat tempus lacus varius nec. Sed non scelerisque eros. Nullam gravida erat eget sodales posuere. Vivamus sed justo viverra, dictum sapien id, ultrices lorem. Nulla eleifend eleifend porttitor. Proin dignissim, elit quis posuere faucibus, lacus sapien lobortis leo, at pellentesque libero turpis in nunc. -------------------------------------------------------------------------------- /section-3/views/contact.jade: -------------------------------------------------------------------------------- 1 | // 2 | Created by aprovis on 05/05/2015. 3 | 4 | extends layout 5 | 6 | block content 7 | .container 8 | form(method='post', action='contact/send') 9 | h1 Contact 10 | .form-group 11 | label Name 12 | input.form-control(type='text' name='name', placeholder='Enter Name') 13 | .form-group 14 | label Email 15 | input.form-control(type='email' name='email', placeholder='Enter Email') 16 | .form-group 17 | label Message 18 | textarea.form-control(name='message', placeholder='Enter Message...') 19 | button.btn.btn-default(type='submit') Submit -------------------------------------------------------------------------------- /section-3/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /section-3/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | .jumbotron 5 | .container 6 | h1 How May I Help You? 7 | p Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec fringilla lacus sed faucibus sodales. Suspendisse interdum elit a augue vulputate sodales. Quisque id mattis nibh. Vestibulum nec suscipit dui. Vivamus laoreet odio vel massa suscipit hendrerit. Fusce interdum velit euismod justo ultrices imperdiet. Ut mattis justo est, a faucibus lacus suscipit congue. 8 | a.btn.btn-primary.btn-lg(href='#') Learn more » 9 | .container 10 | .row 11 | .col-md-4 12 | h2 About Us 13 | p Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec fringilla lacus sed faucibus sodales. Suspendisse interdum elit a augue vulputate sodales. Quisque id mattis nibh. Vestibulum nec suscipit dui. Vivamus laoreet odio vel massa suscipit hendrerit. Fusce interdum velit euismod justo ultrices imperdiet. Ut mattis justo est, a faucibus lacus suscipit congue. 14 | a.btn.btn-default(href='#') View Details 15 | .col-md-4 16 | h2 Services 17 | p Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec fringilla lacus sed faucibus sodales. Suspendisse interdum elit a augue vulputate sodales. Quisque id mattis nibh. Vestibulum nec suscipit dui. Vivamus laoreet odio vel massa suscipit hendrerit. Fusce interdum velit euismod justo ultrices imperdiet. Ut mattis justo est, a faucibus lacus suscipit congue. 18 | a.btn.btn-default(href='#') View Details 19 | .col-md-4 20 | h2 Get In Touch 21 | p Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec fringilla lacus sed faucibus sodales. Suspendisse interdum elit a augue vulputate sodales. Quisque id mattis nibh. Vestibulum nec suscipit dui. Vivamus laoreet odio vel massa suscipit hendrerit. Fusce interdum velit euismod justo ultrices imperdiet. Ut mattis justo est, a faucibus lacus suscipit congue. 22 | a.btn.btn-default(href='#') View Details -------------------------------------------------------------------------------- /section-3/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title #{title}: Express Website 5 | meta(charset='utf-8') 6 | meta(http-equiv='X-UA-Compatible', content='IE=edge') 7 | meta(name='viewport', content='width=device-width, initial-scale=1.0') 8 | link(rel='stylesheet', href='/stylesheets/bootstrap.css') 9 | link(rel='stylesheet', href='/stylesheets/style.css') 10 | body 11 | nav.navbar.navbar-default.navbar-fixed-top 12 | .container 13 | .navbar-header 14 | button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='#navbar', aria-expanded='false', aria-controls='navbar') 15 | span.sr-only Toggle navigation 16 | span.icon-bar 17 | span.icon-bar 18 | span.icon-bar 19 | a.navbar-brand(href='#') Express Website 20 | #navbar.collapse.navbar-collapse 21 | ul.nav.navbar-nav.navbar-right 22 | li(class=(title === 'Home' ? 'active' : '')) 23 | a(href='/') Home 24 | li(class=(title === 'About' ? 'active' : '')) 25 | a(href='/about') About Us 26 | li(class=(title === 'Contact' ? 'active' : '')) 27 | a(href='/contact') Contact 28 | block content 29 | footer 30 | p © 2015, All Rights Reserved 31 | 32 | script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js') 33 | script(src='javascripts/bootstrap.js') -------------------------------------------------------------------------------- /section-4/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('section-4:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /section-4/models/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by aprovis on 06/05/2015. 3 | */ 4 | 5 | var mongoose = require('mongoose'); 6 | var bcrypt = require('bcryptjs'); 7 | 8 | mongoose.connect('mongodb://localhost/nodeauth'); 9 | 10 | var db = mongoose.connection; 11 | 12 | // User Schema 13 | var UserSchema = mongoose.Schema({ 14 | username: { 15 | type: String, 16 | index: true 17 | }, 18 | password: { 19 | type: String, 20 | required: true, 21 | bcrypt: true 22 | }, 23 | email: { 24 | type: String 25 | }, 26 | name: { 27 | type: String 28 | }, 29 | profileimage: { 30 | type: String 31 | } 32 | }); 33 | 34 | var User = module.exports = mongoose.model('User', UserSchema); 35 | 36 | module.exports.comparePassword = function(candidatePassword, hash, callback) { 37 | bcrypt.compare(candidatePassword, hash, function(err, isMatch) { 38 | if (err) return callback(err); 39 | callback(null, isMatch); 40 | }); 41 | }; 42 | 43 | module.exports.getUserByUsername = function(username, callback) { 44 | var query = {username: username}; 45 | User.findOne(query, callback); 46 | }; 47 | 48 | module.exports.getUserById = function(id, callback) { 49 | User.findById(id, callback); 50 | }; 51 | 52 | module.exports.createUser = function(newUser, callback) { 53 | bcrypt.hash(newUser.password, 10, function(err, hash) { 54 | if(err) throw err; 55 | 56 | // Set hashed password 57 | newUser.password = hash; 58 | 59 | // Create user 60 | newUser.save(callback); 61 | }); 62 | }; -------------------------------------------------------------------------------- /section-4/npm-debug.log: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'start' ] 3 | 2 info using npm@1.4.28 4 | 3 info using node@v0.10.35 5 | 4 verbose run-script [ 'prestart', 'start', 'poststart' ] 6 | 5 info prestart section-4@1.0.0 7 | 6 info start section-4@1.0.0 8 | 7 verbose unsafe-perm in lifecycle true 9 | 8 info section-4@1.0.0 Failed to exec start script 10 | 9 error section-4@1.0.0 start: `node ./bin/www` 11 | 9 error Exit status 8 12 | 10 error Failed at the section-4@1.0.0 start script. 13 | 10 error This is most likely a problem with the section-4 package, 14 | 10 error not with npm itself. 15 | 10 error Tell the author that this fails on your system: 16 | 10 error node ./bin/www 17 | 10 error You can get their info via: 18 | 10 error npm owner ls section-4 19 | 10 error There is likely additional logging output above. 20 | 11 error System Darwin 13.4.0 21 | 12 error command "/usr/local/bin/node" "/usr/local/bin/npm" "start" 22 | 13 error cwd /Users/aprovis/dev/personal-dev/learning-nodejs-10-projects/section-4 23 | 14 error node -v v0.10.35 24 | 15 error npm -v 1.4.28 25 | 16 error code ELIFECYCLE 26 | 17 verbose exit [ 1, true ] 27 | -------------------------------------------------------------------------------- /section-4/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "section-4", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.12.0", 10 | "cookie-parser": "~1.3.4", 11 | "debug": "~2.1.1", 12 | "express": "~4.12.2", 13 | "jade": "~1.9.2", 14 | "morgan": "~1.5.1", 15 | "serve-favicon": "~2.2.0", 16 | "mongodb": "*", 17 | "mongoose": "*", 18 | "connect-flash": "*", 19 | "express-validator": "*", 20 | "express-session": "*", 21 | "express-messages": "*", 22 | "passport": "*", 23 | "passport-local": "*", 24 | "passport-http": "*", 25 | "multer": "*" 26 | } 27 | } -------------------------------------------------------------------------------- /section-4/public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-4/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /section-4/public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-4/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /section-4/public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-4/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /section-4/public/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-4/public/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /section-4/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font-family: arial; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | ul.success li, ul.info li, ul.error li { 11 | margin: 20px 0; 12 | padding: 15px; 13 | border: 1px solid transparent; 14 | border-radius: 5px; 15 | list-style: none; 16 | } 17 | 18 | ul.success li { 19 | color: #3c763d; 20 | background: #dff0d8; 21 | border-color: #d6e9c6; 22 | } 23 | 24 | ul.info li { 25 | color: #31708f; 26 | background: #d9edf7; 27 | border-color: #bce8f1; 28 | } 29 | 30 | ul.error li { 31 | color: #3c763d; 32 | background: #dff0d8; 33 | border-color: #d6e9c6; 34 | } -------------------------------------------------------------------------------- /section-4/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | // Members Page 5 | router.get('/', ensureAuthenticated, function(req, res, next) { 6 | res.render('index', { title: 'Members' }); 7 | }); 8 | 9 | function ensureAuthenticated(req, res, next) { 10 | if (req.isAuthenticated()) { 11 | return next(); 12 | } 13 | res.redirect('/users/login'); 14 | } 15 | 16 | module.exports = router; 17 | -------------------------------------------------------------------------------- /section-4/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /section-4/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1 Members Area 5 | p Welcome to the members area 6 | -------------------------------------------------------------------------------- /section-4/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title NodeAuth 5 | link(rel='stylesheet', href='/stylesheets/bootstrap.css') 6 | link(rel='stylesheet', href='/stylesheets/style.css') 7 | body 8 | nav.navbar.navbar-default.navbar-fixed-top 9 | .container 10 | .navbar-header 11 | button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='#navbar', aria-expanded='false', aria-controls='navbar') 12 | span.sr-only Toggle Navigation 13 | span.icon-bar 14 | span.icon-bar 15 | span.icon-bar 16 | a.navbar-brand(href='#') NodeAuth 17 | #navbar.collapse.navbar-collapse 18 | ul.nav.navbar-nav 19 | li(class=(title == 'Members' ? 'active' : '')) 20 | a(href='/') Members 21 | if !user 22 | li(class=(title == 'Register' ? 'active' : '')) 23 | a(href='/users/register') Register 24 | li(class=(title == 'Login' ? 'active' : '')) 25 | a(href='/users/login') Log In 26 | ul.nav.navbar-nav.navbar-right 27 | if user 28 | li 29 | a(href='/users/logout') Logout 30 | .container 31 | != messages() 32 | block content 33 | 34 | script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js') 35 | script(src='javascripts/bootstrap.js') -------------------------------------------------------------------------------- /section-4/views/login.jade: -------------------------------------------------------------------------------- 1 | // 2 | Created by aprovis on 06/05/2015. 3 | 4 | extends layout 5 | 6 | block content 7 | h1 Login 8 | p Please login below. 9 | form(method='post', action='/users/login', enctype='multipart/form-data') 10 | .form-group 11 | label Username 12 | input.form-control(name='username', type='text') 13 | .form-group 14 | label Password 15 | input.form-control(name='password', type='password') 16 | input.btn.btn-default(name='submit', type='submit', value='Login') -------------------------------------------------------------------------------- /section-4/views/register.jade: -------------------------------------------------------------------------------- 1 | // 2 | Created by aprovis on 06/05/2015. 3 | 4 | extends layout 5 | 6 | block content 7 | h1 Register 8 | p Please register using the form below. 9 | ul.errors 10 | if errors 11 | each error, i in errors 12 | li.alert.alert-danger #{error.msg} 13 | form(method='post', action='/users/register', enctype='multipart/form-data') 14 | .form-group 15 | label Name 16 | input.form-control(name='name', type='text', placeholder='Enter Name') 17 | .form-group 18 | label Email 19 | input.form-control(name='email', type='email', placeholder='Enter Email') 20 | .form-group 21 | label Username 22 | input.form-control(name='username', type='text', placeholder='Enter Username') 23 | .form-group 24 | label Password 25 | input.form-control(name='password', type='password', placeholder='Enter Password') 26 | .form-group 27 | label Confirm Password 28 | input.form-control(name='password2', type='password', placeholder='Confirm Password') 29 | .form-group 30 | label Profile Image 31 | input.form-control(name='profileimage', type='file') 32 | input.btn.btn-default(name='submit', type='submit', value='Register') -------------------------------------------------------------------------------- /section-5/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('section-5:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /section-5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "section-5", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.12.0", 10 | "cookie-parser": "~1.3.4", 11 | "debug": "~2.1.1", 12 | "express": "~4.12.2", 13 | "jade": "~1.9.2", 14 | "morgan": "~1.5.1", 15 | "serve-favicon": "~2.2.0", 16 | "mongodb": "*", 17 | "monk": "*", 18 | "connect-flash": "*", 19 | "express-validator": "*", 20 | "express-session": "*", 21 | "express-messages": "*", 22 | "multer": "*", 23 | "moment": "*" 24 | } 25 | } -------------------------------------------------------------------------------- /section-5/public/ckeditor/README.md: -------------------------------------------------------------------------------- 1 | CKEditor 4 2 | ========== 3 | 4 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 5 | http://ckeditor.com - See LICENSE.md for license information. 6 | 7 | CKEditor is a text editor to be used inside web pages. It's not a replacement 8 | for desktop text editors like Word or OpenOffice, but a component to be used as 9 | part of web applications and websites. 10 | 11 | ## Documentation 12 | 13 | The full editor documentation is available online at the following address: 14 | http://docs.ckeditor.com 15 | 16 | ## Installation 17 | 18 | Installing CKEditor is an easy task. Just follow these simple steps: 19 | 20 | 1. **Download** the latest version from the CKEditor website: 21 | http://ckeditor.com. You should have already completed this step, but be 22 | sure you have the very latest version. 23 | 2. **Extract** (decompress) the downloaded file into the root of your website. 24 | 25 | **Note:** CKEditor is by default installed in the `ckeditor` folder. You can 26 | place the files in whichever you want though. 27 | 28 | ## Checking Your Installation 29 | 30 | The editor comes with a few sample pages that can be used to verify that 31 | installation proceeded properly. Take a look at the `samples` directory. 32 | 33 | To test your installation, just call the following page at your website: 34 | 35 | http:////samples/index.html 36 | 37 | For example: 38 | 39 | http://www.example.com/ckeditor/samples/index.html 40 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | 6 | CKEDITOR.editorConfig = function( config ) { 7 | // Define changes to default configuration here. 8 | // For complete reference see: 9 | // http://docs.ckeditor.com/#!/api/CKEDITOR.config 10 | 11 | // The toolbar groups arrangement, optimized for two toolbar rows. 12 | config.toolbarGroups = [ 13 | { name: 'clipboard', groups: [ 'clipboard', 'undo' ] }, 14 | { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] }, 15 | { name: 'links' }, 16 | { name: 'insert' }, 17 | { name: 'forms' }, 18 | { name: 'tools' }, 19 | { name: 'document', groups: [ 'mode', 'document', 'doctools' ] }, 20 | { name: 'others' }, 21 | '/', 22 | { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] }, 23 | { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] }, 24 | { name: 'styles' }, 25 | { name: 'colors' }, 26 | { name: 'about' } 27 | ]; 28 | 29 | // Remove some buttons provided by the standard plugins, which are 30 | // not needed in the Standard(s) toolbar. 31 | config.removeButtons = 'Underline,Subscript,Superscript'; 32 | 33 | // Set the most common block elements. 34 | config.format_tags = 'p;h1;h2;h3;pre'; 35 | 36 | // Simplify the dialog windows. 37 | config.removeDialogTabs = 'image:advanced;link:advanced'; 38 | }; 39 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/contents.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | 6 | body 7 | { 8 | /* Font */ 9 | font-family: sans-serif, Arial, Verdana, "Trebuchet MS"; 10 | font-size: 12px; 11 | 12 | /* Text color */ 13 | color: #333; 14 | 15 | /* Remove the background color to make it transparent */ 16 | background-color: #fff; 17 | 18 | margin: 20px; 19 | } 20 | 21 | .cke_editable 22 | { 23 | font-size: 13px; 24 | line-height: 1.6; 25 | } 26 | 27 | blockquote 28 | { 29 | font-style: italic; 30 | font-family: Georgia, Times, "Times New Roman", serif; 31 | padding: 2px 0; 32 | border-style: solid; 33 | border-color: #ccc; 34 | border-width: 0; 35 | } 36 | 37 | .cke_contents_ltr blockquote 38 | { 39 | padding-left: 20px; 40 | padding-right: 8px; 41 | border-left-width: 5px; 42 | } 43 | 44 | .cke_contents_rtl blockquote 45 | { 46 | padding-left: 8px; 47 | padding-right: 20px; 48 | border-right-width: 5px; 49 | } 50 | 51 | a 52 | { 53 | color: #0782C1; 54 | } 55 | 56 | ol,ul,dl 57 | { 58 | /* IE7: reset rtl list margin. (#7334) */ 59 | *margin-right: 0px; 60 | /* preserved spaces for list items with text direction other than the list. (#6249,#8049)*/ 61 | padding: 0 40px; 62 | } 63 | 64 | h1,h2,h3,h4,h5,h6 65 | { 66 | font-weight: normal; 67 | line-height: 1.2; 68 | } 69 | 70 | hr 71 | { 72 | border: 0px; 73 | border-top: 1px solid #ccc; 74 | } 75 | 76 | img.right 77 | { 78 | border: 1px solid #ccc; 79 | float: right; 80 | margin-left: 15px; 81 | padding: 5px; 82 | } 83 | 84 | img.left 85 | { 86 | border: 1px solid #ccc; 87 | float: left; 88 | margin-right: 15px; 89 | padding: 5px; 90 | } 91 | 92 | pre 93 | { 94 | white-space: pre-wrap; /* CSS 2.1 */ 95 | word-wrap: break-word; /* IE7 */ 96 | -moz-tab-size: 4; 97 | -o-tab-size: 4; 98 | -webkit-tab-size: 4; 99 | tab-size: 4; 100 | } 101 | 102 | .marker 103 | { 104 | background-color: Yellow; 105 | } 106 | 107 | span[lang] 108 | { 109 | font-style: italic; 110 | } 111 | 112 | figure 113 | { 114 | text-align: center; 115 | border: solid 1px #ccc; 116 | border-radius: 2px; 117 | background: rgba(0,0,0,0.05); 118 | padding: 10px; 119 | margin: 10px 20px; 120 | display: inline-block; 121 | } 122 | 123 | figure > figcaption 124 | { 125 | text-align: center; 126 | display: block; /* For IE8 */ 127 | } 128 | 129 | a > img { 130 | padding: 1px; 131 | margin: 1px; 132 | border: none; 133 | outline: 1px solid #0782C1; 134 | } 135 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/a11yhelp/dialogs/a11yhelp.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.dialog.add("a11yHelp",function(j){var a=j.lang.a11yhelp,l=CKEDITOR.tools.getNextId(),e={8:a.backspace,9:a.tab,13:a.enter,16:a.shift,17:a.ctrl,18:a.alt,19:a.pause,20:a.capslock,27:a.escape,33:a.pageUp,34:a.pageDown,35:a.end,36:a.home,37:a.leftArrow,38:a.upArrow,39:a.rightArrow,40:a.downArrow,45:a.insert,46:a["delete"],91:a.leftWindowKey,92:a.rightWindowKey,93:a.selectKey,96:a.numpad0,97:a.numpad1,98:a.numpad2,99:a.numpad3,100:a.numpad4,101:a.numpad5,102:a.numpad6,103:a.numpad7,104:a.numpad8, 6 | 105:a.numpad9,106:a.multiply,107:a.add,109:a.subtract,110:a.decimalPoint,111:a.divide,112:a.f1,113:a.f2,114:a.f3,115:a.f4,116:a.f5,117:a.f6,118:a.f7,119:a.f8,120:a.f9,121:a.f10,122:a.f11,123:a.f12,144:a.numLock,145:a.scrollLock,186:a.semiColon,187:a.equalSign,188:a.comma,189:a.dash,190:a.period,191:a.forwardSlash,192:a.graveAccent,219:a.openBracket,220:a.backSlash,221:a.closeBracket,222:a.singleQuote};e[CKEDITOR.ALT]=a.alt;e[CKEDITOR.SHIFT]=a.shift;e[CKEDITOR.CTRL]=a.ctrl;var f=[CKEDITOR.ALT,CKEDITOR.SHIFT, 7 | CKEDITOR.CTRL],m=/\$\{(.*?)\}/g,p=function(){var a=j.keystrokeHandler.keystrokes,g={},c;for(c in a)g[a[c]]=c;return function(a,c){var b;if(g[c]){b=g[c];for(var h,i,k=[],d=0;d=h&&(b-=i,k.push(e[i]));k.push(e[b]||String.fromCharCode(b));b=k.join("+")}else b=a;return b}}();return{title:a.title,minWidth:600,minHeight:400,contents:[{id:"info",label:j.lang.common.generalTab,expand:!0,elements:[{type:"html",id:"legends",style:"white-space:normal;",focus:function(){this.getElement().focus()}, 8 | html:function(){for(var e='
%1
'+a.contents+" ",g=[],c=a.legend,j=c.length,f=0;f%1
%2
".replace("%1",n.name).replace("%2",o))}g.push("

%1

%2
".replace("%1",b.name).replace("%2",h.join("")))}return e.replace("%1", 9 | g.join(""))}()+''}]}], 10 | buttons:[CKEDITOR.dialog.cancelButton]}}); -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/a11yhelp/dialogs/lang/_translationstatus.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 2 | For licensing, see LICENSE.md or http://ckeditor.com/license 3 | 4 | cs.js Found: 30 Missing: 0 5 | cy.js Found: 30 Missing: 0 6 | da.js Found: 12 Missing: 18 7 | de.js Found: 30 Missing: 0 8 | el.js Found: 25 Missing: 5 9 | eo.js Found: 30 Missing: 0 10 | fa.js Found: 30 Missing: 0 11 | fi.js Found: 30 Missing: 0 12 | fr.js Found: 30 Missing: 0 13 | gu.js Found: 12 Missing: 18 14 | he.js Found: 30 Missing: 0 15 | it.js Found: 30 Missing: 0 16 | mk.js Found: 5 Missing: 25 17 | nb.js Found: 30 Missing: 0 18 | nl.js Found: 30 Missing: 0 19 | no.js Found: 30 Missing: 0 20 | pt-br.js Found: 30 Missing: 0 21 | ro.js Found: 6 Missing: 24 22 | tr.js Found: 30 Missing: 0 23 | ug.js Found: 27 Missing: 3 24 | vi.js Found: 6 Missing: 24 25 | zh-cn.js Found: 30 Missing: 0 26 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/a11yhelp/dialogs/lang/zh-cn.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.plugins.setLang("a11yhelp","zh-cn",{title:"辅助功能说明",contents:"帮助内容。要关闭此对话框请按 ESC 键。",legend:[{name:"常规",items:[{name:"编辑器工具栏",legend:"按 ${toolbarFocus} 导航到工具栏,使用 TAB 键和 SHIFT+TAB 组合键移动到上一个和下一个工具栏组。使用左右箭头键移动到上一个和下一个工具栏按钮。按空格键或回车键以选中工具栏按钮。"},{name:"编辑器对话框",legend:"在对话框内,TAB 键移动到下一个字段,SHIFT + TAB 组合键移动到上一个字段,ENTER 键提交对话框,ESC 键取消对话框。对于有多选项卡的对话框,用ALT + F10来移到选项卡列表。然后用 TAB 键或者向右箭头来移动到下一个选项卡;SHIFT + TAB 组合键或者向左箭头移动到上一个选项卡。用 SPACE 键或者 ENTER 键选择选项卡。"},{name:"编辑器上下文菜单",legend:"用 ${contextMenu} 或者“应用程序键”打开上下文菜单。然后用 TAB 键或者下箭头键来移动到下一个菜单项;SHIFT + TAB 组合键或者上箭头键移动到上一个菜单项。用 SPACE 键或者 ENTER 键选择菜单项。用 SPACE 键,ENTER 键或者右箭头键打开子菜单。返回菜单用 ESC 键或者左箭头键。用 ESC 键关闭上下文菜单。"}, 6 | {name:"编辑器列表框",legend:"在列表框中,移到下一列表项用 TAB 键或者下箭头键。移到上一列表项用SHIFT + TAB 组合键或者上箭头键,用 SPACE 键或者 ENTER 键选择列表项。用 ESC 键收起列表框。"},{name:"编辑器元素路径栏",legend:"按 ${elementsPathFocus} 以导航到元素路径栏,使用 TAB 键或右箭头键选择下一个元素,使用 SHIFT+TAB 组合键或左箭头键选择上一个元素,按空格键或回车键以选定编辑器里的元素。"}]},{name:"命令",items:[{name:" 撤消命令",legend:"按 ${undo}"},{name:" 重做命令",legend:"按 ${redo}"},{name:" 加粗命令",legend:"按 ${bold}"},{name:" 倾斜命令",legend:"按 ${italic}"},{name:" 下划线命令",legend:"按 ${underline}"},{name:" 链接命令",legend:"按 ${link}"},{name:" 工具栏折叠命令",legend:"按 ${toolbarCollapse}"}, 7 | {name:"访问前一个焦点区域的命令",legend:"按 ${accessPreviousSpace} 访问^符号前最近的不可访问的焦点区域,例如:两个相邻的 HR 元素。重复此组合按键可以到达远处的焦点区域。"},{name:"访问下一个焦点区域命令",legend:"按 ${accessNextSpace} 以访问^符号后最近的不可访问的焦点区域。例如:两个相邻的 HR 元素。重复此组合按键可以到达远处的焦点区域。"},{name:"辅助功能帮助",legend:"按 ${a11yHelp}"}]}],backspace:"退格键",tab:"Tab 键",enter:"回车键",shift:"Shift 键",ctrl:"Ctrl 键",alt:"Alt 键",pause:"暂停键",capslock:"大写锁定键",escape:"Esc 键",pageUp:"上翻页键",pageDown:"下翻页键",end:"行尾键",home:"行首键",leftArrow:"向左箭头键",upArrow:"向上箭头键",rightArrow:"向右箭头键",downArrow:"向下箭头键", 8 | insert:"插入键","delete":"删除键",leftWindowKey:"左 WIN 键",rightWindowKey:"右 WIN 键",selectKey:"选择键",numpad0:"小键盘 0 键",numpad1:"小键盘 1 键",numpad2:"小键盘 2 键",numpad3:"小键盘 3 键",numpad4:"小键盘 4 键",numpad5:"小键盘 5 键",numpad6:"小键盘 6 键",numpad7:"小键盘 7 键",numpad8:"小键盘 8 键",numpad9:"小键盘 9 键",multiply:"星号键",add:"加号键",subtract:"减号键",decimalPoint:"小数点键",divide:"除号键",f1:"F1 键",f2:"F2 键",f3:"F3 键",f4:"F4 键",f5:"F5 键",f6:"F6 键",f7:"F7 键",f8:"F8 键",f9:"F9 键",f10:"F10 键",f11:"F11 键",f12:"F12 键",numLock:"数字锁定键",scrollLock:"滚动锁定键", 9 | semiColon:"分号键",equalSign:"等号键",comma:"逗号键",dash:"短划线键",period:"句号键",forwardSlash:"斜杠键",graveAccent:"重音符键",openBracket:"左中括号键",backSlash:"反斜杠键",closeBracket:"右中括号键",singleQuote:"单引号键"}); -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/a11yhelp/dialogs/lang/zh.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.plugins.setLang("a11yhelp","zh",{title:"輔助工具指南",contents:"說明內容。若要關閉此對話框請按「ESC」。",legend:[{name:"一般",items:[{name:"編輯器工具列",legend:"請按「${toolbarFocus}」以瀏覽工具列。\r\n利用「TAB」或「SHIFT+TAB」以便移動到下一個或前一個工具列群組。\r\n利用「→」或「←」以便移動到下一個或前一個工具列按鈕。\r\n請按下「空白鍵」或「ENTER」鍵啟動工具列按鈕。"},{name:"編輯器對話方塊",legend:"在對話框中,請按 TAB 鍵以便移動到下個欄位,請按 SHIFT + TAB 以便移動到前個欄位;請按 ENTER 以提交對話框資料,或按下 ESC 取消對話框。\r\n若是有多個頁框的對話框,請按 ALT + F10 以移動到頁框列表,並以 TAB 或是 → 方向鍵移動到下個頁框。以 SHIFT + TAB 或是 ← 方向鍵移動到前個頁框。按下 空白鍵 或是 ENTER 以選取頁框。"},{name:"編輯器內容功能表", 6 | legend:"請按下「${contextMenu}」或是「應用程式鍵」以開啟內容選單。以「TAB」或是「↓」鍵移動到下一個選單選項。以「SHIFT + TAB」或是「↑」鍵移動到上一個選單選項。按下「空白鍵」或是「ENTER」鍵以選取選單選項。以「空白鍵」或「ENTER」或「→」開啟目前選項之子選單。以「ESC」或「←」回到父選單。以「ESC」鍵關閉內容選單」。"},{name:"編輯器清單方塊",legend:"在列表中,請利用 TAB 或 ↓ 方向鍵以移動到下一個項目;或利用 SHIFT + TAB 或 ↑ 方向鍵移動到前一個項目。請按下 空白鍵 或是 ENTER 以選取項目。請按 ESC 關閉列表。"},{name:"編輯器元件路徑工具列",legend:"請按「${elementsPathFocus}」以瀏覽元素路徑工具列。\r\n利用「TAB」或「→」以便移動到下一個元素按鈕。\r\n利用「SHIFT+TAB」或「←」以便移動到前一個元素按鈕。\r\n請按下「空白鍵」或「ENTER」鍵選擇編輯器中的元素。"}]},{name:"命令",items:[{name:"復原命令", 7 | legend:"請按下「${undo}」"},{name:"重複命令",legend:"請按下「 ${redo}」"},{name:"粗體命令",legend:"請按下「${bold}」"},{name:"斜體",legend:"請按下「${italic}」"},{name:"底線命令",legend:"請按下「${underline}」"},{name:"連結",legend:"請按下「${link}」"},{name:"隱藏工具列",legend:"請按下「${toolbarCollapse}」"},{name:"存取前一個焦點空間命令",legend:"請按下 ${accessPreviousSpace} 以存取最近但無法靠近之插字符號前的焦點空間。舉例:二個相鄰的 HR 元素。\r\n重複按鍵以存取較遠的焦點空間。"},{name:"存取下一個焦點空間命令",legend:"請按下 ${accessNextSpace} 以存取最近但無法靠近之插字符號後的焦點空間。舉例:二個相鄰的 HR 元素。\r\n重複按鍵以存取較遠的焦點空間。"},{name:"協助工具說明",legend:"請按下「${a11yHelp}」"}]}], 8 | backspace:"退格鍵",tab:"Tab",enter:"Enter",shift:"Shift",ctrl:"Ctrl",alt:"Alt",pause:"Pause",capslock:"Caps Lock",escape:"Escape",pageUp:"Page Up",pageDown:"Page Down",end:"End",home:"Home",leftArrow:"向左箭號",upArrow:"向上鍵號",rightArrow:"向右鍵號",downArrow:"向下鍵號",insert:"插入","delete":"刪除",leftWindowKey:"Left Windows key",rightWindowKey:"Right Windows key",selectKey:"Select key",numpad0:"Numpad 0",numpad1:"Numpad 1",numpad2:"Numpad 2",numpad3:"Numpad 3",numpad4:"Numpad 4",numpad5:"Numpad 5",numpad6:"Numpad 6", 9 | numpad7:"Numpad 7",numpad8:"Numpad 8",numpad9:"Numpad 9",multiply:"Multiply",add:"新增",subtract:"Subtract",decimalPoint:"Decimal Point",divide:"Divide",f1:"F1",f2:"F2",f3:"F3",f4:"F4",f5:"F5",f6:"F6",f7:"F7",f8:"F8",f9:"F9",f10:"F10",f11:"F11",f12:"F12",numLock:"Num Lock",scrollLock:"Scroll Lock",semiColon:"Semicolon",equalSign:"等號",comma:"逗號",dash:"虛線",period:"句點",forwardSlash:"斜線",graveAccent:"抑音符號",openBracket:"左方括號",backSlash:"反斜線",closeBracket:"右方括號",singleQuote:"單引號"}); -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/about/dialogs/about.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.dialog.add("about",function(a){var a=a.lang.about,b=CKEDITOR.getUrl(CKEDITOR.plugins.get("about").path+"dialogs/"+(CKEDITOR.env.hidpi?"hidpi/":"")+"logo_ckeditor.png");return{title:CKEDITOR.env.ie?a.dlgTitle:a.title,minWidth:390,minHeight:230,contents:[{id:"tab1",label:"",title:"",expand:!0,padding:0,elements:[{type:"html",html:'

CKEditor '+CKEDITOR.version+" (revision "+CKEDITOR.revision+')
http://ckeditor.com

'+a.help.replace("$1",''+ 7 | a.userGuide+"")+"

"+a.moreInfo+'
http://ckeditor.com/about/license

'+a.copy.replace("$1",'CKSource - Frederico Knabben')+"

"}]}],buttons:[CKEDITOR.dialog.cancelButton]}}); -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/about/dialogs/hidpi/logo_ckeditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/plugins/about/dialogs/hidpi/logo_ckeditor.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/about/dialogs/logo_ckeditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/plugins/about/dialogs/logo_ckeditor.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/dialog/dialogDefinition.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/plugins/icons.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/icons_hidpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/plugins/icons_hidpi.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/image/images/noimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/plugins/image/images/noimage.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/link/dialogs/anchor.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.dialog.add("anchor",function(c){function d(a,b){return a.createFakeElement(a.document.createElement("a",{attributes:b}),"cke_anchor","anchor")}return{title:c.lang.link.anchor.title,minWidth:300,minHeight:60,onOk:function(){var a=CKEDITOR.tools.trim(this.getValueOf("info","txtName")),a={id:a,name:a,"data-cke-saved-name":a};if(this._.selectedElement)this._.selectedElement.data("cke-realelement")?(a=d(c,a),a.replace(this._.selectedElement),CKEDITOR.env.ie&&c.getSelection().selectElement(a)): 6 | this._.selectedElement.setAttributes(a);else{var b=c.getSelection(),b=b&&b.getRanges()[0];b.collapsed?(a=d(c,a),b.insertNode(a)):(CKEDITOR.env.ie&&9>CKEDITOR.env.version&&(a["class"]="cke_anchor"),a=new CKEDITOR.style({element:"a",attributes:a}),a.type=CKEDITOR.STYLE_INLINE,c.applyStyle(a))}},onHide:function(){delete this._.selectedElement},onShow:function(){var a=c.getSelection(),b=a.getSelectedElement(),d=b&&b.data("cke-realelement"),e=d?CKEDITOR.plugins.link.tryRestoreFakeAnchor(c,b):CKEDITOR.plugins.link.getSelectedLink(c); 7 | e&&(this._.selectedElement=e,this.setValueOf("info","txtName",e.data("cke-saved-name")||""),!d&&a.selectElement(e),b&&(this._.selectedElement=b));this.getContentElement("info","txtName").focus()},contents:[{id:"info",label:c.lang.link.anchor.title,accessKey:"I",elements:[{type:"text",id:"txtName",label:c.lang.link.anchor.name,required:!0,validate:function(){return!this.getValue()?(alert(c.lang.link.anchor.errorName),!1):!0}}]}]}}); -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/link/images/anchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/plugins/link/images/anchor.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/link/images/hidpi/anchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/plugins/link/images/hidpi/anchor.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/magicline/images/hidpi/icon-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/plugins/magicline/images/hidpi/icon-rtl.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/magicline/images/hidpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/plugins/magicline/images/hidpi/icon.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/magicline/images/icon-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/plugins/magicline/images/icon-rtl.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/magicline/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/plugins/magicline/images/icon.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/scayt/LICENSE.md: -------------------------------------------------------------------------------- 1 | Software License Agreement 2 | ========================== 3 | 4 | **CKEditor SCAYT Plugin** 5 | Copyright © 2012, [CKSource](http://cksource.com) - Frederico Knabben. All rights reserved. 6 | 7 | Licensed under the terms of any of the following licenses at your choice: 8 | 9 | * GNU General Public License Version 2 or later (the "GPL"): 10 | http://www.gnu.org/licenses/gpl.html 11 | 12 | * GNU Lesser General Public License Version 2.1 or later (the "LGPL"): 13 | http://www.gnu.org/licenses/lgpl.html 14 | 15 | * Mozilla Public License Version 1.1 or later (the "MPL"): 16 | http://www.mozilla.org/MPL/MPL-1.1.html 17 | 18 | You are not required to, but if you want to explicitly declare the license you have chosen to be bound to when using, reproducing, modifying and distributing this software, just include a text file titled "legal.txt" in your version of this software, indicating your license choice. 19 | 20 | Sources of Intellectual Property Included in this plugin 21 | -------------------------------------------------------- 22 | 23 | Where not otherwise indicated, all plugin content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, the plugin will incorporate work done by developers outside of CKSource with their express permission. 24 | 25 | Trademarks 26 | ---------- 27 | 28 | CKEditor is a trademark of CKSource - Frederico Knabben. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders. 29 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/scayt/README.md: -------------------------------------------------------------------------------- 1 | CKEditor SCAYT Plugin 2 | ===================== 3 | 4 | This plugin brings Spell Check As You Type (SCAYT) into up to CKEditor 4+. 5 | 6 | SCAYT is a "installation-less", using the web-services of [WebSpellChecker.net](http://www.webspellchecker.net/). It's an out of the box solution. 7 | 8 | Installation 9 | ------------ 10 | 11 | 1. Clone/copy this repository contents in a new "plugins/scayt" folder in your CKEditor installation. 12 | 2. Enable the "scayt" plugin in the CKEditor configuration file (config.js): 13 | 14 | config.extraPlugins = 'scayt'; 15 | 16 | That's all. SCAYT will appear on the editor toolbar and will be ready to use. 17 | 18 | License 19 | ------- 20 | 21 | Licensed under the terms of any of the following licenses at your choice: [GPL](http://www.gnu.org/licenses/gpl.html), [LGPL](http://www.gnu.org/licenses/lgpl.html) and [MPL](http://www.mozilla.org/MPL/MPL-1.1.html). 22 | 23 | See LICENSE.md for more information. 24 | 25 | Developed in cooperation with [WebSpellChecker.net](http://www.webspellchecker.net/). 26 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/scayt/dialogs/toolbar.css: -------------------------------------------------------------------------------- 1 | a 2 | { 3 | text-decoration:none; 4 | padding: 2px 4px 4px 6px; 5 | display : block; 6 | border-width: 1px; 7 | border-style: solid; 8 | margin : 0px; 9 | } 10 | 11 | a.cke_scayt_toogle:hover, 12 | a.cke_scayt_toogle:focus, 13 | a.cke_scayt_toogle:active 14 | { 15 | border-color: #316ac5; 16 | background-color: #dff1ff; 17 | color : #000; 18 | cursor: pointer; 19 | margin : 0px; 20 | } 21 | a.cke_scayt_toogle { 22 | color : #316ac5; 23 | border-color: #fff; 24 | } 25 | .scayt_enabled a.cke_scayt_item { 26 | color : #316ac5; 27 | border-color: #fff; 28 | margin : 0px; 29 | } 30 | .scayt_disabled a.cke_scayt_item { 31 | color : gray; 32 | border-color : #fff; 33 | } 34 | .scayt_enabled a.cke_scayt_item:hover, 35 | .scayt_enabled a.cke_scayt_item:focus, 36 | .scayt_enabled a.cke_scayt_item:active 37 | { 38 | border-color: #316ac5; 39 | background-color: #dff1ff; 40 | color : #000; 41 | cursor: pointer; 42 | } 43 | .scayt_disabled a.cke_scayt_item:hover, 44 | .scayt_disabled a.cke_scayt_item:focus, 45 | .scayt_disabled a.cke_scayt_item:active 46 | { 47 | border-color: gray; 48 | background-color: #dff1ff; 49 | color : gray; 50 | cursor: no-drop; 51 | } 52 | .cke_scayt_set_on, .cke_scayt_set_off 53 | { 54 | display: none; 55 | } 56 | .scayt_enabled .cke_scayt_set_on 57 | { 58 | display: none; 59 | } 60 | .scayt_disabled .cke_scayt_set_on 61 | { 62 | display: inline; 63 | } 64 | .scayt_disabled .cke_scayt_set_off 65 | { 66 | display: none; 67 | } 68 | .scayt_enabled .cke_scayt_set_off 69 | { 70 | display: inline; 71 | } 72 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/specialchar/dialogs/lang/_translationstatus.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 2 | For licensing, see LICENSE.md or http://ckeditor.com/license 3 | 4 | cs.js Found: 118 Missing: 0 5 | cy.js Found: 118 Missing: 0 6 | de.js Found: 118 Missing: 0 7 | el.js Found: 16 Missing: 102 8 | eo.js Found: 118 Missing: 0 9 | et.js Found: 31 Missing: 87 10 | fa.js Found: 24 Missing: 94 11 | fi.js Found: 23 Missing: 95 12 | fr.js Found: 118 Missing: 0 13 | hr.js Found: 23 Missing: 95 14 | it.js Found: 118 Missing: 0 15 | nb.js Found: 118 Missing: 0 16 | nl.js Found: 118 Missing: 0 17 | no.js Found: 118 Missing: 0 18 | tr.js Found: 118 Missing: 0 19 | ug.js Found: 39 Missing: 79 20 | zh-cn.js Found: 118 Missing: 0 21 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/specialchar/dialogs/lang/ja.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.plugins.setLang("specialchar","ja",{euro:"ユーロ記号",lsquo:"左シングル引用符",rsquo:"右シングル引用符",ldquo:"左ダブル引用符",rdquo:"右ダブル引用符",ndash:"半角ダッシュ",mdash:"全角ダッシュ",iexcl:"逆さ感嘆符",cent:"セント記号",pound:"ポンド記号",curren:"通貨記号",yen:"円記号",brvbar:"上下に分かれた縦棒",sect:"節記号",uml:"分音記号(ウムラウト)",copy:"著作権表示記号",ordf:"女性序数標識",laquo:" 始め二重山括弧引用記号",not:"論理否定記号",reg:"登録商標記号",macr:"長音符",deg:"度記号",sup2:"上つき2, 2乗",sup3:"上つき3, 3乗",acute:"揚音符",micro:"ミクロン記号",para:"段落記号",middot:"中黒",cedil:"セディラ",sup1:"上つき1",ordm:"男性序数標識",raquo:"終わり二重山括弧引用記号", 6 | frac14:"四分の一",frac12:"二分の一",frac34:"四分の三",iquest:"逆疑問符",Agrave:"抑音符つき大文字A",Aacute:"揚音符つき大文字A",Acirc:"曲折アクセントつき大文字A",Atilde:"チルダつき大文字A",Auml:"分音記号つき大文字A",Aring:"リングつき大文字A",AElig:"AとEの合字",Ccedil:"セディラつき大文字C",Egrave:"抑音符つき大文字E",Eacute:"揚音符つき大文字E",Ecirc:"曲折アクセントつき大文字E",Euml:"分音記号つき大文字E",Igrave:"抑音符つき大文字I",Iacute:"揚音符つき大文字I",Icirc:"曲折アクセントつき大文字I",Iuml:"分音記号つき大文字I",ETH:"[アイスランド語]大文字ETH",Ntilde:"チルダつき大文字N",Ograve:"抑音符つき大文字O",Oacute:"揚音符つき大文字O",Ocirc:"曲折アクセントつき大文字O",Otilde:"チルダつき大文字O",Ouml:" 分音記号つき大文字O", 7 | times:"乗算記号",Oslash:"打ち消し線つき大文字O",Ugrave:"抑音符つき大文字U",Uacute:"揚音符つき大文字U",Ucirc:"曲折アクセントつき大文字U",Uuml:"分音記号つき大文字U",Yacute:"揚音符つき大文字Y",THORN:"[アイスランド語]大文字THORN",szlig:"ドイツ語エスツェット",agrave:"抑音符つき小文字a",aacute:"揚音符つき小文字a",acirc:"曲折アクセントつき小文字a",atilde:"チルダつき小文字a",auml:"分音記号つき小文字a",aring:"リングつき小文字a",aelig:"aとeの合字",ccedil:"セディラつき小文字c",egrave:"抑音符つき小文字e",eacute:"揚音符つき小文字e",ecirc:"曲折アクセントつき小文字e",euml:"分音記号つき小文字e",igrave:"抑音符つき小文字i",iacute:"揚音符つき小文字i",icirc:"曲折アクセントつき小文字i",iuml:"分音記号つき小文字i",eth:"アイスランド語小文字eth", 8 | ntilde:"チルダつき小文字n",ograve:"抑音符つき小文字o",oacute:"揚音符つき小文字o",ocirc:"曲折アクセントつき小文字o",otilde:"チルダつき小文字o",ouml:"分音記号つき小文字o",divide:"除算記号",oslash:"打ち消し線つき小文字o",ugrave:"抑音符つき小文字u",uacute:"揚音符つき小文字u",ucirc:"曲折アクセントつき小文字u",uuml:"分音記号つき小文字u",yacute:"揚音符つき小文字y",thorn:"アイスランド語小文字thorn",yuml:"分音記号つき小文字y",OElig:"OとEの合字",oelig:"oとeの合字",372:"曲折アクセントつき大文字W",374:"曲折アクセントつき大文字Y",373:"曲折アクセントつき小文字w",375:"曲折アクセントつき小文字y",sbquo:"シングル下引用符",8219:"左右逆の左引用符",bdquo:"ダブル下引用符",hellip:"三点リーダ",trade:"商標記号",9658:"右黒三角ポインタ",bull:"黒丸", 9 | rarr:"右矢印",rArr:"右二重矢印",hArr:"左右二重矢印",diams:"ダイヤ",asymp:"漸近"}); -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/specialchar/dialogs/lang/zh-cn.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.plugins.setLang("specialchar","zh-cn",{euro:"欧元符号",lsquo:"左单引号",rsquo:"右单引号",ldquo:"左双引号",rdquo:"右双引号",ndash:"短划线",mdash:"长划线",iexcl:"竖翻叹号",cent:"分币符号",pound:"英镑符号",curren:"货币符号",yen:"日元符号",brvbar:"间断条",sect:"节标记",uml:"分音符",copy:"版权所有标记",ordf:"阴性顺序指示符",laquo:"左指双尖引号",not:"非标记",reg:"注册标记",macr:"长音符",deg:"度标记",sup2:"上标二",sup3:"上标三",acute:"锐音符",micro:"微符",para:"段落标记",middot:"中间点",cedil:"下加符",sup1:"上标一",ordm:"阳性顺序指示符",raquo:"右指双尖引号",frac14:"普通分数四分之一",frac12:"普通分数二分之一",frac34:"普通分数四分之三",iquest:"竖翻问号", 6 | Agrave:"带抑音符的拉丁文大写字母 A",Aacute:"带锐音符的拉丁文大写字母 A",Acirc:"带扬抑符的拉丁文大写字母 A",Atilde:"带颚化符的拉丁文大写字母 A",Auml:"带分音符的拉丁文大写字母 A",Aring:"带上圆圈的拉丁文大写字母 A",AElig:"拉丁文大写字母 Ae",Ccedil:"带下加符的拉丁文大写字母 C",Egrave:"带抑音符的拉丁文大写字母 E",Eacute:"带锐音符的拉丁文大写字母 E",Ecirc:"带扬抑符的拉丁文大写字母 E",Euml:"带分音符的拉丁文大写字母 E",Igrave:"带抑音符的拉丁文大写字母 I",Iacute:"带锐音符的拉丁文大写字母 I",Icirc:"带扬抑符的拉丁文大写字母 I",Iuml:"带分音符的拉丁文大写字母 I",ETH:"拉丁文大写字母 Eth",Ntilde:"带颚化符的拉丁文大写字母 N",Ograve:"带抑音符的拉丁文大写字母 O",Oacute:"带锐音符的拉丁文大写字母 O",Ocirc:"带扬抑符的拉丁文大写字母 O",Otilde:"带颚化符的拉丁文大写字母 O", 7 | Ouml:"带分音符的拉丁文大写字母 O",times:"乘号",Oslash:"带粗线的拉丁文大写字母 O",Ugrave:"带抑音符的拉丁文大写字母 U",Uacute:"带锐音符的拉丁文大写字母 U",Ucirc:"带扬抑符的拉丁文大写字母 U",Uuml:"带分音符的拉丁文大写字母 U",Yacute:"带抑音符的拉丁文大写字母 Y",THORN:"拉丁文大写字母 Thorn",szlig:"拉丁文小写字母清音 S",agrave:"带抑音符的拉丁文小写字母 A",aacute:"带锐音符的拉丁文小写字母 A",acirc:"带扬抑符的拉丁文小写字母 A",atilde:"带颚化符的拉丁文小写字母 A",auml:"带分音符的拉丁文小写字母 A",aring:"带上圆圈的拉丁文小写字母 A",aelig:"拉丁文小写字母 Ae",ccedil:"带下加符的拉丁文小写字母 C",egrave:"带抑音符的拉丁文小写字母 E",eacute:"带锐音符的拉丁文小写字母 E",ecirc:"带扬抑符的拉丁文小写字母 E",euml:"带分音符的拉丁文小写字母 E",igrave:"带抑音符的拉丁文小写字母 I", 8 | iacute:"带锐音符的拉丁文小写字母 I",icirc:"带扬抑符的拉丁文小写字母 I",iuml:"带分音符的拉丁文小写字母 I",eth:"拉丁文小写字母 Eth",ntilde:"带颚化符的拉丁文小写字母 N",ograve:"带抑音符的拉丁文小写字母 O",oacute:"带锐音符的拉丁文小写字母 O",ocirc:"带扬抑符的拉丁文小写字母 O",otilde:"带颚化符的拉丁文小写字母 O",ouml:"带分音符的拉丁文小写字母 O",divide:"除号",oslash:"带粗线的拉丁文小写字母 O",ugrave:"带抑音符的拉丁文小写字母 U",uacute:"带锐音符的拉丁文小写字母 U",ucirc:"带扬抑符的拉丁文小写字母 U",uuml:"带分音符的拉丁文小写字母 U",yacute:"带抑音符的拉丁文小写字母 Y",thorn:"拉丁文小写字母 Thorn",yuml:"带分音符的拉丁文小写字母 Y",OElig:"拉丁文大写连字 Oe",oelig:"拉丁文小写连字 Oe",372:"带扬抑符的拉丁文大写字母 W",374:"带扬抑符的拉丁文大写字母 Y", 9 | 373:"带扬抑符的拉丁文小写字母 W",375:"带扬抑符的拉丁文小写字母 Y",sbquo:"单下 9 形引号",8219:"单高横翻 9 形引号",bdquo:"双下 9 形引号",hellip:"水平省略号",trade:"商标标志",9658:"实心右指指针",bull:"加重号",rarr:"向右箭头",rArr:"向右双线箭头",hArr:"左右双线箭头",diams:"实心方块纸牌",asymp:"约等于"}); -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/wsc/LICENSE.md: -------------------------------------------------------------------------------- 1 | Software License Agreement 2 | ========================== 3 | 4 | **CKEditor WSC Plugin** 5 | Copyright © 2012, [CKSource](http://cksource.com) - Frederico Knabben. All rights reserved. 6 | 7 | Licensed under the terms of any of the following licenses at your choice: 8 | 9 | * GNU General Public License Version 2 or later (the "GPL"): 10 | http://www.gnu.org/licenses/gpl.html 11 | 12 | * GNU Lesser General Public License Version 2.1 or later (the "LGPL"): 13 | http://www.gnu.org/licenses/lgpl.html 14 | 15 | * Mozilla Public License Version 1.1 or later (the "MPL"): 16 | http://www.mozilla.org/MPL/MPL-1.1.html 17 | 18 | You are not required to, but if you want to explicitly declare the license you have chosen to be bound to when using, reproducing, modifying and distributing this software, just include a text file titled "legal.txt" in your version of this software, indicating your license choice. 19 | 20 | Sources of Intellectual Property Included in this plugin 21 | -------------------------------------------------------- 22 | 23 | Where not otherwise indicated, all plugin content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, the plugin will incorporate work done by developers outside of CKSource with their express permission. 24 | 25 | Trademarks 26 | ---------- 27 | 28 | CKEditor is a trademark of CKSource - Frederico Knabben. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders. 29 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/wsc/README.md: -------------------------------------------------------------------------------- 1 | CKEditor WebSpellChecker Plugin 2 | =============================== 3 | 4 | This plugin brings Web Spell Checker (WSC) into CKEditor. 5 | 6 | WSC is "installation-less", using the web-services of [WebSpellChecker.net](http://www.webspellchecker.net/). It's an out of the box solution. 7 | 8 | Installation 9 | ------------ 10 | 11 | 1. Clone/copy this repository contents in a new "plugins/wsc" folder in your CKEditor installation. 12 | 2. Enable the "wsc" plugin in the CKEditor configuration file (config.js): 13 | 14 | config.extraPlugins = 'wsc'; 15 | 16 | That's all. WSC will appear on the editor toolbar and will be ready to use. 17 | 18 | License 19 | ------- 20 | 21 | Licensed under the terms of any of the following licenses at your choice: [GPL](http://www.gnu.org/licenses/gpl.html), [LGPL](http://www.gnu.org/licenses/lgpl.html) and [MPL](http://www.mozilla.org/MPL/MPL-1.1.html). 22 | 23 | See LICENSE.md for more information. 24 | 25 | Developed in cooperation with [WebSpellChecker.net](http://www.webspellchecker.net/). 26 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/wsc/dialogs/ciframe.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 64 | 65 |

66 | 67 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/wsc/dialogs/tmpFrameset.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/plugins/wsc/dialogs/wsc.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.html or http://ckeditor.com/license 4 | */ 5 | 6 | html, body 7 | { 8 | background-color: transparent; 9 | margin: 0px; 10 | padding: 0px; 11 | } 12 | 13 | body 14 | { 15 | padding: 10px; 16 | } 17 | 18 | body, td, input, select, textarea 19 | { 20 | font-size: 11px; 21 | font-family: 'Microsoft Sans Serif' , Arial, Helvetica, Verdana; 22 | } 23 | 24 | .midtext 25 | { 26 | padding:0px; 27 | margin:10px; 28 | } 29 | 30 | .midtext p 31 | { 32 | padding:0px; 33 | margin:10px; 34 | } 35 | 36 | .Button 37 | { 38 | border: #737357 1px solid; 39 | color: #3b3b1f; 40 | background-color: #c7c78f; 41 | } 42 | 43 | .PopupTabArea 44 | { 45 | color: #737357; 46 | background-color: #e3e3c7; 47 | } 48 | 49 | .PopupTitleBorder 50 | { 51 | border-bottom: #d5d59d 1px solid; 52 | } 53 | .PopupTabEmptyArea 54 | { 55 | padding-left: 10px; 56 | border-bottom: #d5d59d 1px solid; 57 | } 58 | 59 | .PopupTab, .PopupTabSelected 60 | { 61 | border-right: #d5d59d 1px solid; 62 | border-top: #d5d59d 1px solid; 63 | border-left: #d5d59d 1px solid; 64 | padding: 3px 5px 3px 5px; 65 | color: #737357; 66 | } 67 | 68 | .PopupTab 69 | { 70 | margin-top: 1px; 71 | border-bottom: #d5d59d 1px solid; 72 | cursor: pointer; 73 | } 74 | 75 | .PopupTabSelected 76 | { 77 | font-weight: bold; 78 | cursor: default; 79 | padding-top: 4px; 80 | border-bottom: #f1f1e3 1px solid; 81 | background-color: #f1f1e3; 82 | } 83 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/samples/ajax.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Ajax — CKEditor Sample 10 | 11 | 12 | 40 | 41 | 42 |

43 | CKEditor Samples » Create and Destroy Editor Instances for Ajax Applications 44 |

45 |
46 |

47 | This sample shows how to create and destroy CKEditor instances on the fly. After the removal of CKEditor the content created inside the editing 48 | area will be displayed in a <div> element. 49 |

50 |

51 | For details of how to create this setup check the source code of this sample page 52 | for JavaScript code responsible for the creation and destruction of a CKEditor instance. 53 |

54 |
55 |

Click the buttons to create and remove a CKEditor instance.

56 |

57 | 58 | 59 |

60 | 61 |
62 |
63 | 71 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/samples/appendto.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Append To Page Element Using JavaScript Code — CKEditor Sample 10 | 11 | 12 | 13 | 14 |

15 | CKEditor Samples » Append To Page Element Using JavaScript Code 16 |

17 |
18 |
19 |

20 | The CKEDITOR.appendTo() method serves to to place editors inside existing DOM elements. Unlike CKEDITOR.replace(), 21 | a target container to be replaced is no longer necessary. A new editor 22 | instance is inserted directly wherever it is desired. 23 |

24 |
CKEDITOR.appendTo( 'container_id',
25 | 	{ /* Configuration options to be used. */ }
26 | 	'Editor content to be used.'
27 | );
28 |
29 | 43 |
44 |
45 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/samples/assets/inlineall/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/samples/assets/inlineall/logo.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/samples/assets/posteddata.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | Sample — CKEditor 12 | 13 | 14 | 15 |

16 | CKEditor — Posted Data 17 |

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | $value ) 31 | { 32 | if ( ( !is_string($value) && !is_numeric($value) ) || !is_string($key) ) 33 | continue; 34 | 35 | if ( get_magic_quotes_gpc() ) 36 | $value = htmlspecialchars( stripslashes((string)$value) ); 37 | else 38 | $value = htmlspecialchars( (string)$value ); 39 | ?> 40 | 41 | 42 | 43 | 44 | 48 |
Field NameValue
49 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/samples/assets/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/samples/assets/sample.jpg -------------------------------------------------------------------------------- /section-5/public/ckeditor/samples/assets/uilanguages/languages.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | var CKEDITOR_LANGS=function(){var c={af:"Afrikaans",ar:"Arabic",bg:"Bulgarian",bn:"Bengali/Bangla",bs:"Bosnian",ca:"Catalan",cs:"Czech",cy:"Welsh",da:"Danish",de:"German",el:"Greek",en:"English","en-au":"English (Australia)","en-ca":"English (Canadian)","en-gb":"English (United Kingdom)",eo:"Esperanto",es:"Spanish",et:"Estonian",eu:"Basque",fa:"Persian",fi:"Finnish",fo:"Faroese",fr:"French","fr-ca":"French (Canada)",gl:"Galician",gu:"Gujarati",he:"Hebrew",hi:"Hindi",hr:"Croatian",hu:"Hungarian",id:"Indonesian", 6 | is:"Icelandic",it:"Italian",ja:"Japanese",ka:"Georgian",km:"Khmer",ko:"Korean",ku:"Kurdish",lt:"Lithuanian",lv:"Latvian",mk:"Macedonian",mn:"Mongolian",ms:"Malay",nb:"Norwegian Bokmal",nl:"Dutch",no:"Norwegian",pl:"Polish",pt:"Portuguese (Portugal)","pt-br":"Portuguese (Brazil)",ro:"Romanian",ru:"Russian",si:"Sinhala",sk:"Slovak",sq:"Albanian",sl:"Slovenian",sr:"Serbian (Cyrillic)","sr-latn":"Serbian (Latin)",sv:"Swedish",th:"Thai",tr:"Turkish",tt:"Tatar",ug:"Uighur",uk:"Ukrainian",vi:"Vietnamese", 7 | zh:"Chinese Traditional","zh-cn":"Chinese Simplified"},b=[],a;for(a in CKEDITOR.lang.languages)b.push({code:a,name:c[a]||a});b.sort(function(a,b){return a.name' + requires[ i ] + '' ); 22 | } 23 | 24 | if ( missing.length ) { 25 | var warn = CKEDITOR.dom.element.createFromHtml( 26 | '
' + 27 | 'To fully experience this demo, the ' + missing.join( ', ' ) + ' plugin' + ( missing.length > 1 ? 's are' : ' is' ) + ' required.' + 28 | '
' 29 | ); 30 | warn.insertBefore( editor.container ); 31 | } 32 | } 33 | 34 | // Set icons. 35 | var doc = new CKEDITOR.dom.document( document ), 36 | icons = doc.find( '.button_icon' ); 37 | 38 | for ( i = 0; i < icons.count(); i++ ) { 39 | var icon = icons.getItem( i ), 40 | name = icon.getAttribute( 'data-icon' ), 41 | style = CKEDITOR.skin.getIconStyle( name, ( CKEDITOR.lang.dir == 'rtl' ) ); 42 | 43 | icon.addClass( 'cke_button_icon' ); 44 | icon.addClass( 'cke_button__' + name + '_icon' ); 45 | icon.setAttribute( 'style', style ); 46 | icon.setStyle( 'float', 'none' ); 47 | 48 | } 49 | } ); 50 | } )(); 51 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/samples/sample_posteddata.php: -------------------------------------------------------------------------------- 1 |
 2 | 
 3 | -------------------------------------------------------------------------------------------
 4 |   CKEditor - Posted Data
 5 | 
 6 |   We are sorry, but your Web server does not support the PHP language used in this script.
 7 | 
 8 |   Please note that CKEditor can be used with any other server-side language than just PHP.
 9 |   To save the content created with CKEditor you need to read the POST data on the server
10 |   side and write it to a file or the database.
11 | 
12 |   Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
13 |   For licensing, see LICENSE.md or http://ckeditor.com/license
14 | -------------------------------------------------------------------------------------------
15 | 
16 | 
*/ include "assets/posteddata.php"; ?> 17 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/samples/tabindex.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | TAB Key-Based Navigation — CKEditor Sample 10 | 11 | 12 | 22 | 42 | 43 | 44 |

45 | CKEditor Samples » TAB Key-Based Navigation 46 |

47 |
48 |

49 | This sample shows how tab key navigation among editor instances is 50 | affected by the tabIndex attribute from 51 | the original page element. Use TAB key to move between the editors. 52 |

53 |
54 |

55 | 56 |

57 |
58 |

59 | 60 |

61 |

62 | 63 |

64 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/samples/uicolor.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | UI Color Picker — CKEditor Sample 10 | 11 | 12 | 13 | 14 |

15 | CKEditor Samples » UI Color 16 |

17 |
18 |

19 | This sample shows how to automatically replace <textarea> elements 20 | with a CKEditor instance with an option to change the color of its user interface.
21 | Note:The UI skin color feature depends on the CKEditor skin 22 | compatibility. The Moono and Kama skins are examples of skins that work with it. 23 |

24 |
25 |
26 |

27 | This editor instance has a UI color value defined in configuration to change the skin color, 28 | To specify the color of the user interface, set the uiColor property: 29 |

30 |
31 | CKEDITOR.replace( 'textarea_id', {
32 | 	uiColor: '#14B8C4'
33 | });
34 |

35 | Note that textarea_id in the code above is the id attribute of 36 | the <textarea> element to be replaced. 37 |

38 |

39 | 40 | 53 |

54 |

55 | 56 |

57 |
58 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/skins/moono/icons.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/icons_hidpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/skins/moono/icons_hidpi.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/images/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/skins/moono/images/arrow.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/skins/moono/images/close.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/images/hidpi/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/skins/moono/images/hidpi/close.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/images/hidpi/lock-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/skins/moono/images/hidpi/lock-open.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/images/hidpi/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/skins/moono/images/hidpi/lock.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/images/hidpi/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/skins/moono/images/hidpi/refresh.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/images/lock-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/skins/moono/images/lock-open.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/images/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/skins/moono/images/lock.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/images/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/ckeditor/skins/moono/images/refresh.png -------------------------------------------------------------------------------- /section-5/public/ckeditor/skins/moono/readme.md: -------------------------------------------------------------------------------- 1 | "Moono" Skin 2 | ==================== 3 | 4 | This skin has been chosen for the **default skin** of CKEditor 4.x, elected from the CKEditor 5 | [skin contest](http://ckeditor.com/blog/new_ckeditor_4_skin) and further shaped by 6 | the CKEditor team. "Moono" is maintained by the core developers. 7 | 8 | For more information about skins, please check the [CKEditor Skin SDK](http://docs.cksource.com/CKEditor_4.x/Skin_SDK) 9 | documentation. 10 | 11 | Features 12 | ------------------- 13 | "Moono" is a monochromatic skin, which offers a modern look coupled with gradients and transparency. 14 | It comes with the following features: 15 | 16 | - Chameleon feature with brightness, 17 | - high-contrast compatibility, 18 | - graphics source provided in SVG. 19 | 20 | Directory Structure 21 | ------------------- 22 | 23 | CSS parts: 24 | - **editor.css**: the main CSS file. It's simply loading several other files, for easier maintenance, 25 | - **mainui.css**: the file contains styles of entire editor outline structures, 26 | - **toolbar.css**: the file contains styles of the editor toolbar space (top), 27 | - **richcombo.css**: the file contains styles of the rich combo ui elements on toolbar, 28 | - **panel.css**: the file contains styles of the rich combo drop-down, it's not loaded 29 | until the first panel open up, 30 | - **elementspath.css**: the file contains styles of the editor elements path bar (bottom), 31 | - **menu.css**: the file contains styles of all editor menus including context menu and button drop-down, 32 | it's not loaded until the first menu open up, 33 | - **dialog.css**: the CSS files for the dialog UI, it's not loaded until the first dialog open, 34 | - **reset.css**: the file defines the basis of style resets among all editor UI spaces, 35 | - **preset.css**: the file defines the default styles of some UI elements reflecting the skin preference, 36 | - **editor_XYZ.css** and **dialog_XYZ.css**: browser specific CSS hacks. 37 | 38 | Other parts: 39 | - **skin.js**: the only JavaScript part of the skin that registers the skin, its browser specific files and its icons and defines the Chameleon feature, 40 | - **icons/**: contains all skin defined icons, 41 | - **images/**: contains a fill general used images, 42 | - **dev/**: contains SVG source of the skin icons. 43 | 44 | License 45 | ------- 46 | 47 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 48 | 49 | Licensed under the terms of any of the following licenses at your choice: [GPL](http://www.gnu.org/licenses/gpl.html), [LGPL](http://www.gnu.org/licenses/lgpl.html) and [MPL](http://www.mozilla.org/MPL/MPL-1.1.html). 50 | 51 | See LICENSE.md for more information. 52 | -------------------------------------------------------------------------------- /section-5/public/images/nodebloglogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/images/nodebloglogo.png -------------------------------------------------------------------------------- /section-5/public/images/uploads/noimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-5/public/images/uploads/noimage.png -------------------------------------------------------------------------------- /section-5/routes/categories.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by aprovis on 08/05/2015. 3 | */ 4 | 5 | var express = require('express'); 6 | var router = express.Router(); 7 | var mongo = require('mongodb'); 8 | var db = require('monk')('localhost/nodeblog'); 9 | 10 | router.get('/show/:category', function(req, res, next) { 11 | var db = req.db; 12 | var posts = db.get('posts'); 13 | posts.find({category: req.params.category}, {}, function(err, posts) { 14 | res.render('index', { 15 | "title": req.params.category, 16 | "posts": posts 17 | }); 18 | }); 19 | }); 20 | 21 | router.get('/add', function(req, res, next) { 22 | res.render('addcategory', { 23 | "title": "Add Category" 24 | }) 25 | }); 26 | 27 | router.post('/add', function(req, res, next) { 28 | // Get form values 29 | var title = req.body.title; 30 | 31 | // Form validation 32 | req.checkBody('title', 'Title field is required').notEmpty(); 33 | 34 | var errors = req.validationErrors(); 35 | 36 | if (errors) { 37 | res.render('addcategory', { 38 | errors: errors, 39 | "title": title 40 | }); 41 | } else { 42 | var categories = db.get('categories'); 43 | 44 | // Submit to database 45 | categories.insert({ 46 | "title": title 47 | }, function(err, category) { 48 | if (err) { 49 | res.send('There was an issue submitting the category'); 50 | } else { 51 | req.flash('success', 'Category Submitted'); 52 | res.location('/'); 53 | res.redirect('/'); 54 | } 55 | }); 56 | } 57 | }); 58 | 59 | module.exports = router; 60 | -------------------------------------------------------------------------------- /section-5/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var mongo = require('mongodb'); 4 | var db = require('monk')('localhost/nodeblog'); 5 | 6 | /* GET home page and display blog posts. */ 7 | router.get('/', function(req, res, next) { 8 | var db = req.db; 9 | var posts = db.get('posts'); 10 | posts.find({}, {}, function(err, posts) { 11 | res.render('index', { 12 | "posts": posts 13 | }); 14 | }); 15 | }); 16 | 17 | module.exports = router; 18 | -------------------------------------------------------------------------------- /section-5/views/addcategory.jade: -------------------------------------------------------------------------------- 1 | // 2 | Created by aprovis on 08/05/2015. 3 | 4 | extends layout 5 | 6 | block content 7 | h1=title 8 | ul.errors 9 | if errors 10 | each error, i in errors 11 | li.alert.alert-danger #{error.msg} 12 | form(method='post', action='/categories/add', enctype='multipart/form-data') 13 | .form-group 14 | label Title: 15 | input.form-control(name='title', type='text') 16 | input.btn.btn-default(name='submit', type='submit', value='Save') -------------------------------------------------------------------------------- /section-5/views/addpost.jade: -------------------------------------------------------------------------------- 1 | // 2 | Created by aprovis on 08/05/2015. 3 | 4 | extends layout 5 | 6 | block content 7 | h1=title 8 | ul.errors 9 | if errors 10 | each error, i in errors 11 | li.alert.alert-danger #{error.msg} 12 | form(method='post', action='/posts/add', enctype='multipart/form-data') 13 | .form-group 14 | label Title: 15 | input.form-control(name='title', type='text') 16 | .form-group 17 | label Category: 18 | select.form-control(name='category') 19 | each category, i in categories 20 | option(value='#{category.title}') #{category.title} 21 | .form-group 22 | label Body: 23 | textarea.form-control(name='body', id='body') 24 | .form-group 25 | label Main Image: 26 | input.form-control(name='mainimage', type='file') 27 | .form-group 28 | label Author: 29 | select.form-control(name='author') 30 | option(value='Andrew Provis') Andrew Provis 31 | option(value='John Doe') John Doe 32 | input.btn.btn-default(name='submit', type='submit', value='Save') 33 | script(src='/ckeditor/ckeditor.js') 34 | script 35 | | CKEDITOR.replace('body'); -------------------------------------------------------------------------------- /section-5/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /section-5/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | if posts 5 | each post, i in posts 6 | .post 7 | h1 8 | a(href='/posts/show/#{post._id}') 9 | =post.title 10 | p.meta Posted in 11 | a(href='/categories/show/#{post.category}') #{post.category} 12 | span by #{post.author} on #{moment(post.date).format("DD-MMM-YYYY")} 13 | img(src='/images/uploads/#{post.mainimage}') 14 | !=truncateText(post.body, 400) 15 | a.more(href='/posts/show/#{post._id}') Read more 16 | else 17 | p.noposts There are no posts to display -------------------------------------------------------------------------------- /section-5/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title NodeBlog 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | .container 8 | img.logo(src='/images/nodebloglogo.png') 9 | nav 10 | ul 11 | li 12 | a(href='/') Home 13 | li 14 | a(href='/posts/add') Add Post 15 | li 16 | a(href='/categories/add') Add Category 17 | != messages() 18 | block content 19 | footer 20 | p NodeBlog © 2015 -------------------------------------------------------------------------------- /section-5/views/show.jade: -------------------------------------------------------------------------------- 1 | // 2 | Created by aprovis on 09/07/15. 3 | 4 | extends layout 5 | 6 | block content 7 | .show 8 | h1=post.title 9 | p.meta Posted in 10 | a(href='/categories/show/#{post.category}') #{post.category} 11 | span by #{post.author} on #{moment(post.date).format("DD-MMM-YYYY")} 12 | img(src='/images/uploads/#{post.mainimage}') 13 | !=post.body 14 | br 15 | hr 16 | if post.comments 17 | h3 Comments 18 | each comment, i in post.comments 19 | .comment 20 | p.comment-name #{comment.name} 21 | p.comment-text #{comment.body} 22 | br 23 | h3 Add Comment 24 | if errors 25 | ul.errors 26 | each error, i in errors 27 | li.alert.alert-danger #{error.msg} 28 | form.comment-form(method='post', action='/posts/addcomment') 29 | input(name='postid', type='hidden', value='#{post._id}') 30 | .form-group 31 | label Name 32 | input.form-control(type='text', name='name') 33 | .form-group 34 | label Email 35 | input.form-control(type='email', name='email') 36 | .form-group 37 | label Body 38 | textarea.form-control(name='body', id='body') 39 | br 40 | input.btn.btn-defaut(name='submit', type='submit', value='Add Comment') -------------------------------------------------------------------------------- /section-6/LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2012 Reza Akhavan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /section-6/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.port = process.env.PORT || 3000; 4 | exports.mongodb = { 5 | uri: process.env.MONGOLAB_URI || process.env.MONGOHQ_URL || 'mongodb://localhost:27017/drywall' 6 | }; 7 | exports.companyName = 'Acme, Inc.'; 8 | exports.projectName = 'Community Events'; 9 | exports.systemEmail = 'example@example.com'; 10 | exports.cryptoKey = 'k3yb0ardc4t'; 11 | exports.loginAttempts = { 12 | forIp: 50, 13 | forIpAndUser: 7, 14 | logExpiration: '20m' 15 | }; 16 | exports.requireAccountVerification = false; 17 | exports.smtp = { 18 | from: { 19 | name: process.env.SMTP_FROM_NAME || exports.projectName +' Website', 20 | address: process.env.SMTP_FROM_ADDRESS || 'your@email.addy' 21 | }, 22 | credentials: { 23 | user: process.env.SMTP_USERNAME || 'your@email.addy', 24 | password: process.env.SMTP_PASSWORD || 'bl4rg!', 25 | host: process.env.SMTP_HOST || 'smtp.gmail.com', 26 | ssl: true 27 | } 28 | }; 29 | exports.oauth = { 30 | twitter: { 31 | key: process.env.TWITTER_OAUTH_KEY || '', 32 | secret: process.env.TWITTER_OAUTH_SECRET || '' 33 | }, 34 | facebook: { 35 | key: process.env.FACEBOOK_OAUTH_KEY || '', 36 | secret: process.env.FACEBOOK_OAUTH_SECRET || '' 37 | }, 38 | github: { 39 | key: process.env.GITHUB_OAUTH_KEY || '', 40 | secret: process.env.GITHUB_OAUTH_SECRET || '' 41 | }, 42 | google: { 43 | key: process.env.GOOGLE_OAUTH_KEY || '', 44 | secret: process.env.GOOGLE_OAUTH_SECRET || '' 45 | }, 46 | tumblr: { 47 | key: process.env.TUMBLR_OAUTH_KEY || '', 48 | secret: process.env.TUMBLR_OAUTH_SECRET || '' 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /section-6/layouts/account.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | block head 5 | title #{title} 6 | meta(name='viewport', content='width=device-width, initial-scale=1.0') 7 | link(rel='stylesheet', href='/layouts/core.min.css?#{cacheBreaker}') 8 | block neck 9 | body 10 | div.navbar.navbar-default.navbar-fixed-top 11 | div.container 12 | div.navbar-header 13 | a.navbar-brand(href='/account/') 14 | img.navbar-logo(src='/media/logo-symbol-64x64.png', alt='Logo') 15 | span.navbar-brand-label #{projectName} 16 | button.navbar-toggle.collapsed(data-toggle='collapse', data-target='.my-navbar-collapse') 17 | span.icon-bar 18 | span.icon-bar 19 | span.icon-bar 20 | div.navbar-collapse.my-navbar-collapse.collapse 21 | ul.nav.navbar-nav 22 | li: a(href='/account/') My Account 23 | li: a(href='/account/settings/') Settings 24 | ul.nav.navbar-nav.pull-right 25 | li: a(href='/logout/') 26 | i.fa.fa-user 27 | | Sign Out 28 | 29 | div.page 30 | div.container 31 | block body 32 | 33 | div.footer 34 | div.container 35 | div.inner 36 | span.copyright.pull-right 37 | |© #{copyrightYear} #{copyrightName} 38 | ul.links 39 | li: a(href='/') Home 40 | li: a(href='/logout/') Sign Out 41 | div.clearfix 42 | 43 | div.ajax-spinner 44 | img(src='/media/ajax-pulse.gif', alt='Loading') 45 | 46 | //if lte IE 9 47 | script(src='/layouts/ie-sucks.min.js?#{cacheBreaker}') 48 | script(src='/layouts/core.min.js?#{cacheBreaker}') 49 | 50 | block feet 51 | -------------------------------------------------------------------------------- /section-6/layouts/default.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | block head 5 | title #{title} 6 | meta(name='viewport', content='width=device-width, initial-scale=1.0') 7 | link(rel='stylesheet', href='/layouts/core.min.css?#{cacheBreaker}') 8 | block neck 9 | body 10 | div.navbar.navbar-default.navbar-fixed-top 11 | div.container 12 | div.navbar-header 13 | a.navbar-brand(href='/') 14 | img.navbar-logo(src='/media/logo-symbol-64x64.png', alt='Logo') 15 | span.navbar-brand-label #{projectName} 16 | button.navbar-toggle.collapsed(data-toggle='collapse', data-target='.my-navbar-collapse') 17 | span.icon-bar 18 | span.icon-bar 19 | span.icon-bar 20 | div.navbar-collapse.my-navbar-collapse.collapse 21 | ul.nav.navbar-nav 22 | li: a(href='/') Home 23 | li: a(href='/about/') About 24 | li: a(href='/signup/') Sign Up 25 | li: a(href='/contact/') Contact 26 | ul.nav.navbar-nav.navbar-right 27 | if user && user.username 28 | li: a(href='#{user.defaultReturnUrl}') 29 | i.fa.fa-user 30 | | #{user.username} 31 | else 32 | li: a(href='/login/') 33 | i.fa.fa-user 34 | | Sign In 35 | 36 | div.page 37 | div.container 38 | block body 39 | 40 | div.footer 41 | div.container 42 | span.copyright.pull-right 43 | |© #{copyrightYear} #{copyrightName} 44 | ul.links 45 | li: a(href='/') Home 46 | li: a(href='/contact/') Contact 47 | div.clearfix 48 | 49 | div.ajax-spinner 50 | img(src='/media/ajax-pulse.gif', alt='Loading') 51 | 52 | //if lte IE 9 53 | script(src='/layouts/ie-sucks.min.js?#{cacheBreaker}') 54 | script(src='/layouts/core.min.js?#{cacheBreaker}') 55 | 56 | block feet 57 | -------------------------------------------------------------------------------- /section-6/models.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(app, mongoose) { 4 | //embeddable docs first 5 | require('./schema/Note')(app, mongoose); 6 | require('./schema/Status')(app, mongoose); 7 | require('./schema/StatusLog')(app, mongoose); 8 | require('./schema/Category')(app, mongoose); 9 | 10 | //then regular docs 11 | require('./schema/User')(app, mongoose); 12 | require('./schema/Admin')(app, mongoose); 13 | require('./schema/AdminGroup')(app, mongoose); 14 | require('./schema/Account')(app, mongoose); 15 | require('./schema/LoginAttempt')(app, mongoose); 16 | }; 17 | -------------------------------------------------------------------------------- /section-6/npm-debug.log: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'install' ] 3 | 2 info using npm@1.4.28 4 | 3 info using node@v0.10.35 5 | 4 error install Couldn't read dependencies 6 | 5 error package.json ENOENT, open '/Users/aprovis/dev/personal-dev/learning-nodejs-10-projects/section-6/package.json' 7 | 5 error package.json This is most likely not a problem with npm itself. 8 | 5 error package.json npm can't find a package.json file in your current directory. 9 | 6 error System Darwin 14.4.0 10 | 7 error command "/usr/local/bin/node" "/usr/local/bin/npm" "install" 11 | 8 error cwd /Users/aprovis/dev/personal-dev/learning-nodejs-10-projects/section-6 12 | 9 error node -v v0.10.35 13 | 10 error npm -v 1.4.28 14 | 11 error path /Users/aprovis/dev/personal-dev/learning-nodejs-10-projects/section-6/package.json 15 | 12 error code ENOPACKAGEJSON 16 | 13 error errno 34 17 | 14 verbose exit [ 34, true ] 18 | -------------------------------------------------------------------------------- /section-6/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Drywall", 3 | "version": "0.16.4", 4 | "private": true, 5 | "scripts": { 6 | "start": "grunt", 7 | "update": "david update" 8 | }, 9 | "dependencies": { 10 | "async": "^1.x.x", 11 | "backbone": "^1.x.x", 12 | "bcrypt": "^0.8.x", 13 | "body-parser": "^1.x.x", 14 | "compression": "^1.x.x", 15 | "connect-mongo": "^0.8.x", 16 | "cookie-parser": "^1.x.x", 17 | "csurf": "^1.x.x", 18 | "emailjs": "^0.3.x", 19 | "express": "^4.12.x", 20 | "express-session": "^1.x.x", 21 | "helmet": "^0.9.x", 22 | "jade": "^1.x.x", 23 | "method-override": "^2.x.x", 24 | "mongoose": "^4.x.x", 25 | "morgan": "^1.x.x", 26 | "passport": "^0.2.x", 27 | "passport-facebook": "^2.x.x", 28 | "passport-github": "^0.1.x", 29 | "passport-google-oauth": "^0.2.x", 30 | "passport-local": "^1.x.x", 31 | "passport-oauth": "^1.x.x", 32 | "passport-tumblr": "^0.1.x", 33 | "passport-twitter": "^1.x.x", 34 | "serve-static": "^1.9.x", 35 | "underscore": "^1.x.x" 36 | }, 37 | "devDependencies": { 38 | "bootstrap": "^3.x.x", 39 | "david": "^6.x.x", 40 | "font-awesome": "^4.x.x", 41 | "grunt": "^0.4.x", 42 | "grunt-cli": "^0.1.13", 43 | "grunt-concurrent": "^2.x.x", 44 | "grunt-contrib-clean": "^0.6.x", 45 | "grunt-contrib-copy": "^0.8.x", 46 | "grunt-contrib-jshint": "^0.11.x", 47 | "grunt-contrib-less": "^1.x.x", 48 | "grunt-contrib-uglify": "^0.9.x", 49 | "grunt-contrib-watch": "^0.6.x", 50 | "grunt-newer": "^1.x.x", 51 | "grunt-nodemon": "^0.4.x", 52 | "html5shiv": "^3.x.x", 53 | "jquery": "^2.x.x", 54 | "jquery.cookie": "^1.x.x", 55 | "moment": "^2.x.x", 56 | "respond.js": "^1.x.x" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /section-6/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-6/public/favicon.ico -------------------------------------------------------------------------------- /section-6/public/layouts/admin.less: -------------------------------------------------------------------------------- 1 | .navbar { 2 | .navbar-brand { 3 | color: #eee; 4 | } 5 | 6 | .navbar-form .dropdown { 7 | position: relative; 8 | } 9 | } 10 | 11 | .navbar .nav > li > a { 12 | text-transform: uppercase; 13 | font-weight: bold; 14 | } 15 | 16 | .filters { 17 | margin-bottom: 20px; 18 | } 19 | 20 | .stretch { 21 | width: 100%; 22 | } 23 | 24 | .nowrap { 25 | white-space: nowrap; 26 | } 27 | -------------------------------------------------------------------------------- /section-6/public/layouts/core.js: -------------------------------------------------------------------------------- 1 | /* global app:true */ 2 | /* exported app */ 3 | 4 | var app; //the main declaration 5 | 6 | (function() { 7 | 'use strict'; 8 | 9 | $(document).ready(function() { 10 | //active (selected) navigation elements 11 | $('.nav [href="'+ window.location.pathname +'"]').closest('li').toggleClass('active'); 12 | 13 | //register global ajax handlers 14 | $(document).ajaxStart(function(){ $('.ajax-spinner').show(); }); 15 | $(document).ajaxStop(function(){ $('.ajax-spinner').hide(); }); 16 | $.ajaxSetup({ 17 | beforeSend: function (xhr) { 18 | xhr.setRequestHeader('x-csrf-token', $.cookie('_csrfToken')); 19 | } 20 | }); 21 | 22 | //ajax spinner follows mouse 23 | $(document).bind('mousemove', function(e) { 24 | $('.ajax-spinner').css({ 25 | left: e.pageX + 15, 26 | top: e.pageY 27 | }); 28 | }); 29 | }); 30 | }()); 31 | -------------------------------------------------------------------------------- /section-6/public/layouts/core.less: -------------------------------------------------------------------------------- 1 | @import "../vendor/bootstrap/less/mixins.less"; 2 | 3 | body { 4 | padding-top: 50px; 5 | } 6 | 7 | h1, h2 { 8 | letter-spacing: -1px; 9 | } 10 | 11 | fieldset { 12 | min-width: 0; 13 | margin-bottom: 20px; 14 | } 15 | 16 | table thead th { 17 | background-color: #ddd; 18 | } 19 | 20 | select, textarea, input, .form-control { 21 | font-size: 16px; //ios input zoom prevention hack 22 | } 23 | 24 | .input-group-btn .btn-group:first-of-type .btn { 25 | .border-left-radius(0); 26 | } 27 | 28 | .dropdown-header { 29 | font-weight: bold; 30 | text-transform: uppercase; 31 | padding-left: 12px; 32 | } 33 | 34 | .navbar { 35 | .box-shadow(0 2px 5px rgba(0,0,0,.125)); 36 | 37 | .navbar-brand { 38 | font-weight: bold; 39 | color: #003d5f; 40 | position: relative; 41 | display: inline-block; 42 | 43 | .navbar-logo { 44 | position: absolute; 45 | top: 10px; 46 | height: 32px; 47 | width: 32px; 48 | } 49 | } 50 | .navbar-brand-label { 51 | margin-left: 35px; 52 | } 53 | } 54 | 55 | .footer { 56 | margin-top: 50px; 57 | color: #999; 58 | 59 | .container { 60 | border-top: 1px solid #ccc; 61 | padding: 20px 5px 20px 5px; 62 | border-radius: 4px; 63 | } 64 | a, a:visited { 65 | color: #999; 66 | } 67 | .links { 68 | margin: 0; 69 | padding: 0; 70 | 71 | li { 72 | display: inline-block; 73 | padding-right: 10px; 74 | margin-right: 10px; 75 | list-style: none; 76 | border-right: 1px dotted #999; 77 | } 78 | li:last-child { 79 | padding-right: 0; 80 | margin-right: 0; 81 | border: none; 82 | } 83 | } 84 | } 85 | 86 | .ajax-spinner { 87 | position: absolute; 88 | z-index: 999; 89 | display: none; 90 | padding: 5px; 91 | font-size: inherit; 92 | line-height: 11px; 93 | background-color: #ccc; 94 | border-radius: 4px; 95 | 96 | img { 97 | width: inherit; 98 | height: inherit; 99 | margin: 0; 100 | } 101 | }​ 102 | 103 | .force-wrap { 104 | white-space: pre-wrap; /* css-3 */ 105 | white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ 106 | white-space: -pre-wrap; /* Opera 4-6 */ 107 | white-space: -o-pre-wrap; /* Opera 7 */ 108 | word-wrap: break-word; /* Internet Explorer 5.5+ */ 109 | } 110 | -------------------------------------------------------------------------------- /section-6/public/layouts/ie-sucks.js: -------------------------------------------------------------------------------- 1 | //damn you ie! 2 | -------------------------------------------------------------------------------- /section-6/public/less/bootstrap-build.less: -------------------------------------------------------------------------------- 1 | // Core variables and mixins 2 | @import "bootstrap-vars.less"; 3 | @import "../vendor/bootstrap/less/mixins.less"; 4 | 5 | // Reset 6 | @import "../vendor/bootstrap/less/normalize.less"; 7 | @import "../vendor/bootstrap/less/print.less"; 8 | 9 | // Core CSS 10 | @import "../vendor/bootstrap/less/scaffolding.less"; 11 | @import "../vendor/bootstrap/less/type.less"; 12 | @import "../vendor/bootstrap/less/code.less"; 13 | @import "../vendor/bootstrap/less/grid.less"; 14 | @import "../vendor/bootstrap/less/tables.less"; 15 | @import "../vendor/bootstrap/less/forms.less"; 16 | @import "../vendor/bootstrap/less/buttons.less"; 17 | 18 | // Components 19 | @import "../vendor/bootstrap/less/component-animations.less"; 20 | @import "../vendor/bootstrap/less/glyphicons.less"; 21 | @import "../vendor/bootstrap/less/dropdowns.less"; 22 | @import "../vendor/bootstrap/less/button-groups.less"; 23 | @import "../vendor/bootstrap/less/input-groups.less"; 24 | @import "../vendor/bootstrap/less/navs.less"; 25 | @import "../vendor/bootstrap/less/navbar.less"; 26 | @import "../vendor/bootstrap/less/breadcrumbs.less"; 27 | @import "../vendor/bootstrap/less/pagination.less"; 28 | @import "../vendor/bootstrap/less/pager.less"; 29 | @import "../vendor/bootstrap/less/labels.less"; 30 | @import "../vendor/bootstrap/less/badges.less"; 31 | @import "../vendor/bootstrap/less/jumbotron.less"; 32 | @import "../vendor/bootstrap/less/thumbnails.less"; 33 | @import "../vendor/bootstrap/less/alerts.less"; 34 | @import "../vendor/bootstrap/less/progress-bars.less"; 35 | @import "../vendor/bootstrap/less/media.less"; 36 | @import "../vendor/bootstrap/less/list-group.less"; 37 | @import "../vendor/bootstrap/less/panels.less"; 38 | @import "../vendor/bootstrap/less/wells.less"; 39 | @import "../vendor/bootstrap/less/close.less"; 40 | 41 | // Components w/ JavaScript 42 | @import "../vendor/bootstrap/less/modals.less"; 43 | @import "../vendor/bootstrap/less/tooltip.less"; 44 | @import "../vendor/bootstrap/less/popovers.less"; 45 | @import "../vendor/bootstrap/less/carousel.less"; 46 | 47 | // Utility classes 48 | @import "../vendor/bootstrap/less/utilities.less"; 49 | @import "../vendor/bootstrap/less/responsive-utilities.less"; 50 | 51 | // Theme 52 | @import "bootstrap-theme.less"; 53 | -------------------------------------------------------------------------------- /section-6/public/less/font-awesome-build.less: -------------------------------------------------------------------------------- 1 | @import "../vendor/font-awesome/less/variables.less"; 2 | @import "font-awesome-vars.less"; 3 | @import "../vendor/font-awesome/less/mixins.less"; 4 | @import "../vendor/font-awesome/less/path.less"; 5 | @import "../vendor/font-awesome/less/core.less"; 6 | @import "../vendor/font-awesome/less/larger.less"; 7 | @import "../vendor/font-awesome/less/fixed-width.less"; 8 | @import "../vendor/font-awesome/less/list.less"; 9 | @import "../vendor/font-awesome/less/bordered-pulled.less"; 10 | @import "../vendor/font-awesome/less/animated.less"; 11 | @import "../vendor/font-awesome/less/rotated-flipped.less"; 12 | @import "../vendor/font-awesome/less/stacked.less"; 13 | @import "../vendor/font-awesome/less/icons.less"; 14 | -------------------------------------------------------------------------------- /section-6/public/less/font-awesome-vars.less: -------------------------------------------------------------------------------- 1 | @fa-font-path: "/vendor/font-awesome/fonts"; 2 | -------------------------------------------------------------------------------- /section-6/public/media/ajax-pulse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-6/public/media/ajax-pulse.gif -------------------------------------------------------------------------------- /section-6/public/media/logo-symbol-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-6/public/media/logo-symbol-32x32.png -------------------------------------------------------------------------------- /section-6/public/media/logo-symbol-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewprovis/learning-nodejs-10-projects/fd8a9b02cec1a2c22fc0cdfc7bee102b93f6ae5d/section-6/public/media/logo-symbol-64x64.png -------------------------------------------------------------------------------- /section-6/public/views/about/index.less: -------------------------------------------------------------------------------- 1 | .special { 2 | text-align: center; 3 | } 4 | .super-awesome { 5 | display: block; 6 | margin-top: -15px; 7 | color: #7f7f7f; 8 | font-size: 20em; 9 | } 10 | -------------------------------------------------------------------------------- /section-6/public/views/account/index.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | $('.day-of-year').text(moment().format('DDD')); 5 | $('.day-of-month').text(moment().format('D')); 6 | $('.week-of-year').text(moment().format('w')); 7 | $('.day-of-week').text(moment().format('d')); 8 | $('.week-year').text(moment().format('gg')); 9 | $('.hour-of-day').text(moment().format('H')); 10 | }()); 11 | -------------------------------------------------------------------------------- /section-6/public/views/account/index.less: -------------------------------------------------------------------------------- 1 | .special { 2 | text-align: center; 3 | } 4 | .super-awesome { 5 | display: block; 6 | margin-top: -15px; 7 | color: #7f7f7f; 8 | font-size: 20em; 9 | } 10 | 11 | .stat { 12 | text-align: center; 13 | } 14 | .stat-value { 15 | color: #555; 16 | font-size: 2.2em; 17 | font-weight: bold; 18 | letter-spacing: -1px; 19 | } 20 | .stat-label { 21 | color: #999; 22 | font-weight: bold; 23 | } 24 | -------------------------------------------------------------------------------- /section-6/public/views/account/verification/index.js: -------------------------------------------------------------------------------- 1 | /* global app:true */ 2 | 3 | (function() { 4 | 'use strict'; 5 | 6 | app = app || {}; 7 | 8 | app.Verify = Backbone.Model.extend({ 9 | url: '/account/verification/', 10 | defaults: { 11 | success: false, 12 | errors: [], 13 | errfor: {}, 14 | keepFormOpen: false, 15 | email: '' 16 | } 17 | }); 18 | 19 | app.VerifyView = Backbone.View.extend({ 20 | el: '#verify', 21 | template: _.template( $('#tmpl-verify').html() ), 22 | events: { 23 | 'submit form': 'preventSubmit', 24 | 'click .btn-resend': 'resend', 25 | 'click .btn-verify': 'verify' 26 | }, 27 | initialize: function() { 28 | this.model = new app.Verify( JSON.parse($('#data-user').html()) ); 29 | this.listenTo(this.model, 'sync', this.render); 30 | this.render(); 31 | }, 32 | render: function() { 33 | this.$el.html(this.template( this.model.attributes )); 34 | }, 35 | preventSubmit: function(event) { 36 | event.preventDefault(); 37 | }, 38 | resend: function() { 39 | this.model.set({ 40 | keepFormOpen: true 41 | }); 42 | this.render(); 43 | }, 44 | verify: function() { 45 | this.$el.find('.btn-verify').attr('disabled', true); 46 | 47 | this.model.save({ 48 | email: this.$el.find('[name="email"]').val() 49 | }); 50 | } 51 | }); 52 | 53 | $(document).ready(function() { 54 | app.verifyView = new app.VerifyView(); 55 | }); 56 | }()); 57 | -------------------------------------------------------------------------------- /section-6/public/views/account/verification/index.less: -------------------------------------------------------------------------------- 1 | .special { 2 | text-align: center; 3 | } 4 | .super-awesome { 5 | display: block; 6 | margin-top: -15px; 7 | color: #7f7f7f; 8 | font-size: 20em; 9 | } 10 | .not-received-hidden { 11 | display: none; 12 | } 13 | .verify-form-hidden { 14 | display: none; 15 | } 16 | -------------------------------------------------------------------------------- /section-6/public/views/admin/accounts/details.less: -------------------------------------------------------------------------------- 1 | @import "../../../vendor/bootstrap/less/mixins.less"; 2 | 3 | .status-items { 4 | overflow: scroll; 5 | 6 | .status { 7 | padding: 10px 5px; 8 | font-size: 12px; 9 | border-bottom: 1px solid #ccc; 10 | 11 | .author { 12 | font-weight: normal; 13 | color: #999; 14 | text-shadow: none; 15 | background-color: transparent; 16 | border: 1px solid #ccc; 17 | } 18 | } 19 | .status:nth-child(even) { 20 | background-color: #f5f5f5; 21 | } 22 | } 23 | 24 | .notes-new { 25 | textarea { 26 | margin-bottom: 0; 27 | border-bottom: none; 28 | .border-bottom-radius(0); 29 | } 30 | .btn { 31 | .border-top-radius(0); 32 | } 33 | } 34 | 35 | .notes-items { 36 | .note { 37 | padding: 10px 5px; 38 | font-size: 12px; 39 | border-bottom: 1px solid #ccc; 40 | 41 | .author { 42 | font-weight: normal; 43 | color: #999; 44 | text-shadow: none; 45 | background-color: transparent; 46 | border: 1px solid #ccc; 47 | } 48 | } 49 | .note:nth-child(even) { 50 | background-color: #f5f5f5; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /section-6/public/views/admin/accounts/index.less: -------------------------------------------------------------------------------- 1 | @import "../../../vendor/bootstrap/less/mixins.less"; 2 | 3 | .input-group { 4 | margin-bottom: 20px; 5 | 6 | input { 7 | width: 200px !important; 8 | } 9 | 10 | .btn { 11 | .border-left-radius(0); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /section-6/public/views/admin/admin-groups/details.less: -------------------------------------------------------------------------------- 1 | .permissions .input-group input[disabled] { 2 | border-radius: 0; 3 | border-bottom: none; 4 | } 5 | .permissions .input-group .btn { 6 | border-radius: 0; 7 | } 8 | .permissions .input-group:first-of-type input[disabled] { 9 | border-top-left-radius: 4px; 10 | } 11 | .permissions .input-group:first-of-type .btn-delete { 12 | border-top-right-radius: 4px; 13 | } 14 | .permissions .input-group:last-of-type input[disabled] { 15 | border-bottom-left-radius: 4px; 16 | border-bottom: 1px solid #ccc; 17 | } 18 | .permissions .input-group:last-of-type .btn-delete { 19 | border-bottom-right-radius: 4px; 20 | } 21 | -------------------------------------------------------------------------------- /section-6/public/views/admin/admin-groups/index.less: -------------------------------------------------------------------------------- 1 | @import "../../../vendor/bootstrap/less/mixins.less"; 2 | 3 | .input-group { 4 | margin-bottom: 20px; 5 | 6 | input { 7 | width: 200px !important; 8 | } 9 | 10 | .btn { 11 | .border-left-radius(0); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /section-6/public/views/admin/administrators/details.less: -------------------------------------------------------------------------------- 1 | .groups .input-group input[disabled], 2 | .permissions .input-group input[disabled] { 3 | border-radius: 0; 4 | border-bottom: none; 5 | } 6 | .groups .input-group .btn, 7 | .permissions .input-group .btn { 8 | border-radius: 0; 9 | } 10 | .groups .input-group:first-of-type input[disabled], 11 | .permissions .input-group:first-of-type input[disabled] { 12 | border-top-left-radius: 4px; 13 | } 14 | .groups .input-group:first-of-type .btn-delete, 15 | .permissions .input-group:first-of-type .btn-delete { 16 | border-top-right-radius: 4px; 17 | } 18 | .groups .input-group:last-of-type input[disabled], 19 | .permissions .input-group:last-of-type input[disabled] { 20 | border-bottom-left-radius: 4px; 21 | border-bottom: 1px solid #ccc; 22 | } 23 | .groups .input-group:last-of-type .btn-delete, 24 | .permissions .input-group:last-of-type .btn-delete { 25 | border-bottom-right-radius: 4px; 26 | } 27 | -------------------------------------------------------------------------------- /section-6/public/views/admin/administrators/index.less: -------------------------------------------------------------------------------- 1 | @import "../../../vendor/bootstrap/less/mixins.less"; 2 | 3 | .input-group { 4 | margin-bottom: 20px; 5 | 6 | input { 7 | width: 200px !important; 8 | } 9 | 10 | .btn { 11 | .border-left-radius(0); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /section-6/public/views/admin/categories/index.less: -------------------------------------------------------------------------------- 1 | @import "../../../vendor/bootstrap/less/mixins.less"; 2 | @import "../../../less/bootstrap-vars.less"; 3 | 4 | .input-group { 5 | margin-bottom: 20px; 6 | 7 | input { 8 | width: 100px !important; 9 | 10 | @media screen and (min-width: @grid-float-breakpoint) { 11 | width: 160px !important; 12 | } 13 | } 14 | 15 | input:nth-child(2) { 16 | border-left: none; 17 | } 18 | 19 | .btn { 20 | .border-left-radius(0) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /section-6/public/views/admin/index.less: -------------------------------------------------------------------------------- 1 | @import "../../vendor/bootstrap/less/mixins.less"; 2 | 3 | .special { 4 | text-align: center; 5 | } 6 | .super-awesome { 7 | display: block; 8 | margin-top: -15px; 9 | color: #7f7f7f; 10 | font-size: 20em; 11 | } 12 | 13 | .stat { 14 | text-align: center; 15 | #gradient > .vertical(@start-color: #555; @end-color: darken(#555, 12%)); 16 | background-color: #555; 17 | padding-left: 0; 18 | padding-right: 0; 19 | } 20 | .stat-value { 21 | color: #fff; 22 | font-size: 2.2em; 23 | font-weight: bold; 24 | letter-spacing: -1px; 25 | } 26 | .stat-label { 27 | color: #ccc; 28 | font-weight: bold; 29 | } 30 | -------------------------------------------------------------------------------- /section-6/public/views/admin/statuses/index.less: -------------------------------------------------------------------------------- 1 | @import "../../../vendor/bootstrap/less/mixins.less"; 2 | @import "../../../less/bootstrap-vars.less"; 3 | 4 | .input-group { 5 | margin-bottom: 20px; 6 | 7 | input { 8 | width: 100px !important; 9 | 10 | @media screen and (min-width: @grid-float-breakpoint) { 11 | width: 160px !important; 12 | } 13 | } 14 | 15 | input:nth-child(2) { 16 | border-left: none; 17 | } 18 | 19 | .btn { 20 | .border-left-radius(0) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /section-6/public/views/admin/users/index.less: -------------------------------------------------------------------------------- 1 | @import "../../../vendor/bootstrap/less/mixins.less"; 2 | 3 | .input-group { 4 | margin-bottom: 20px; 5 | 6 | input { 7 | width: 200px !important; 8 | } 9 | 10 | .btn { 11 | .border-left-radius(0); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /section-6/public/views/contact/index.js: -------------------------------------------------------------------------------- 1 | /* global app:true */ 2 | 3 | (function() { 4 | 'use strict'; 5 | 6 | app = app || {}; 7 | 8 | app.Contact = Backbone.Model.extend({ 9 | url: '/contact/', 10 | defaults: { 11 | success: false, 12 | errors: [], 13 | errfor: {}, 14 | name: '', 15 | email: '', 16 | message: '' 17 | } 18 | }); 19 | 20 | app.ContactView = Backbone.View.extend({ 21 | el: '#contact', 22 | template: _.template( $('#tmpl-contact').html() ), 23 | events: { 24 | 'submit form': 'preventSubmit', 25 | 'click .btn-contact': 'contact' 26 | }, 27 | initialize: function() { 28 | this.model = new app.Contact(); 29 | this.listenTo(this.model, 'sync', this.render); 30 | this.render(); 31 | }, 32 | render: function() { 33 | this.$el.html(this.template( this.model.attributes )); 34 | this.$el.find('[name="name"]').focus(); 35 | }, 36 | preventSubmit: function(event) { 37 | event.preventDefault(); 38 | }, 39 | contact: function() { 40 | this.$el.find('.btn-contact').attr('disabled', true); 41 | 42 | this.model.save({ 43 | name: this.$el.find('[name="name"]').val(), 44 | email: this.$el.find('[name="email"]').val(), 45 | message: this.$el.find('[name="message"]').val() 46 | }); 47 | } 48 | }); 49 | 50 | $(document).ready(function() { 51 | app.contactView = new app.ContactView(); 52 | }); 53 | }()); 54 | -------------------------------------------------------------------------------- /section-6/public/views/contact/index.less: -------------------------------------------------------------------------------- 1 | .special { 2 | text-align: center; 3 | } 4 | .super-awesome { 5 | display: block; 6 | margin-top: -15px; 7 | color: #7f7f7f; 8 | font-size: 20em; 9 | } 10 | -------------------------------------------------------------------------------- /section-6/public/views/index.less: -------------------------------------------------------------------------------- 1 | @import '../less/bootstrap-vars.less'; 2 | 3 | .jumbotron { 4 | background-color: transparent; 5 | text-align: center; 6 | padding: 30px 60px; 7 | 8 | h1 { 9 | font-weight: bold; 10 | } 11 | 12 | @media (max-width: @screen-tablet) { 13 | h1 { 14 | font-size: (@font-size-base * 3.5); 15 | } 16 | } 17 | @media (min-width: @screen-tablet) { 18 | h1 { 19 | font-size: (@font-size-base * 7.5); 20 | } 21 | } 22 | } 23 | 24 | .panel h3 { 25 | margin-top: 0; 26 | } 27 | -------------------------------------------------------------------------------- /section-6/public/views/login/forgot/index.js: -------------------------------------------------------------------------------- 1 | /* global app:true */ 2 | 3 | (function() { 4 | 'use strict'; 5 | 6 | app = app || {}; 7 | 8 | app.Forgot = Backbone.Model.extend({ 9 | url: '/login/forgot/', 10 | defaults: { 11 | success: false, 12 | errors: [], 13 | errfor: {}, 14 | email: '', 15 | } 16 | }); 17 | 18 | app.ForgotView = Backbone.View.extend({ 19 | el: '#forgot', 20 | template: _.template( $('#tmpl-forgot').html() ), 21 | events: { 22 | 'submit form': 'preventSubmit', 23 | 'keypress [name="email"]': 'forgotOnEnter', 24 | 'click .btn-forgot': 'forgot' 25 | }, 26 | initialize: function() { 27 | this.model = new app.Forgot(); 28 | this.listenTo(this.model, 'sync', this.render); 29 | this.render(); 30 | }, 31 | render: function() { 32 | this.$el.html(this.template( this.model.attributes )); 33 | this.$el.find('[name="email"]').focus(); 34 | return this; 35 | }, 36 | preventSubmit: function(event) { 37 | event.preventDefault(); 38 | }, 39 | forgotOnEnter: function(event) { 40 | if (event.keyCode !== 13) { return; } 41 | event.preventDefault(); 42 | this.forgot(); 43 | }, 44 | forgot: function() { 45 | this.$el.find('.btn-forgot').attr('disabled', true); 46 | 47 | this.model.save({ 48 | email: this.$el.find('[name="email"]').val() 49 | }); 50 | } 51 | }); 52 | 53 | $(document).ready(function() { 54 | app.forgotView = new app.ForgotView(); 55 | }); 56 | }()); 57 | -------------------------------------------------------------------------------- /section-6/public/views/login/index.js: -------------------------------------------------------------------------------- 1 | /* global app:true */ 2 | 3 | (function() { 4 | 'use strict'; 5 | 6 | app = app || {}; 7 | 8 | app.Login = Backbone.Model.extend({ 9 | url: '/login/', 10 | defaults: { 11 | errors: [], 12 | errfor: {}, 13 | username: '', 14 | password: '' 15 | } 16 | }); 17 | 18 | app.LoginView = Backbone.View.extend({ 19 | el: '#login', 20 | template: _.template( $('#tmpl-login').html() ), 21 | events: { 22 | 'submit form': 'preventSubmit', 23 | 'keypress [name="password"]': 'loginOnEnter', 24 | 'click .btn-login': 'login' 25 | }, 26 | initialize: function() { 27 | this.model = new app.Login(); 28 | this.listenTo(this.model, 'sync', this.render); 29 | this.render(); 30 | }, 31 | render: function() { 32 | this.$el.html(this.template( this.model.attributes )); 33 | this.$el.find('[name="username"]').focus(); 34 | }, 35 | preventSubmit: function(event) { 36 | event.preventDefault(); 37 | }, 38 | loginOnEnter: function(event) { 39 | if (event.keyCode !== 13) { return; } 40 | if ($(event.target).attr('name') !== 'password') { return; } 41 | event.preventDefault(); 42 | this.login(); 43 | }, 44 | login: function() { 45 | this.$el.find('.btn-login').attr('disabled', true); 46 | 47 | this.model.save({ 48 | username: this.$el.find('[name="username"]').val(), 49 | password: this.$el.find('[name="password"]').val() 50 | },{ 51 | success: function(model, response) { 52 | if (response.success) { 53 | location.href = '/login/'; 54 | } 55 | else { 56 | model.set(response); 57 | } 58 | } 59 | }); 60 | } 61 | }); 62 | 63 | $(document).ready(function() { 64 | app.loginView = new app.LoginView(); 65 | }); 66 | }()); 67 | -------------------------------------------------------------------------------- /section-6/public/views/login/reset/index.js: -------------------------------------------------------------------------------- 1 | /* global app:true */ 2 | 3 | (function() { 4 | 'use strict'; 5 | 6 | app = app || {}; 7 | 8 | app.Reset = Backbone.Model.extend({ 9 | defaults: { 10 | success: false, 11 | errors: [], 12 | errfor: {}, 13 | id: undefined, 14 | email: undefined, 15 | password: '', 16 | confirm: '' 17 | }, 18 | url: function() { 19 | return '/login/reset/'+ this.get('email') +'/'+ this.id +'/'; 20 | } 21 | }); 22 | 23 | app.ResetView = Backbone.View.extend({ 24 | el: '#reset', 25 | template: _.template( $('#tmpl-reset').html() ), 26 | events: { 27 | 'submit form': 'preventSubmit', 28 | 'keypress [name="confirm"]': 'resetOnEnter', 29 | 'click .btn-reset': 'reset' 30 | }, 31 | initialize: function() { 32 | this.listenTo(this.model, 'sync', this.render); 33 | this.render(); 34 | }, 35 | render: function() { 36 | this.$el.html(this.template( this.model.attributes )); 37 | this.$el.find('[name="password"]').focus(); 38 | return this; 39 | }, 40 | preventSubmit: function(event) { 41 | event.preventDefault(); 42 | }, 43 | resetOnEnter: function(event) { 44 | if (event.keyCode !== 13) { return; } 45 | event.preventDefault(); 46 | this.reset(); 47 | }, 48 | reset: function() { 49 | this.$el.find('.btn-reset').attr('disabled', true); 50 | 51 | this.model.save({ 52 | password: this.$el.find('[name="password"]').val(), 53 | confirm: this.$el.find('[name="confirm"]').val() 54 | }); 55 | } 56 | }); 57 | 58 | app.Router = Backbone.Router.extend({ 59 | routes: { 60 | 'login/reset/': 'start', 61 | 'login/reset/:email/:token/': 'start' 62 | }, 63 | start: function(email, token) { 64 | app.resetView = new app.ResetView({ model: new app.Reset({ id: token, email: email }) }); 65 | } 66 | }); 67 | 68 | $(document).ready(function() { 69 | app.router = new app.Router(); 70 | Backbone.history.start({ pushState: true }); 71 | }); 72 | }()); 73 | -------------------------------------------------------------------------------- /section-6/public/views/signup/index.js: -------------------------------------------------------------------------------- 1 | /* global app:true */ 2 | 3 | (function() { 4 | 'use strict'; 5 | 6 | app = app || {}; 7 | 8 | app.Signup = Backbone.Model.extend({ 9 | url: '/signup/', 10 | defaults: { 11 | errors: [], 12 | errfor: {}, 13 | username: '', 14 | email: '', 15 | password: '' 16 | } 17 | }); 18 | 19 | app.SignupView = Backbone.View.extend({ 20 | el: '#signup', 21 | template: _.template( $('#tmpl-signup').html() ), 22 | events: { 23 | 'submit form': 'preventSubmit', 24 | 'keypress [name="password"]': 'signupOnEnter', 25 | 'click .btn-signup': 'signup' 26 | }, 27 | initialize: function() { 28 | this.model = new app.Signup(); 29 | this.listenTo(this.model, 'sync', this.render); 30 | this.render(); 31 | }, 32 | render: function() { 33 | this.$el.html(this.template( this.model.attributes )); 34 | this.$el.find('[name="username"]').focus(); 35 | }, 36 | preventSubmit: function(event) { 37 | event.preventDefault(); 38 | }, 39 | signupOnEnter: function(event) { 40 | if (event.keyCode !== 13) { return; } 41 | if ($(event.target).attr('name') !== 'password') { return; } 42 | event.preventDefault(); 43 | this.signup(); 44 | }, 45 | signup: function() { 46 | this.$el.find('.btn-signup').attr('disabled', true); 47 | 48 | this.model.save({ 49 | username: this.$el.find('[name="username"]').val(), 50 | email: this.$el.find('[name="email"]').val(), 51 | password: this.$el.find('[name="password"]').val() 52 | },{ 53 | success: function(model, response) { 54 | if (response.success) { 55 | location.href = '/account/'; 56 | } 57 | else { 58 | model.set(response); 59 | } 60 | } 61 | }); 62 | } 63 | }); 64 | 65 | $(document).ready(function() { 66 | app.signupView = new app.SignupView(); 67 | }); 68 | }()); 69 | -------------------------------------------------------------------------------- /section-6/public/views/signup/index.less: -------------------------------------------------------------------------------- 1 | .marketing { 2 | text-align: center; 3 | } 4 | .super-awesome { 5 | display: block; 6 | margin-top: 50px; 7 | color: #7f7f7f; 8 | font-size: 20em; 9 | } 10 | -------------------------------------------------------------------------------- /section-6/public/views/signup/social.js: -------------------------------------------------------------------------------- 1 | /* global app:true */ 2 | 3 | (function() { 4 | 'use strict'; 5 | 6 | app = app || {}; 7 | 8 | app.Signup = Backbone.Model.extend({ 9 | url: '/signup/social/', 10 | defaults: { 11 | errors: [], 12 | errfor: {}, 13 | email: '' 14 | } 15 | }); 16 | 17 | app.SignupView = Backbone.View.extend({ 18 | el: '#signup', 19 | template: _.template( $('#tmpl-signup').html() ), 20 | events: { 21 | 'submit form': 'preventSubmit', 22 | 'keypress [name="password"]': 'signupOnEnter', 23 | 'click .btn-signup': 'signup' 24 | }, 25 | initialize: function() { 26 | this.model = new app.Signup(); 27 | this.model.set('email', $('#data-email').text()); 28 | this.listenTo(this.model, 'sync', this.render); 29 | this.render(); 30 | }, 31 | render: function() { 32 | this.$el.html(this.template( this.model.attributes )); 33 | this.$el.find('[name="email"]').focus(); 34 | }, 35 | preventSubmit: function(event) { 36 | event.preventDefault(); 37 | }, 38 | signupOnEnter: function(event) { 39 | if (event.keyCode !== 13) { return; } 40 | event.preventDefault(); 41 | this.signup(); 42 | }, 43 | signup: function() { 44 | this.$el.find('.btn-signup').attr('disabled', true); 45 | 46 | this.model.save({ 47 | email: this.$el.find('[name="email"]').val() 48 | },{ 49 | success: function(model, response) { 50 | if (response.success) { 51 | location.href = '/account/'; 52 | } 53 | else { 54 | model.set(response); 55 | } 56 | } 57 | }); 58 | } 59 | }); 60 | 61 | $(document).ready(function() { 62 | app.signupView = new app.SignupView(); 63 | }); 64 | }()); 65 | -------------------------------------------------------------------------------- /section-6/schema/Account.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(app, mongoose) { 4 | var accountSchema = new mongoose.Schema({ 5 | user: { 6 | id: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, 7 | name: { type: String, default: '' } 8 | }, 9 | isVerified: { type: String, default: '' }, 10 | verificationToken: { type: String, default: '' }, 11 | name: { 12 | first: { type: String, default: '' }, 13 | middle: { type: String, default: '' }, 14 | last: { type: String, default: '' }, 15 | full: { type: String, default: '' } 16 | }, 17 | company: { type: String, default: '' }, 18 | phone: { type: String, default: '' }, 19 | zip: { type: String, default: '' }, 20 | status: { 21 | id: { type: String, ref: 'Status' }, 22 | name: { type: String, default: '' }, 23 | userCreated: { 24 | id: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, 25 | name: { type: String, default: '' }, 26 | time: { type: Date, default: Date.now } 27 | } 28 | }, 29 | statusLog: [mongoose.modelSchemas.StatusLog], 30 | notes: [mongoose.modelSchemas.Note], 31 | userCreated: { 32 | id: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, 33 | name: { type: String, default: '' }, 34 | time: { type: Date, default: Date.now } 35 | }, 36 | search: [String] 37 | }); 38 | accountSchema.plugin(require('./plugins/pagedFind')); 39 | accountSchema.index({ user: 1 }); 40 | accountSchema.index({ 'status.id': 1 }); 41 | accountSchema.index({ search: 1 }); 42 | accountSchema.set('autoIndex', (app.get('env') === 'development')); 43 | app.db.model('Account', accountSchema); 44 | }; 45 | -------------------------------------------------------------------------------- /section-6/schema/Admin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(app, mongoose) { 4 | var adminSchema = new mongoose.Schema({ 5 | user: { 6 | id: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, 7 | name: { type: String, default: '' } 8 | }, 9 | name: { 10 | full: { type: String, default: '' }, 11 | first: { type: String, default: '' }, 12 | middle: { type: String, default: '' }, 13 | last: { type: String, default: '' }, 14 | }, 15 | groups: [{ type: String, ref: 'AdminGroup' }], 16 | permissions: [{ 17 | name: String, 18 | permit: Boolean 19 | }], 20 | timeCreated: { type: Date, default: Date.now }, 21 | search: [String] 22 | }); 23 | adminSchema.methods.hasPermissionTo = function(something) { 24 | //check group permissions 25 | var groupHasPermission = false; 26 | for (var i = 0 ; i < this.groups.length ; i++) { 27 | for (var j = 0 ; j < this.groups[i].permissions.length ; j++) { 28 | if (this.groups[i].permissions[j].name === something) { 29 | if (this.groups[i].permissions[j].permit) { 30 | groupHasPermission = true; 31 | } 32 | } 33 | } 34 | } 35 | 36 | //check admin permissions 37 | for (var k = 0 ; k < this.permissions.length ; k++) { 38 | if (this.permissions[k].name === something) { 39 | if (this.permissions[k].permit) { 40 | return true; 41 | } 42 | 43 | return false; 44 | } 45 | } 46 | 47 | return groupHasPermission; 48 | }; 49 | adminSchema.methods.isMemberOf = function(group) { 50 | for (var i = 0 ; i < this.groups.length ; i++) { 51 | if (this.groups[i]._id === group) { 52 | return true; 53 | } 54 | } 55 | 56 | return false; 57 | }; 58 | adminSchema.plugin(require('./plugins/pagedFind')); 59 | adminSchema.index({ 'user.id': 1 }); 60 | adminSchema.index({ search: 1 }); 61 | adminSchema.set('autoIndex', (app.get('env') === 'development')); 62 | app.db.model('Admin', adminSchema); 63 | }; 64 | -------------------------------------------------------------------------------- /section-6/schema/AdminGroup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(app, mongoose) { 4 | var adminGroupSchema = new mongoose.Schema({ 5 | _id: { type: String }, 6 | name: { type: String, default: '' }, 7 | permissions: [{ name: String, permit: Boolean }] 8 | }); 9 | adminGroupSchema.plugin(require('./plugins/pagedFind')); 10 | adminGroupSchema.index({ name: 1 }, { unique: true }); 11 | adminGroupSchema.set('autoIndex', (app.get('env') === 'development')); 12 | app.db.model('AdminGroup', adminGroupSchema); 13 | }; 14 | -------------------------------------------------------------------------------- /section-6/schema/Category.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(app, mongoose) { 4 | var categorySchema = new mongoose.Schema({ 5 | _id: { type: String }, 6 | pivot: { type: String, default: '' }, 7 | name: { type: String, default: '' } 8 | }); 9 | categorySchema.plugin(require('./plugins/pagedFind')); 10 | categorySchema.index({ pivot: 1 }); 11 | categorySchema.index({ name: 1 }); 12 | categorySchema.set('autoIndex', (app.get('env') === 'development')); 13 | app.db.model('Category', categorySchema); 14 | }; 15 | -------------------------------------------------------------------------------- /section-6/schema/LoginAttempt.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(app, mongoose) { 4 | var attemptSchema = new mongoose.Schema({ 5 | ip: { type: String, default: '' }, 6 | user: { type: String, default: '' }, 7 | time: { type: Date, default: Date.now, expires: app.config.loginAttempts.logExpiration } 8 | }); 9 | attemptSchema.index({ ip: 1 }); 10 | attemptSchema.index({ user: 1 }); 11 | attemptSchema.set('autoIndex', (app.get('env') === 'development')); 12 | app.db.model('LoginAttempt', attemptSchema); 13 | }; 14 | -------------------------------------------------------------------------------- /section-6/schema/Note.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(app, mongoose) { 4 | var noteSchema = new mongoose.Schema({ 5 | data: { type: String, default: '' }, 6 | userCreated: { 7 | id: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, 8 | name: { type: String, default: '' }, 9 | time: { type: Date, default: Date.now } 10 | } 11 | }); 12 | app.db.model('Note', noteSchema); 13 | }; 14 | -------------------------------------------------------------------------------- /section-6/schema/Status.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(app, mongoose) { 4 | var statusSchema = new mongoose.Schema({ 5 | _id: { type: String }, 6 | pivot: { type: String, default: '' }, 7 | name: { type: String, default: '' } 8 | }); 9 | statusSchema.plugin(require('./plugins/pagedFind')); 10 | statusSchema.index({ pivot: 1 }); 11 | statusSchema.index({ name: 1 }); 12 | statusSchema.set('autoIndex', (app.get('env') === 'development')); 13 | app.db.model('Status', statusSchema); 14 | }; 15 | -------------------------------------------------------------------------------- /section-6/schema/StatusLog.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(app, mongoose) { 4 | var statusLogSchema = new mongoose.Schema({ 5 | id: { type: String, ref: 'Status' }, 6 | name: { type: String, default: '' }, 7 | userCreated: { 8 | id: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, 9 | name: { type: String, default: '' }, 10 | time: { type: Date, default: Date.now } 11 | } 12 | }); 13 | app.db.model('StatusLog', statusLogSchema); 14 | }; 15 | -------------------------------------------------------------------------------- /section-6/schema/User.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(app, mongoose) { 4 | var userSchema = new mongoose.Schema({ 5 | username: { type: String, unique: true }, 6 | password: String, 7 | email: { type: String, unique: true }, 8 | roles: { 9 | admin: { type: mongoose.Schema.Types.ObjectId, ref: 'Admin' }, 10 | account: { type: mongoose.Schema.Types.ObjectId, ref: 'Account' } 11 | }, 12 | isActive: String, 13 | timeCreated: { type: Date, default: Date.now }, 14 | resetPasswordToken: String, 15 | resetPasswordExpires: Date, 16 | twitter: {}, 17 | github: {}, 18 | facebook: {}, 19 | google: {}, 20 | tumblr: {}, 21 | search: [String] 22 | }); 23 | userSchema.methods.canPlayRoleOf = function(role) { 24 | if (role === "admin" && this.roles.admin) { 25 | return true; 26 | } 27 | 28 | if (role === "account" && this.roles.account) { 29 | return true; 30 | } 31 | 32 | return false; 33 | }; 34 | userSchema.methods.defaultReturnUrl = function() { 35 | var returnUrl = '/'; 36 | if (this.canPlayRoleOf('account')) { 37 | returnUrl = '/account/'; 38 | } 39 | 40 | if (this.canPlayRoleOf('admin')) { 41 | returnUrl = '/admin/'; 42 | } 43 | 44 | return returnUrl; 45 | }; 46 | userSchema.statics.encryptPassword = function(password, done) { 47 | var bcrypt = require('bcrypt'); 48 | bcrypt.genSalt(10, function(err, salt) { 49 | if (err) { 50 | return done(err); 51 | } 52 | 53 | bcrypt.hash(password, salt, function(err, hash) { 54 | done(err, hash); 55 | }); 56 | }); 57 | }; 58 | userSchema.statics.validatePassword = function(password, hash, done) { 59 | var bcrypt = require('bcrypt'); 60 | bcrypt.compare(password, hash, function(err, res) { 61 | done(err, res); 62 | }); 63 | }; 64 | userSchema.plugin(require('./plugins/pagedFind')); 65 | userSchema.index({ username: 1 }, { unique: true }); 66 | userSchema.index({ email: 1 }, { unique: true }); 67 | userSchema.index({ timeCreated: 1 }); 68 | userSchema.index({ 'twitter.id': 1 }); 69 | userSchema.index({ 'github.id': 1 }); 70 | userSchema.index({ 'facebook.id': 1 }); 71 | userSchema.index({ 'google.id': 1 }); 72 | userSchema.index({ search: 1 }); 73 | userSchema.set('autoIndex', (app.get('env') === 'development')); 74 | app.db.model('User', userSchema); 75 | }; 76 | -------------------------------------------------------------------------------- /section-6/schema/plugins/pagedFind.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function pagedFindPlugin (schema) { 4 | schema.statics.pagedFind = function(options, cb) { 5 | var thisSchema = this; 6 | 7 | if (!options.filters) { 8 | options.filters = {}; 9 | } 10 | 11 | if (!options.keys) { 12 | options.keys = ''; 13 | } 14 | 15 | if (!options.limit) { 16 | options.limit = 20; 17 | } 18 | 19 | if (!options.page) { 20 | options.page = 1; 21 | } 22 | 23 | if (!options.sort) { 24 | options.sort = {}; 25 | } 26 | 27 | var output = { 28 | data: null, 29 | pages: { 30 | current: options.page, 31 | prev: 0, 32 | hasPrev: false, 33 | next: 0, 34 | hasNext: false, 35 | total: 0 36 | }, 37 | items: { 38 | begin: ((options.page * options.limit) - options.limit) + 1, 39 | end: options.page * options.limit, 40 | total: 0 41 | } 42 | }; 43 | 44 | var countResults = function(callback) { 45 | thisSchema.count(options.filters, function(err, count) { 46 | output.items.total = count; 47 | callback(null, 'done counting'); 48 | }); 49 | }; 50 | 51 | var getResults = function(callback) { 52 | var query = thisSchema.find(options.filters, options.keys); 53 | query.skip((options.page - 1) * options.limit); 54 | query.limit(options.limit); 55 | query.sort(options.sort); 56 | query.exec(function(err, results) { 57 | output.data = results; 58 | callback(null, 'done getting records'); 59 | }); 60 | }; 61 | 62 | require('async').parallel([ 63 | countResults, 64 | getResults 65 | ], 66 | function(err, results){ 67 | if (err) { 68 | cb(err, null); 69 | } 70 | 71 | //final paging math 72 | output.pages.total = Math.ceil(output.items.total / options.limit); 73 | output.pages.next = ((output.pages.current + 1) > output.pages.total ? 0 : output.pages.current + 1); 74 | output.pages.hasNext = (output.pages.next !== 0); 75 | output.pages.prev = output.pages.current - 1; 76 | output.pages.hasPrev = (output.pages.prev !== 0); 77 | if (output.items.end > output.items.total) { 78 | output.items.end = output.items.total; 79 | } 80 | 81 | cb(null, output); 82 | }); 83 | }; 84 | }; 85 | -------------------------------------------------------------------------------- /section-6/util/sendmail/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(req, res, options) { 4 | /* options = { 5 | from: String, 6 | to: String, 7 | cc: String, 8 | bcc: String, 9 | text: String, 10 | textPath String, 11 | html: String, 12 | htmlPath: String, 13 | attachments: [String], 14 | success: Function, 15 | error: Function 16 | } */ 17 | 18 | var renderText = function(callback) { 19 | res.render(options.textPath, options.locals, function(err, text) { 20 | if (err) { 21 | callback(err, null); 22 | } 23 | else { 24 | options.text = text; 25 | return callback(null, 'done'); 26 | } 27 | }); 28 | }; 29 | 30 | var renderHtml = function(callback) { 31 | res.render(options.htmlPath, options.locals, function(err, html) { 32 | if (err) { 33 | callback(err, null); 34 | } 35 | else { 36 | options.html = html; 37 | return callback(null, 'done'); 38 | } 39 | }); 40 | }; 41 | 42 | var renderers = []; 43 | if (options.textPath) { 44 | renderers.push(renderText); 45 | } 46 | 47 | if (options.htmlPath) { 48 | renderers.push(renderHtml); 49 | } 50 | 51 | require('async').parallel( 52 | renderers, 53 | function(err, results){ 54 | if (err) { 55 | options.error('Email template render failed. '+ err); 56 | return; 57 | } 58 | 59 | var attachments = []; 60 | 61 | if (options.html) { 62 | attachments.push({ data: options.html, alternative: true }); 63 | } 64 | 65 | if (options.attachments) { 66 | for (var i = 0 ; i < options.attachments.length ; i++) { 67 | attachments.push(options.attachments[i]); 68 | } 69 | } 70 | 71 | var emailjs = require('emailjs/email'); 72 | var emailer = emailjs.server.connect( req.app.config.smtp.credentials ); 73 | emailer.send({ 74 | from: options.from, 75 | to: options.to, 76 | 'reply-to': options.replyTo || options.from, 77 | cc: options.cc, 78 | bcc: options.bcc, 79 | subject: options.subject, 80 | text: options.text, 81 | attachment: attachments 82 | }, function(err, message) { 83 | if (err) { 84 | options.error('Email failed to send. '+ err); 85 | return; 86 | } 87 | else { 88 | options.success(message); 89 | return; 90 | } 91 | }); 92 | } 93 | ); 94 | }; 95 | -------------------------------------------------------------------------------- /section-6/util/slugify/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(text) { 4 | return text.toLowerCase().replace(/[^\w ]+/g, '').replace(/ +/g, '-'); 5 | }; 6 | -------------------------------------------------------------------------------- /section-6/util/workflow/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function(req, res) { 4 | var workflow = new (require('events').EventEmitter)(); 5 | 6 | workflow.outcome = { 7 | success: false, 8 | errors: [], 9 | errfor: {} 10 | }; 11 | 12 | workflow.hasErrors = function() { 13 | return Object.keys(workflow.outcome.errfor).length !== 0 || workflow.outcome.errors.length !== 0; 14 | }; 15 | 16 | workflow.on('exception', function(err) { 17 | workflow.outcome.errors.push('Exception: '+ err); 18 | return workflow.emit('response'); 19 | }); 20 | 21 | workflow.on('response', function() { 22 | workflow.outcome.success = !workflow.hasErrors(); 23 | res.send(workflow.outcome); 24 | }); 25 | 26 | return workflow; 27 | }; 28 | -------------------------------------------------------------------------------- /section-6/views/about/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.init = function(req, res){ 4 | res.render('about/index'); 5 | }; 6 | -------------------------------------------------------------------------------- /section-6/views/account/index.jade: -------------------------------------------------------------------------------- 1 | extends ../../layouts/account 2 | 3 | block head 4 | title Account Area 5 | 6 | block neck 7 | link(rel='stylesheet', href='/views/account/index.min.css?#{cacheBreaker}') 8 | 9 | block feet 10 | script(src='/views/account/index.min.js?#{cacheBreaker}') 11 | 12 | block body 13 | div.row 14 | div.col-sm-6 15 | div.page-header 16 | h1 My Account 17 | div.row 18 | div.col-sm-4 19 | div.well.stat 20 | div.stat-value.day-of-year -- 21 | div.stat-label Day of Year 22 | div.col-sm-4 23 | div.well.stat 24 | div.stat-value.day-of-month -- 25 | div.stat-label Day of Month 26 | div.col-sm-4 27 | div.well.stat 28 | div.stat-value.week-of-year -- 29 | div.stat-label Week of Year 30 | div.row 31 | div.col-sm-4 32 | div.well.stat 33 | div.stat-value.day-of-week -- 34 | div.stat-label Day of Week 35 | div.col-sm-4 36 | div.well.stat 37 | div.stat-value.week-year -- 38 | div.stat-label Week Year 39 | div.col-sm-4 40 | div.well.stat 41 | div.stat-value.hour-of-day -- 42 | div.stat-label Hour of Day 43 | div.col-sm-6.special 44 | div.page-header 45 | h1 Go Faster Everyday 46 | i.fa.fa-dashboard.super-awesome 47 | -------------------------------------------------------------------------------- /section-6/views/account/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.init = function(req, res){ 4 | res.render('account/index'); 5 | }; 6 | -------------------------------------------------------------------------------- /section-6/views/account/verification/email-html.jade: -------------------------------------------------------------------------------- 1 | h3 #{projectName} Email Verification 2 | p Your #{projectName} account is nearly ready. Please visit the link below to confirm your email address. 3 | a(href='#{verifyURL}') #{verifyURL} 4 | p Note: If you did not sign for a #{projectName} account, don't worry. You can ignore this email. 5 | p 6 | | Thanks, 7 | br 8 | | #{projectName} 9 | -------------------------------------------------------------------------------- /section-6/views/account/verification/email-text.jade: -------------------------------------------------------------------------------- 1 | | #{projectName} Email Verification 2 | = '\n' 3 | = '\n' 4 | | Your #{projectName} account is nearly ready. Please visit the link below to confirm your email address. 5 | = '\n' 6 | = '\n' 7 | | #{verifyURL} 8 | = '\n' 9 | = '\n' 10 | | Note: If you did not sign for a #{projectName} account, don't worry. You can ignore this email. 11 | = '\n' 12 | = '\n' 13 | | Thanks, 14 | | #{projectName} 15 | -------------------------------------------------------------------------------- /section-6/views/account/verification/index.jade: -------------------------------------------------------------------------------- 1 | extends ../../../layouts/account 2 | 3 | block head 4 | title Verification Required 5 | 6 | block neck 7 | link(rel='stylesheet', href='/views/account/verification/index.min.css?#{cacheBreaker}') 8 | 9 | block feet 10 | script(src='/views/account/verification/index.min.js?#{cacheBreaker}') 11 | 12 | block body 13 | div.row 14 | div.col-sm-6 15 | div.page-header 16 | h1 Verification Required 17 | div.alert.alert-warning Your account is nearly ready. Check your inbox for next steps. 18 | div#verify 19 | div.col-sm-6.special 20 | div.page-header 21 | h1 You're Almost Done 22 | i.fa.fa-key.super-awesome 23 | 24 | script(type='text/template', id='tmpl-verify') 25 | form 26 | div.alerts 27 | |<% _.each(errors, function(err) { %> 28 | div.alert.alert-danger.alert-dismissable 29 | button.close(type='button', data-dismiss='alert') × 30 | |<%- err %> 31 | |<% }); %> 32 | |<% if (success) { %> 33 | div.alert.alert-info.alert-dismissable 34 | button.close(type='button', data-dismiss='alert') × 35 | | Verification email successfully re-sent. 36 | |<% } %> 37 | |<% if (!success) { %> 38 | div(class!='not-received<%= !keepFormOpen ? "" : " not-received-hidden" %>') 39 | a.btn.btn-link.btn-resend I checked my email and spam folder, nothing yet. 40 | div(class!='verify-form<%= keepFormOpen ? "" : " verify-form-hidden" %>') 41 | div.form-group(class!='<%- errfor.email ? "has-error" : "" %>') 42 | label Your Email: 43 | input.form-control(type='text', name='email', value!='<%= email %>') 44 | span.help-block <%- errfor.email %> 45 | div.form-group 46 | button.btn.btn-primary.btn-verify(type='button') Re-Send Verification 47 | |<% } %> 48 | 49 | script(type='text/template', id='data-user') !{data.user} 50 | -------------------------------------------------------------------------------- /section-6/views/admin/admin-groups/index.jade: -------------------------------------------------------------------------------- 1 | extends ../../../layouts/admin 2 | 3 | block head 4 | title Manage Admin Groups 5 | 6 | block neck 7 | link(rel='stylesheet', href='/views/admin/admin-groups/index.min.css?#{cacheBreaker}') 8 | 9 | block feet 10 | script(src='/views/admin/admin-groups/index.min.js?#{cacheBreaker}') 11 | 12 | block body 13 | div.row 14 | div.col-xs-12 15 | div#header 16 | div#filters 17 | div#results-table 18 | div#results-paging 19 | 20 | script(type='text/template', id='tmpl-header') 21 | div.page-header 22 | form.form-inline.pull-right 23 | div.input-group 24 | input.form-control(name='name', type='text', placeholder='enter a name', value!='<%= name %>') 25 | button.btn.btn-primary.btn-add(type='button') Add New 26 | h1 Admin Groups 27 | 28 | script(type='text/template', id='tmpl-filters') 29 | form.filters 30 | div.row 31 | div.col-sm-3 32 | label Name Search 33 | input.form-control(name='name', type='text') 34 | div.col-sm-3 35 | label Sort By 36 | select.form-control(name='sort') 37 | option(value='_id') id ▲ 38 | option(value='-_id') id ▼ 39 | option(value='name') name ▲ 40 | option(value='-name') name ▼ 41 | div.col-sm-3 42 | label Limit 43 | select.form-control(name='limit') 44 | option(value='10') 10 items 45 | option(value='20', selected='selected') 20 items 46 | option(value='50') 50 items 47 | option(value='100') 100 items 48 | 49 | script(type='text/template', id='tmpl-results-table') 50 | table.table.table-striped 51 | thead 52 | tr 53 | th 54 | th.stretch name 55 | th id 56 | tbody#results-rows 57 | 58 | script(type='text/template', id='tmpl-results-row') 59 | td 60 | input.btn.btn-default.btn-sm.btn-details(type='button', value='Edit') 61 | td <%- name %> 62 | td.nowrap <%= _id %> 63 | 64 | script(type='text/template', id='tmpl-results-empty-row') 65 | tr 66 | td(colspan='3') no documents matched 67 | 68 | script(type='text/template', id='tmpl-results-paging') 69 | div.well 70 | div.btn-group.pull-left 71 | button.btn.btn-default(disabled=true) Page <%= pages.current %> of <%= pages.total %> 72 | button.btn.btn-default(disabled=true) Rows <%= items.begin %> - <%= items.end %> of <%= items.total %> 73 | div.btn-group.pull-right 74 | button.btn.btn-default.btn-page.btn-prev(data-page!='<%= pages.prev %>') Prev 75 | button.btn.btn-default.btn-page.btn-next(data-page!='<%= pages.next %>') Next 76 | div.clearfix 77 | 78 | script(type='text/template', id='data-results') !{data.results} 79 | -------------------------------------------------------------------------------- /section-6/views/admin/administrators/index.jade: -------------------------------------------------------------------------------- 1 | extends ../../../layouts/admin 2 | 3 | block head 4 | title Manage Administrators 5 | 6 | block neck 7 | link(rel='stylesheet', href='/views/admin/administrators/index.min.css?#{cacheBreaker}') 8 | 9 | block feet 10 | script(src='/views/admin/administrators/index.min.js?#{cacheBreaker}') 11 | 12 | block body 13 | div.row 14 | div.col-xs-12 15 | div#header 16 | div#filters 17 | div#results-table 18 | div#results-paging 19 | 20 | script(type='text/template', id='tmpl-header') 21 | div.page-header 22 | form.form-inline.pull-right 23 | div.input-group 24 | input.form-control(name='name', type='text', placeholder='enter a name', value!='<%= name.full %>') 25 | button.btn.btn-primary.btn-add(type='button') Add New 26 | h1 Administrators 27 | 28 | script(type='text/template', id='tmpl-filters') 29 | form.filters 30 | div.row 31 | div.col-sm-3 32 | label Name Search 33 | input.form-control(name='search', type='text') 34 | div.col-sm-3 35 | label Sort By 36 | select.form-control(name='sort') 37 | option(value='_id') id ▲ 38 | option(value='-_id') id ▼ 39 | option(value='name') name ▲ 40 | option(value='-name') name ▼ 41 | div.col-sm-3 42 | label Limit 43 | select.form-control(name='limit') 44 | option(value='10') 10 items 45 | option(value='20', selected='selected') 20 items 46 | option(value='50') 50 items 47 | option(value='100') 100 items 48 | 49 | script(type='text/template', id='tmpl-results-table') 50 | table.table.table-striped 51 | thead 52 | tr 53 | th 54 | th.stretch name 55 | th id 56 | tbody#results-rows 57 | 58 | script(type='text/template', id='tmpl-results-row') 59 | td 60 | input.btn.btn-default.btn-sm.btn-details(type='button', value='Edit') 61 | td.nowrap <%- name.full %> 62 | td <%= _id %> 63 | 64 | script(type='text/template', id='tmpl-results-empty-row') 65 | tr 66 | td(colspan='3') no documents matched 67 | 68 | script(type='text/template', id='tmpl-results-paging') 69 | div.well 70 | div.btn-group.pull-left 71 | button.btn.btn-default(disabled=true) Page <%= pages.current %> of <%= pages.total %> 72 | button.btn.btn-default(disabled=true) Rows <%= items.begin %> - <%= items.end %> of <%= items.total %> 73 | div.btn-group.pull-right 74 | button.btn.btn-default.btn-page.btn-prev(data-page!='<%= pages.prev %>') Prev 75 | button.btn.btn-default.btn-page.btn-next(data-page!='<%= pages.next %>') Next 76 | div.clearfix 77 | 78 | script(type='text/template', id='data-results') !{data.results} 79 | -------------------------------------------------------------------------------- /section-6/views/admin/categories/details.jade: -------------------------------------------------------------------------------- 1 | extends ../../../layouts/admin 2 | 3 | block head 4 | title Categories / Details 5 | 6 | block feet 7 | script(src='/views/admin/categories/details.min.js?#{cacheBreaker}') 8 | 9 | block body 10 | div.row 11 | div.col-xs-12 12 | div#header 13 | div#details 14 | div#delete 15 | 16 | script(type='text/template', id='tmpl-header') 17 | div.page-header 18 | h1 19 | a(href='/admin/categories/') Categories 20 | | / <%- name %> 21 | 22 | script(type='text/template', id='tmpl-details') 23 | fieldset 24 | legend Details 25 | div.alerts 26 | |<% _.each(errors, function(err) { %> 27 | div.alert.alert-danger.alert-dismissable 28 | button.close(type='button', data-dismiss='alert') × 29 | |<%- err %> 30 | |<% }); %> 31 | |<% if (success) { %> 32 | div.alert.alert-info.alert-dismissable 33 | button.close(type='button', data-dismiss='alert') × 34 | | Changes have been saved. 35 | |<% } %> 36 | div.form-group(class!='<%- errfor.pivot ? "has-error" : "" %>') 37 | label Pivot: 38 | input.form-control(type='text', name='pivot', value!='<%- pivot %>') 39 | span.help-block <%- errfor.pivot %> 40 | div.form-group(class!='<%- errfor.name ? "has-error" : "" %>') 41 | label Name: 42 | input.form-control(type='text', name='name', value!='<%- name %>') 43 | span.help-block <%- errfor.name %> 44 | div.form-group 45 | button.btn.btn-primary.btn-update(type='button') Update 46 | 47 | script(type='text/template', id='tmpl-delete') 48 | fieldset 49 | legend Danger Zone 50 | div.alerts 51 | |<% _.each(errors, function(err) { %> 52 | div.alert.alert-danger.alert-dismissable 53 | button.close(type='button', data-dismiss='alert') × 54 | |<%- err %> 55 | |<% }); %> 56 | div.form-group 57 | span.help-block 58 | span.label.label-danger If you do this, it cannot be undone. 59 | |  60 | span.text-muted You may also create orphaned document relationships too. 61 | div.form-group 62 | button.btn.btn-danger.btn-delete(type='button') Delete 63 | 64 | script(type='text/template', id='data-record') !{data.record} 65 | -------------------------------------------------------------------------------- /section-6/views/admin/index.jade: -------------------------------------------------------------------------------- 1 | extends ../../layouts/admin 2 | 3 | block head 4 | title Admin Area 5 | 6 | block neck 7 | link(rel='stylesheet', href='/views/admin/index.min.css?#{cacheBreaker}') 8 | 9 | block body 10 | div.row 11 | div.col-sm-6 12 | div.page-header 13 | h1 Admin Area 14 | div.row 15 | div.col-sm-4 16 | div.well.stat 17 | div.stat-value #{countUser} 18 | div.stat-label Users 19 | div.col-sm-4 20 | div.well.stat 21 | div.stat-value #{countAccount} 22 | div.stat-label Accounts 23 | div.col-sm-4 24 | div.well.stat 25 | div.stat-value #{countAdmin} 26 | div.stat-label Admins 27 | div.row 28 | div.col-sm-4 29 | div.well.stat 30 | div.stat-value #{countAdminGroup} 31 | div.stat-label Groups 32 | div.col-sm-4 33 | div.well.stat 34 | div.stat-value #{countCategory} 35 | div.stat-label Categories 36 | div.col-sm-4 37 | div.well.stat 38 | div.stat-value #{countStatus} 39 | div.stat-label Statuses 40 | div.col-sm-6.special 41 | div.page-header 42 | h1 Super Dashboard 43 | i.fa.fa-gears.super-awesome 44 | -------------------------------------------------------------------------------- /section-6/views/admin/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.init = function(req, res, next){ 4 | var sigma = {}; 5 | var collections = ['User', 'Account', 'Admin', 'AdminGroup', 'Category', 'Status']; 6 | var queries = []; 7 | 8 | collections.forEach(function(el, i, arr) { 9 | queries.push(function(done) { 10 | req.app.db.models[el].count({}, function(err, count) { 11 | if (err) { 12 | return done(err, null); 13 | } 14 | 15 | sigma['count'+ el] = count; 16 | done(null, el); 17 | }); 18 | }); 19 | }); 20 | 21 | var asyncFinally = function(err, results) { 22 | if (err) { 23 | return next(err); 24 | } 25 | 26 | res.render('admin/index', sigma); 27 | }; 28 | 29 | require('async').parallel(queries, asyncFinally); 30 | }; 31 | -------------------------------------------------------------------------------- /section-6/views/admin/search/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.find = function(req, res, next){ 4 | req.query.q = req.query.q ? req.query.q : ''; 5 | var regexQuery = new RegExp('^.*?'+ req.query.q +'.*$', 'i'); 6 | var outcome = {}; 7 | 8 | var searchUsers = function(done) { 9 | req.app.db.models.User.find({search: regexQuery}, 'username').sort('username').limit(10).lean().exec(function(err, results) { 10 | if (err) { 11 | return done(err, null); 12 | } 13 | 14 | outcome.users = results; 15 | done(null, 'searchUsers'); 16 | }); 17 | }; 18 | 19 | var searchAccounts = function(done) { 20 | req.app.db.models.Account.find({search: regexQuery}, 'name.full').sort('name.full').limit(10).lean().exec(function(err, results) { 21 | if (err) { 22 | return done(err, null); 23 | } 24 | 25 | outcome.accounts = results; 26 | return done(null, 'searchAccounts'); 27 | }); 28 | }; 29 | 30 | var searchAdministrators = function(done) { 31 | req.app.db.models.Admin.find({search: regexQuery}, 'name.full').sort('name.full').limit(10).lean().exec(function(err, results) { 32 | if (err) { 33 | return done(err, null); 34 | } 35 | 36 | outcome.administrators = results; 37 | return done(null, 'searchAdministrators'); 38 | }); 39 | }; 40 | 41 | var asyncFinally = function(err, results) { 42 | if (err) { 43 | return next(err, null); 44 | } 45 | 46 | res.send(outcome); 47 | }; 48 | 49 | require('async').parallel([searchUsers, searchAccounts, searchAdministrators], asyncFinally); 50 | }; 51 | -------------------------------------------------------------------------------- /section-6/views/admin/statuses/details.jade: -------------------------------------------------------------------------------- 1 | extends ../../../layouts/admin 2 | 3 | block head 4 | title Statuses / Details 5 | 6 | block feet 7 | script(src='/views/admin/statuses/details.min.js?#{cacheBreaker}') 8 | 9 | block body 10 | div.row 11 | div.col-xs-12 12 | div#header 13 | div#details 14 | div#delete 15 | 16 | script(type='text/template', id='tmpl-header') 17 | div.page-header 18 | h1 19 | a(href='/admin/statuses/') Statuses 20 | | / <%- name %> 21 | 22 | script(type='text/template', id='tmpl-details') 23 | fieldset 24 | legend Details 25 | div.alerts 26 | |<% _.each(errors, function(err) { %> 27 | div.alert.alert-danger.alert-dismissable 28 | button.close(type='button', data-dismiss='alert') × 29 | |<%- err %> 30 | |<% }); %> 31 | |<% if (success) { %> 32 | div.alert.alert-info.alert-dismissable 33 | button.close(type='button', data-dismiss='alert') × 34 | | Changes have been saved. 35 | |<% } %> 36 | div.form-group(class!='<%- errfor.pivot ? "has-error" : "" %>') 37 | label Pivot: 38 | input.form-control(type='text', name='pivot', value!='<%- pivot %>') 39 | span.help-block <%- errfor.pivot %> 40 | div.form-group(class!='<%- errfor.name ? "has-error" : "" %>') 41 | label Name: 42 | input.form-control(type='text', name='name', value!='<%- name %>') 43 | span.help-block <%- errfor.name %> 44 | div.form-group 45 | button.btn.btn-primary.btn-update(type='button') Update 46 | 47 | script(type='text/template', id='tmpl-delete') 48 | fieldset 49 | legend Danger Zone 50 | div.alerts 51 | |<% _.each(errors, function(err) { %> 52 | div.alert.alert-danger.alert-dismissable 53 | button.close(type='button', data-dismiss='alert') × 54 | |<%- err %> 55 | |<% }); %> 56 | div.form-group 57 | span.help-block 58 | span.label.label-danger If you do this, it cannot be undone. 59 | |  60 | span.text-muted You may also create orphaned document relationships too. 61 | div.form-group 62 | button.btn.btn-danger.btn-delete(type='button') Delete 63 | 64 | script(type='text/template', id='data-record') !{data.record} 65 | -------------------------------------------------------------------------------- /section-6/views/contact/email-html.jade: -------------------------------------------------------------------------------- 1 | h3 #{projectName} Contact Form 2 | table(border='1', cellpadding='5', cellspacing='0') 3 | tr 4 | td Name: 5 | td #{name} 6 | tr 7 | td Email: 8 | td #{email} 9 | tr 10 | td Message: 11 | td #{message} 12 | -------------------------------------------------------------------------------- /section-6/views/contact/email-text.jade: -------------------------------------------------------------------------------- 1 | | #{projectName} Contact Form 2 | = '\n' 3 | = '\n' 4 | | Name: #{name} 5 | | Email: #{email} 6 | | Message: 7 | | #{message} 8 | -------------------------------------------------------------------------------- /section-6/views/contact/index.jade: -------------------------------------------------------------------------------- 1 | extends ../../layouts/default 2 | 3 | block head 4 | title Contact Us 5 | 6 | block neck 7 | link(rel='stylesheet', href='/views/contact/index.min.css?#{cacheBreaker}') 8 | 9 | block feet 10 | script(src='/views/contact/index.min.js?#{cacheBreaker}') 11 | 12 | block body 13 | div.row 14 | div.col-sm-6 15 | div.page-header 16 | h1 Send A Message 17 | div#contact 18 | div.col-sm-6.special 19 | div.page-header 20 | h1 Contact Us 21 | p.lead Freddy can't wait to hear from you. 22 | i.fa.fa-reply-all.super-awesome 23 | address 1428 Elm Street • San Francisco, CA 94122 24 | 25 | script(type='text/template', id='tmpl-contact') 26 | form 27 | div.alerts 28 | |<% _.each(errors, function(err) { %> 29 | div.alert.alert-danger.alert-dismissable 30 | button.close(type='button', data-dismiss='alert') × 31 | |<%- err %> 32 | |<% }); %> 33 | |<% if (success) { %> 34 | div.alert.alert-info.alert-dismissable 35 | button.close(type='button', data-dismiss='alert') × 36 | | We have received your message. Thank you. 37 | |<% } %> 38 | |<% if (!success) { %> 39 | div.form-group(class!='<%- errfor.name ? "has-error" : "" %>') 40 | label Your Name: 41 | input.form-control(type='text', name='name', value!='<%= name %>') 42 | span.help-block <%- errfor.name %> 43 | div.form-group(class!='<%- errfor.email ? "has-error" : "" %>') 44 | label Your Email: 45 | input.form-control(type='text', name='email', value!='<%= email %>') 46 | span.help-block <%- errfor.email %> 47 | div.form-group(class!='<%- errfor.message ? "has-error" : "" %>') 48 | label Message: 49 | textarea.form-control(name='message', rows='5') <%= message %> 50 | span.help-block <%- errfor.message %> 51 | div.form-group 52 | button.btn.btn-primary.btn-contact(type='button') Send Message 53 | |<% } %> 54 | -------------------------------------------------------------------------------- /section-6/views/contact/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.init = function(req, res){ 4 | res.render('contact/index'); 5 | }; 6 | 7 | exports.sendMessage = function(req, res){ 8 | var workflow = req.app.utility.workflow(req, res); 9 | 10 | workflow.on('validate', function() { 11 | if (!req.body.name) { 12 | workflow.outcome.errfor.name = 'required'; 13 | } 14 | 15 | if (!req.body.email) { 16 | workflow.outcome.errfor.email = 'required'; 17 | } 18 | 19 | if (!req.body.message) { 20 | workflow.outcome.errfor.message = 'required'; 21 | } 22 | 23 | if (workflow.hasErrors()) { 24 | return workflow.emit('response'); 25 | } 26 | 27 | workflow.emit('sendEmail'); 28 | }); 29 | 30 | workflow.on('sendEmail', function() { 31 | req.app.utility.sendmail(req, res, { 32 | from: req.app.config.smtp.from.name +' <'+ req.app.config.smtp.from.address +'>', 33 | replyTo: req.body.email, 34 | to: req.app.config.systemEmail, 35 | subject: req.app.config.projectName +' contact form', 36 | textPath: 'contact/email-text', 37 | htmlPath: 'contact/email-html', 38 | locals: { 39 | name: req.body.name, 40 | email: req.body.email, 41 | message: req.body.message, 42 | projectName: req.app.config.projectName 43 | }, 44 | success: function(message) { 45 | workflow.emit('response'); 46 | }, 47 | error: function(err) { 48 | workflow.outcome.errors.push('Error Sending: '+ err); 49 | workflow.emit('response'); 50 | } 51 | }); 52 | }); 53 | 54 | workflow.emit('validate'); 55 | }; 56 | -------------------------------------------------------------------------------- /section-6/views/http/404.jade: -------------------------------------------------------------------------------- 1 | extends ../../layouts/default 2 | 3 | block head 4 | title Page Not Found 5 | 6 | block body 7 | h1 Page Not Found 8 | p.lead 9 | | The resource you requested doesn't exist. 10 | -------------------------------------------------------------------------------- /section-6/views/http/500.jade: -------------------------------------------------------------------------------- 1 | extends ../../layouts/default 2 | 3 | block head 4 | title Server Error 5 | 6 | block body 7 | h1 Server Error 8 | p.lead 9 | | Sorry something went wrong. 10 | if err.stack 11 | pre #{err.stack} 12 | -------------------------------------------------------------------------------- /section-6/views/http/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.http404 = function(req, res){ 4 | res.status(404); 5 | if (req.xhr) { 6 | res.send({ error: 'Resource not found.' }); 7 | } 8 | else { 9 | res.render('http/404'); 10 | } 11 | }; 12 | 13 | exports.http500 = function(err, req, res, next){ 14 | res.status(500); 15 | 16 | var data = { err: {} }; 17 | if (req.app.get('env') === 'development') { 18 | data.err = err; 19 | console.log(err.stack); 20 | } 21 | 22 | if (req.xhr) { 23 | res.send({ error: 'Something went wrong.', details: data }); 24 | } 25 | else { 26 | res.render('http/500', data); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /section-6/views/index.jade: -------------------------------------------------------------------------------- 1 | extends ../layouts/default 2 | 3 | block head 4 | title Drywall is Running 5 | 6 | block neck 7 | link(rel='stylesheet', href='/views/index.min.css?#{cacheBreaker}') 8 | 9 | block body 10 | div.jumbotron 11 | h1 Success 12 | p.lead 13 | | Your Node.js website and user system is running. May the force be with you. 14 | div 15 | a.btn.btn-primary.btn-lg(href='/signup/') Create an Account 16 | |  or  17 | a.btn.btn-warning.btn-lg(href='/login/forgot/') Reset Your Password 18 | div.clearfix 19 | div.row 20 | div.col-sm-4 21 | div.panel.panel-default 22 | div.panel-body 23 | h3 About Us 24 | p At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti. 25 | a.btn.btn-default.btn-block(href='/about/') Learn More 26 | div.col-sm-4 27 | div.panel.panel-default 28 | div.panel-body 29 | h3 Sign Up 30 | p At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti. 31 | a.btn.btn-default.btn-block(href='/signup/') Learn More 32 | div.col-sm-4 33 | div.panel.panel-default 34 | div.panel-body 35 | h3 Contact Us 36 | p At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti. 37 | a.btn.btn-default.btn-block(href='/contact/') Learn More 38 | -------------------------------------------------------------------------------- /section-6/views/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.init = function(req, res){ 4 | res.render('index'); 5 | }; 6 | -------------------------------------------------------------------------------- /section-6/views/login/forgot/email-html.jade: -------------------------------------------------------------------------------- 1 | h3 Forgot your password? 2 | p 3 | | We received a request to reset the password for your account (#{username}). 4 | p 5 | | To reset your password, click on the link below (or copy and paste the URL into your browser): 6 | p 7 | a(href='#{resetLink}') #{resetLink} 8 | p 9 | | Thanks, 10 | br 11 | | #{projectName} 12 | -------------------------------------------------------------------------------- /section-6/views/login/forgot/email-text.jade: -------------------------------------------------------------------------------- 1 | | Forgot your password? 2 | = '\n' 3 | = '\n' 4 | | We received a request to reset the password for your account (#{username}). 5 | = '\n' 6 | = '\n' 7 | | To reset your password, click on the link below (or copy and paste the URL into your browser): 8 | | #{resetLink} 9 | = '\n' 10 | = '\n' 11 | | Thanks, 12 | | #{projectName} 13 | -------------------------------------------------------------------------------- /section-6/views/login/forgot/index.jade: -------------------------------------------------------------------------------- 1 | extends ../../../layouts/default 2 | 3 | block head 4 | title Forgot Your Password? 5 | 6 | block feet 7 | script(src='/views/login/forgot/index.min.js?#{cacheBreaker}') 8 | 9 | block body 10 | div.row 11 | div.col-sm-6 12 | div.page-header 13 | h1 Forgot Your Password? 14 | div#forgot 15 | 16 | script(type='text/template', id='tmpl-forgot') 17 | form 18 | div.alerts 19 | |<% _.each(errors, function(err) { %> 20 | div.alert.alert-danger 21 | button.close(type='button', data-dismiss='alert') × 22 | |<%- err %> 23 | |<% }); %> 24 | |<% if (success) { %> 25 | div.alert.alert-info 26 | button.close(type='button', data-dismiss='alert') × 27 | | If an account matched that address, an email will be sent with instructions. 28 | |<% } %> 29 | |<% if (!success) { %> 30 | div.form-group(class!='<%- errfor.email ? "has-error" : "" %>') 31 | label Enter Your Email: 32 | input.form-control(type='text', name='email', value!='<%= email %>') 33 | span.help-block <%- errfor.email %> 34 | |<% } %> 35 | div.form-group 36 | |<% if (!success) { %> 37 | button.btn.btn-primary.btn-forgot(type='button') Send Reset 38 | |<% } %> 39 | |  40 | a.btn.btn-link(href='/login/') Back to Login 41 | -------------------------------------------------------------------------------- /section-6/views/login/forgot/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.init = function(req, res){ 4 | if (req.isAuthenticated()) { 5 | res.redirect(req.user.defaultReturnUrl()); 6 | } 7 | else { 8 | res.render('login/forgot/index'); 9 | } 10 | }; 11 | 12 | exports.send = function(req, res, next){ 13 | var workflow = req.app.utility.workflow(req, res); 14 | 15 | workflow.on('validate', function() { 16 | if (!req.body.email) { 17 | workflow.outcome.errfor.email = 'required'; 18 | return workflow.emit('response'); 19 | } 20 | 21 | workflow.emit('generateToken'); 22 | }); 23 | 24 | workflow.on('generateToken', function() { 25 | var crypto = require('crypto'); 26 | crypto.randomBytes(21, function(err, buf) { 27 | if (err) { 28 | return next(err); 29 | } 30 | 31 | var token = buf.toString('hex'); 32 | req.app.db.models.User.encryptPassword(token, function(err, hash) { 33 | if (err) { 34 | return next(err); 35 | } 36 | 37 | workflow.emit('patchUser', token, hash); 38 | }); 39 | }); 40 | }); 41 | 42 | workflow.on('patchUser', function(token, hash) { 43 | var conditions = { email: req.body.email.toLowerCase() }; 44 | var fieldsToSet = { 45 | resetPasswordToken: hash, 46 | resetPasswordExpires: Date.now() + 10000000 47 | }; 48 | req.app.db.models.User.findOneAndUpdate(conditions, fieldsToSet, function(err, user) { 49 | if (err) { 50 | return workflow.emit('exception', err); 51 | } 52 | 53 | if (!user) { 54 | return workflow.emit('response'); 55 | } 56 | 57 | workflow.emit('sendEmail', token, user); 58 | }); 59 | }); 60 | 61 | workflow.on('sendEmail', function(token, user) { 62 | req.app.utility.sendmail(req, res, { 63 | from: req.app.config.smtp.from.name +' <'+ req.app.config.smtp.from.address +'>', 64 | to: user.email, 65 | subject: 'Reset your '+ req.app.config.projectName +' password', 66 | textPath: 'login/forgot/email-text', 67 | htmlPath: 'login/forgot/email-html', 68 | locals: { 69 | username: user.username, 70 | resetLink: req.protocol +'://'+ req.headers.host +'/login/reset/'+ user.email +'/'+ token +'/', 71 | projectName: req.app.config.projectName 72 | }, 73 | success: function(message) { 74 | workflow.emit('response'); 75 | }, 76 | error: function(err) { 77 | workflow.outcome.errors.push('Error Sending: '+ err); 78 | workflow.emit('response'); 79 | } 80 | }); 81 | }); 82 | 83 | workflow.emit('validate'); 84 | }; 85 | -------------------------------------------------------------------------------- /section-6/views/login/index.jade: -------------------------------------------------------------------------------- 1 | extends ../../layouts/default 2 | 3 | block head 4 | title Login 5 | 6 | block feet 7 | script(src='/views/login/index.min.js?#{cacheBreaker}') 8 | 9 | block body 10 | div.row 11 | div.col-sm-6 12 | div.page-header 13 | h1 Sign In 14 | div#login 15 | if oauthTwitter || oauthGitHub || oauthFacebook || oauthGoogle || oauthTumblr 16 | hr 17 | p Or sign in using... 18 | if oauthMessage 19 | div.alerts 20 | div.alert.alert-info.alert-dismissable 21 | button.close(type='button', data-dismiss='alert') × 22 | |#{oauthMessage}  23 | b 24 | a(href='/signup/') Sign Up Here 25 | div.form-actions 26 | div.btn-group.btn-group-justified 27 | if oauthTwitter 28 | a.btn.btn-info(href='/login/twitter/') 29 | i.fa.fa-twitter.fa-lg 30 | | Twitter 31 | if oauthGitHub 32 | a.btn.btn-info(href='/login/github/') 33 | i.fa.fa-github.fa-lg 34 | | GitHub 35 | if oauthFacebook 36 | a.btn.btn-info(href='/login/facebook/') 37 | i.fa.fa-facebook-square.fa-lg 38 | | Facebook 39 | if oauthGoogle 40 | a.btn.btn-info(href='/login/google/') 41 | i.fa.fa-google-plus-square.fa-lg 42 | | Google 43 | if oauthTumblr 44 | a.btn.btn-info(href='/login/tumblr/') 45 | i.fa.fa-tumblr-square.fa-lg 46 | | Tumblr 47 | 48 | script(type='text/template', id='tmpl-login') 49 | form 50 | div.alerts 51 | |<% _.each(errors, function(err) { %> 52 | div.alert.alert-danger.alert-dismissable 53 | button.close(type='button', data-dismiss='alert') × 54 | |<%- err %> 55 | |<% }); %> 56 | div.form-group(class!='<%- errfor.username ? "has-error" : "" %>') 57 | label Username or Email: 58 | input.form-control(type='text', name='username', value!='<%= username %>') 59 | span.help-block <%- errfor.username %> 60 | div.form-group(class!='<%- errfor.password ? "has-error" : "" %>') 61 | label Password: 62 | input.form-control(type='password', name='password', value!='<%= password %>') 63 | span.help-block <%- errfor.password %> 64 | div.form-actions 65 | button.btn.btn-primary.btn-login(type='button') Sign In 66 | |  67 | a.btn.btn-link(href='/login/forgot/') Forgot your password? 68 | -------------------------------------------------------------------------------- /section-6/views/login/reset/index.jade: -------------------------------------------------------------------------------- 1 | extends ../../../layouts/default 2 | 3 | block head 4 | title Reset Your Password 5 | 6 | block feet 7 | script(src='/views/login/reset/index.min.js?#{cacheBreaker}') 8 | 9 | block body 10 | div.row 11 | div.col-sm-6 12 | div.page-header 13 | h1 Reset Your Password 14 | div#reset 15 | 16 | script(type='text/template', id='tmpl-reset') 17 | form 18 | div.alerts 19 | |<% _.each(errors, function(err) { %> 20 | div.alert.alert-danger.alert-dismissable 21 | button.close(type='button', data-dismiss='alert') × 22 | |<%- err %> 23 | |<% }); %> 24 | |<% if (success) { %> 25 | div.alert.alert-info.alert-dismissable 26 | button.close(type='button', data-dismiss='alert') × 27 | | Your password has been reset. Please login to confirm. 28 | |<% } %> 29 | |<% if (id == undefined) { %> 30 | div.alert.alert-warning.alert-dismissable 31 | button.close(type='button', data-dismiss='alert') × 32 | | You do not have a valid reset request. 33 | |<% } %> 34 | |<% if (!success && id != undefined) { %> 35 | div.form-group(class!='<%- errfor.password ? "has-error" : "" %>') 36 | label New Password: 37 | input.form-control(type='password', name='password', value!='<%= password %>') 38 | span.help-block <%- errfor.password %> 39 | div.form-group(class!='<%- errfor.confirm ? "has-error" : "" %>') 40 | label Confirm Password: 41 | input.form-control(type='password', name='confirm', value!='<%= confirm %>') 42 | span.help-block <%- errfor.confirm %> 43 | |<% } %> 44 | div.form-group 45 | |<% if (!success && id != undefined) { %> 46 | button.btn.btn-primary.btn-reset(type='button') Set Password 47 | |<% } %> 48 | |  49 | a.btn.btn-link(href='/login/') Back to Login 50 | -------------------------------------------------------------------------------- /section-6/views/login/reset/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.init = function(req, res){ 4 | if (req.isAuthenticated()) { 5 | res.redirect(req.user.defaultReturnUrl()); 6 | } 7 | else { 8 | res.render('login/reset/index'); 9 | } 10 | }; 11 | 12 | exports.set = function(req, res){ 13 | var workflow = req.app.utility.workflow(req, res); 14 | 15 | workflow.on('validate', function() { 16 | if (!req.body.password) { 17 | workflow.outcome.errfor.password = 'required'; 18 | } 19 | 20 | if (!req.body.confirm) { 21 | workflow.outcome.errfor.confirm = 'required'; 22 | } 23 | 24 | if (req.body.password !== req.body.confirm) { 25 | workflow.outcome.errors.push('Passwords do not match.'); 26 | } 27 | 28 | if (workflow.hasErrors()) { 29 | return workflow.emit('response'); 30 | } 31 | 32 | workflow.emit('findUser'); 33 | }); 34 | 35 | workflow.on('findUser', function() { 36 | var conditions = { 37 | email: req.params.email, 38 | resetPasswordExpires: { $gt: Date.now() } 39 | }; 40 | req.app.db.models.User.findOne(conditions, function(err, user) { 41 | if (err) { 42 | return workflow.emit('exception', err); 43 | } 44 | 45 | if (!user) { 46 | workflow.outcome.errors.push('Invalid request.'); 47 | return workflow.emit('response'); 48 | } 49 | 50 | req.app.db.models.User.validatePassword(req.params.token, user.resetPasswordToken, function(err, isValid) { 51 | if (err) { 52 | return workflow.emit('exception', err); 53 | } 54 | 55 | if (!isValid) { 56 | workflow.outcome.errors.push('Invalid request.'); 57 | return workflow.emit('response'); 58 | } 59 | 60 | workflow.emit('patchUser', user); 61 | }); 62 | }); 63 | }); 64 | 65 | workflow.on('patchUser', function(user) { 66 | req.app.db.models.User.encryptPassword(req.body.password, function(err, hash) { 67 | if (err) { 68 | return workflow.emit('exception', err); 69 | } 70 | 71 | var fieldsToSet = { password: hash, resetPasswordToken: '' }; 72 | req.app.db.models.User.findByIdAndUpdate(user._id, fieldsToSet, function(err, user) { 73 | if (err) { 74 | return workflow.emit('exception', err); 75 | } 76 | 77 | workflow.emit('response'); 78 | }); 79 | }); 80 | }); 81 | 82 | workflow.emit('validate'); 83 | }; 84 | -------------------------------------------------------------------------------- /section-6/views/logout/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.init = function(req, res){ 4 | req.logout(); 5 | res.redirect('/'); 6 | }; 7 | -------------------------------------------------------------------------------- /section-6/views/signup/email-html.jade: -------------------------------------------------------------------------------- 1 | h3 Welcome to #{projectName} 2 | p Thanks for signing up. As requested, your account has been created. Here are your login credentials: 3 | table(border='1', cellpadding='5', cellspacing='0') 4 | tr 5 | td Username: 6 | td #{username} 7 | tr 8 | td Email: 9 | td #{email} 10 | tr 11 | td Login Here: 12 | td #{loginURL} 13 | p 14 | | Thanks, 15 | br 16 | | #{projectName} 17 | -------------------------------------------------------------------------------- /section-6/views/signup/email-text.jade: -------------------------------------------------------------------------------- 1 | | Welcome to #{projectName} 2 | = '\n' 3 | = '\n' 4 | | Thanks for signing up. As requested, your account has been created. Here are your login credentials: 5 | = '\n' 6 | = '\n' 7 | | Username: #{username} 8 | | Email: #{email} 9 | = '\n' 10 | = '\n' 11 | | Login Here: #{loginURL} 12 | = '\n' 13 | = '\n' 14 | | Thanks, 15 | | #{projectName} 16 | -------------------------------------------------------------------------------- /section-6/views/signup/social.jade: -------------------------------------------------------------------------------- 1 | extends ../../layouts/default 2 | 3 | block head 4 | title Sign Up 5 | 6 | block feet 7 | script(src='/views/signup/social.min.js?#{cacheBreaker}') 8 | 9 | block body 10 | div.row 11 | div.col-sm-6 12 | div.page-header 13 | h1 Complete Sign Up 14 | div#signup 15 | 16 | script(type='text/template', id='tmpl-signup') 17 | form 18 | div.alerts 19 | |<% _.each(errors, function(err) { %> 20 | div.alert.alert-danger.alert-dismissable 21 | button.close(type='button', data-dismiss='alert') × 22 | |<%- err %> 23 | |<% }); %> 24 | div.form-group(class!='<%- errfor.email ? "has-error" : "" %>') 25 | label Enter Your Email: 26 | input.form-control(type='text', name='email', value!='<%= email %>') 27 | span.help-block <%- errfor.email %> 28 | div.form-group 29 | button.btn.btn-primary.btn-signup(type='button') Create My Account 30 | 31 | script(type='text/template', id='data-email') !{email} 32 | --------------------------------------------------------------------------------