Implementing new client design using Backbone.js.
This commit is contained in:
parent
d0aab2d0c6
commit
e10ec26a21
Binary file not shown.
Binary file not shown.
@ -3,7 +3,7 @@
|
||||
|
||||
start() ->
|
||||
ok = application:load(mnesia),
|
||||
ok = application:set_env(mnesia, dir, "/home/jdbernard/projects/timestamper/web-app/db/test"),
|
||||
ok = application:set_env(mnesia, dir, "/home/jdbernard/projects/jdb-labs/timestamper/web-app/db/test"),
|
||||
ok = mnesia:start(),
|
||||
error_logger:info_report("TimeStampter app started."),
|
||||
ok.
|
||||
|
@ -2,261 +2,127 @@
|
||||
* author: Jonathan Bernard
|
||||
* TimeStamper main CSS for screen media types
|
||||
*/
|
||||
/*
|
||||
$obg: #D9CEB2;
|
||||
$bor: #948C75;
|
||||
$ibg: #D5DED9;
|
||||
$txt: #7A6A53;
|
||||
$bbg: #99B2B7;
|
||||
*/
|
||||
/*
|
||||
$obg: #979681;
|
||||
$obor: #E6DEC7;
|
||||
$ibg: #657A8B;
|
||||
$bbor: #B34C2B;
|
||||
$bbg: #252D42;
|
||||
$txt: #E6DEC7;
|
||||
*/
|
||||
/* _rounded.scss */
|
||||
html {
|
||||
background: url("/img/loving_blu.png") repeat; }
|
||||
* {
|
||||
color: #222222;
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
|
||||
body {
|
||||
width: 50%;
|
||||
background-color: #657a8b;
|
||||
margin: auto;
|
||||
padding: 1em;
|
||||
border: solid #b34c2b;
|
||||
border-top: 0;
|
||||
-moz-border-radius-bottomleft: 1em;
|
||||
-webkit-border-bottom-left-radius: 1em;
|
||||
border-bottom-left-radius: 1em;
|
||||
-moz-border-radius-bottomright: 1em;
|
||||
-webkit-border-bottom-right-radius: 1em;
|
||||
border-bottom-right-radius: 1em; }
|
||||
width: 70%;
|
||||
margin: auto; }
|
||||
|
||||
@media all and (min-device-width: 480) {
|
||||
body {
|
||||
width: 50%; } }
|
||||
|
||||
@media all and (max-device-width: 480) {
|
||||
body {
|
||||
width: 80%; } }
|
||||
|
||||
.control-links {
|
||||
color: #c5c5b9;
|
||||
float: right;
|
||||
display: block;
|
||||
height: 100%;
|
||||
font-weight: bold;
|
||||
font-size: smaller; }
|
||||
.control-links:hover {
|
||||
color: #252d42; }
|
||||
.control-links a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
margin-right: 0.5em; }
|
||||
.control-links a:hover {
|
||||
color: #b34c2b;
|
||||
text-decoration: underline; }
|
||||
|
||||
.bar {
|
||||
font-family: Helvetica, sans-serif;
|
||||
color: #252d42;
|
||||
background-color: #e6dec7;
|
||||
border-color: #979681;
|
||||
border-style: solid;
|
||||
border-width: 0.2em;
|
||||
border-bottom-width: 0;
|
||||
padding: 0.1em 1em 0.1em 1em;
|
||||
overflow: hidden; }
|
||||
|
||||
.last-bar {
|
||||
border-color: #979681;
|
||||
border-style: solid;
|
||||
border-width: 0.2em;
|
||||
-moz-border-radius-bottomright: 0.5em;
|
||||
-webkit-border-bottom-right-radius: 0.5em;
|
||||
border-bottom-right-radius: 0.5em;
|
||||
-moz-border-radius-bottomleft: 0.5em;
|
||||
-webkit-border-bottom-left-radius: 0.5em;
|
||||
border-bottom-left-radius: 0.5em;
|
||||
background-color: #e6dec7;
|
||||
padding: 0.1em 1em 0.1em 1em;
|
||||
overflow: hidden; }
|
||||
|
||||
#more-entries {
|
||||
overflow: hidden; }
|
||||
#more-entries div {
|
||||
float: right;
|
||||
left: -50%;
|
||||
position: relative; }
|
||||
#more-entries div a {
|
||||
position: relative;
|
||||
float: right;
|
||||
left: 50%;
|
||||
border: 1px solid #979681;
|
||||
padding: 0.1em;
|
||||
background: #f6f3ea;
|
||||
color: #626150;
|
||||
text-decoration: none;
|
||||
font-size: smaller; }
|
||||
#more-entries div a:hover {
|
||||
color: #b34c2b; }
|
||||
|
||||
.bar form {
|
||||
border-top: solid 1px #979681;
|
||||
padding: 0.5em 0 0.5em 2em;
|
||||
overflow: hidden; }
|
||||
.bar form label {
|
||||
color: #626150;
|
||||
display: block;
|
||||
overflow: hidden; }
|
||||
.bar form label span {
|
||||
float: left;
|
||||
width: 6em;
|
||||
padding-top: 0.1em; }
|
||||
.bar form input.text-input {
|
||||
border: 1px solid #979681; }
|
||||
.bar form .form-col {
|
||||
overflow: hidden;
|
||||
float: left;
|
||||
padding-right: 2em; }
|
||||
.bar form .form-col input.text-input {
|
||||
width: 10em; }
|
||||
.bar form div.form-submit {
|
||||
float: left;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
padding: 0.5em 2em 0.5em 2em;
|
||||
position: relative; }
|
||||
.bar form div.form-submit div {
|
||||
position: relative;
|
||||
float: right;
|
||||
left: -50%; }
|
||||
.bar form div.form-submit div input {
|
||||
position: relative;
|
||||
left: 50%; }
|
||||
.bar form div.form-submit input, .bar form input.form-submit {
|
||||
border: 1px solid #979681;
|
||||
background: #f6f3ea; }
|
||||
#top {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
background: white;
|
||||
width: 70%; }
|
||||
#top #fade-bar {
|
||||
background: url("img/fade.png") repeat-x;
|
||||
height: 32px;
|
||||
width: 100%; }
|
||||
|
||||
#user {
|
||||
-moz-border-radius-topleft: 0.5em;
|
||||
-webkit-border-top-left-radius: 0.5em;
|
||||
border-top-left-radius: 0.5em;
|
||||
-moz-border-radius-topright: 0.5em;
|
||||
-webkit-border-top-right-radius: 0.5em;
|
||||
border-top-right-radius: 0.5em; }
|
||||
#user .control-links {
|
||||
padding-top: 0.6em; }
|
||||
#user #fullname, #user #username {
|
||||
font-weight: bold;
|
||||
font-size: x-large;
|
||||
float: left; }
|
||||
#user #username {
|
||||
padding-left: 0.2em;
|
||||
color: #626150; }
|
||||
#user #change-pwd {
|
||||
font-family: "Josefin Sans";
|
||||
width: 100%; }
|
||||
#user .fullname, #user .username {
|
||||
display: inline-block;
|
||||
font-size: larger; }
|
||||
#user .fullname-input {
|
||||
display: none; }
|
||||
#user.fullname-edit .fullname-input {
|
||||
display: inline-block; }
|
||||
#user.fullname-edit .fullname {
|
||||
display: none; }
|
||||
#user ul#user-controls {
|
||||
float: right;
|
||||
list-style: none; }
|
||||
#user ul#user-controls li {
|
||||
float: right;
|
||||
text-align: center;
|
||||
padding-right: 1em; }
|
||||
#user a {
|
||||
display: inline-block;
|
||||
text-decoration: none; }
|
||||
#user a:hover {
|
||||
text-decoration: underline; }
|
||||
|
||||
#timeline {
|
||||
border-bottom: thin solid black;
|
||||
font-family: Arvo;
|
||||
font-size: 1.5em; }
|
||||
#timeline .timeline-desc {
|
||||
display: inline-block;
|
||||
width: 70%; }
|
||||
#timeline .timeline-id {
|
||||
display: inline-block; }
|
||||
#timeline .timeline-menu {
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
width: 29%; }
|
||||
#timeline .timeline-menu .drop-menu-items {
|
||||
text-align: right;
|
||||
right: 0; }
|
||||
|
||||
#entries {
|
||||
margin-top: 5.5em;
|
||||
padding: 0.5em 0.5em; }
|
||||
#entries .mark-input, #entries .timestamp-input {
|
||||
border: solid thin #555555;
|
||||
-webkit-box-shadow: inset 0px 2px 4px #CCC;
|
||||
box-shadow: inset 0px 2px 4px #CCC;
|
||||
margin-bottom: 0.5em;
|
||||
font-family: Cantarell; }
|
||||
#entries .mark-input {
|
||||
width: 78%; }
|
||||
#entries .entry {
|
||||
font-family: Cantarell; }
|
||||
#entries .entry div {
|
||||
display: inline-block; }
|
||||
#entries .entry .timestamp, #entries .entry .timestamp-input {
|
||||
width: 10%; }
|
||||
#entries .entry .duration {
|
||||
width: 10%; }
|
||||
#entries .entry .mark {
|
||||
width: 78%; }
|
||||
#entries .entry .mark-input, #entries .entry .timestamp-input {
|
||||
display: none; }
|
||||
#entries .entry .notes {
|
||||
display: none; }
|
||||
#entries .edit-mark .mark-input {
|
||||
display: inline-block; }
|
||||
#entries .edit-mark .mark {
|
||||
display: none; }
|
||||
#entries .edit-timestamp .timestamp-input {
|
||||
display: inline-block; }
|
||||
#entries .edit-timestamp .timestamp {
|
||||
display: none; }
|
||||
|
||||
#timeline #timeline-name, #timeline #timeline-desc {
|
||||
font-weight: bold; }
|
||||
#timeline #timeline-desc {
|
||||
color: #626150; }
|
||||
#timeline .control-links {
|
||||
padding-top: 0.2em; }
|
||||
|
||||
#user-info, #timeline-info {
|
||||
display: none;
|
||||
width: 100%;
|
||||
float: left; }
|
||||
|
||||
#new-entry {
|
||||
-moz-border-radius-bottomright: 0.5em;
|
||||
-webkit-border-bottom-right-radius: 0.5em;
|
||||
border-bottom-right-radius: 0.5em;
|
||||
-moz-border-radius-bottomleft: 0.5em;
|
||||
-webkit-border-bottom-left-radius: 0.5em;
|
||||
border-bottom-left-radius: 0.5em;
|
||||
border-bottom: solid #979681 0.2em;
|
||||
margin-bottom: 1em; }
|
||||
#new-entry form {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
#new-entry input {
|
||||
color: #626150; }
|
||||
#new-entry #add-notes {
|
||||
.drop-menu {
|
||||
position: relative; }
|
||||
.drop-menu .drop-menu-items {
|
||||
background: white;
|
||||
display: none;
|
||||
padding: 0.5em 0 0.5em 2em; }
|
||||
#new-entry #new-entry-input {
|
||||
margin-right: 1em;
|
||||
width: 15em; }
|
||||
list-style: none;
|
||||
position: absolute; }
|
||||
.drop-menu:hover .drop-menu-items, .drop-menu .drop-menu-items:hover {
|
||||
display: block; }
|
||||
|
||||
.entry-bar {
|
||||
background-color: #e6dec7;
|
||||
border-color: #979681;
|
||||
border-style: solid;
|
||||
border-width: 0.2em;
|
||||
border-bottom-width: 0;
|
||||
padding: 0.1em 1em 0.1em 1em;
|
||||
overflow: hidden; }
|
||||
.entry-bar .id {
|
||||
float: left;
|
||||
-moz-border-radius: 0.5em;
|
||||
-webkit-border-radius: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
padding: 0 0.3em 0 0.3em;
|
||||
background: #b34c2b;
|
||||
color: #c5c5b9;
|
||||
font-weight: bold;
|
||||
width: 2em;
|
||||
text-align: right; }
|
||||
.entry-bar .details {
|
||||
float: left; }
|
||||
.entry-bar .details .entry-mark {
|
||||
padding-left: 0.5em;
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
font-family: Helvetica, sans-serif; }
|
||||
.entry-bar .details .entry-notes {
|
||||
display: none;
|
||||
padding-left: 1.5em; }
|
||||
.entry-bar .entry-edit {
|
||||
display: none; }
|
||||
.entry-bar .entry-edit .id {
|
||||
width: 2em;
|
||||
padding: 0.2em 0.5em 0.2em 0.5em; }
|
||||
.entry-bar .entry-edit .entry-notes {
|
||||
padding: 0; }
|
||||
.footer {
|
||||
border-top: thin solid black;
|
||||
color: #888888;
|
||||
font-family: Bentham;
|
||||
margin: 1em 0;
|
||||
padding-top: 1em;
|
||||
text-align: center;
|
||||
width: 100%; }
|
||||
.footer a {
|
||||
color: #888888;
|
||||
text-decoration: none; }
|
||||
.footer a:hover {
|
||||
text-decoration: underline; }
|
||||
|
||||
.top-entry {
|
||||
-moz-border-radius-topleft: 0.5em;
|
||||
-webkit-border-top-left-radius: 0.5em;
|
||||
border-top-left-radius: 0.5em;
|
||||
-moz-border-radius-topright: 0.5em;
|
||||
-webkit-border-top-right-radius: 0.5em;
|
||||
border-top-right-radius: 0.5em; }
|
||||
|
||||
#login-dialog {
|
||||
font-size: small; }
|
||||
#login-dialog input.text {
|
||||
margin-bottom: 1em;
|
||||
width: 95%;
|
||||
padding: 0.4em; }
|
||||
#login-dialog form fieldset {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0; }
|
||||
#login-dialog .validate-tips {
|
||||
display: none; }
|
||||
|
||||
#ui-dialog-title-login-dialog, .ui-dialog-buttonset {
|
||||
font-size: medium; }
|
||||
|
||||
.signup {
|
||||
display: none; }
|
||||
|
||||
#signup-checkbox {
|
||||
display: inline; }
|
||||
.logo {
|
||||
font-family: Bentham;
|
||||
text-decoration: overline underline;
|
||||
color: inherit; }
|
||||
|
@ -3,328 +3,179 @@
|
||||
* TimeStamper main CSS for screen media types
|
||||
*/
|
||||
|
||||
/*
|
||||
$obg: #D9CEB2;
|
||||
$bor: #948C75;
|
||||
$ibg: #D5DED9;
|
||||
$txt: #7A6A53;
|
||||
$bbg: #99B2B7;
|
||||
*/
|
||||
|
||||
/*
|
||||
$obg: #979681;
|
||||
$obor: #E6DEC7;
|
||||
$ibg: #657A8B;
|
||||
$bbor: #B34C2B;
|
||||
$bbg: #252D42;
|
||||
$txt: #E6DEC7;
|
||||
*/
|
||||
|
||||
$obg: #252D42;
|
||||
$obor: #B34C2B;
|
||||
$ibg: #657A8B;
|
||||
$bbor: #979681;
|
||||
$bbg: #E6DEC7;
|
||||
$txt: #252D42;
|
||||
$greyTxt: darken($bbor, 20%);
|
||||
|
||||
$iBorWidth: 0.2em;
|
||||
|
||||
@import "rounded";
|
||||
|
||||
html {
|
||||
//background-color: $obg;
|
||||
background: url('/img/loving_blu.png') repeat;
|
||||
$txtClr: #222;
|
||||
|
||||
* {
|
||||
color: $txtClr;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 50%;
|
||||
background-color: $ibg;
|
||||
width: 70%;
|
||||
margin: auto;
|
||||
padding: 1em;
|
||||
border: solid $obor;
|
||||
border-top: 0;
|
||||
@include rounded2(bottom, left, 1em);
|
||||
@include rounded2(bottom, right, 1em);
|
||||
}
|
||||
|
||||
@media all and (min-device-width: 480) { body { width: 50%; }}
|
||||
#top {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
background: white;
|
||||
width: 70%;
|
||||
|
||||
@media all and (max-device-width: 480) { body { width: 80%; }}
|
||||
|
||||
.control-links {
|
||||
|
||||
color: lighten($greyTxt, 40%);
|
||||
float: right;
|
||||
display: block;
|
||||
height: 100%;
|
||||
font-weight: bold;
|
||||
font-size: smaller;
|
||||
|
||||
&:hover { color: $txt; }
|
||||
|
||||
a {
|
||||
color: inherit; //lighten($greyTxt, 20%);
|
||||
text-decoration: none;
|
||||
margin-right: 0.5em;
|
||||
&:hover {
|
||||
color: $obor;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bar {
|
||||
|
||||
font-family: Helvetica, sans-serif;
|
||||
color: $txt;
|
||||
background-color: $bbg;
|
||||
border-color: $bbor;
|
||||
border-style: solid;
|
||||
border-width: $iBorWidth;
|
||||
border-bottom-width: 0;
|
||||
padding: 0.1em 1em 0.1em 1em;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
|
||||
.last-bar {
|
||||
|
||||
border-color: $bbor;
|
||||
border-style: solid;
|
||||
border-width: $iBorWidth;
|
||||
@include rounded2(bottom, right, 0.5em);
|
||||
@include rounded2(bottom, left, 0.5em);
|
||||
|
||||
background-color: $bbg;
|
||||
padding: 0.1em 1em 0.1em 1em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#more-entries {
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
div {
|
||||
float: right;
|
||||
left: -50%;
|
||||
position: relative;
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
float: right;
|
||||
left: 50%;
|
||||
|
||||
border: 1px solid $bbor;
|
||||
padding: 0.1em;
|
||||
background: lighten($bbg, 10%);
|
||||
color: $greyTxt;
|
||||
text-decoration: none;
|
||||
font-size: smaller;
|
||||
|
||||
&:hover { color: $obor; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bar form {
|
||||
|
||||
border-top: solid 1px $bbor;
|
||||
padding: 0.5em 0 0.5em 2em;
|
||||
overflow: hidden;
|
||||
|
||||
label {
|
||||
span {
|
||||
float: left;
|
||||
width: 6em;
|
||||
padding-top: 0.1em;
|
||||
}
|
||||
color: $greyTxt;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input.text-input { border: 1px solid $bbor; }
|
||||
|
||||
.form-col {
|
||||
overflow: hidden;
|
||||
float: left;
|
||||
padding-right: 2em;
|
||||
|
||||
input.text-input { width: 10em; }
|
||||
}
|
||||
|
||||
div.form-submit {
|
||||
float: left;
|
||||
#fade-bar {
|
||||
background: url('img/fade.png') repeat-x;
|
||||
height: 32px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
padding: 0.5em 2em 0.5em 2em;
|
||||
position: relative;
|
||||
|
||||
div {
|
||||
position: relative;
|
||||
float: right;
|
||||
left: -50%;
|
||||
|
||||
input {
|
||||
position: relative;
|
||||
left: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
div.form-submit input, input.form-submit {
|
||||
border: 1px solid $bbor;
|
||||
background: lighten($bbg, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
#user {
|
||||
|
||||
@include rounded2(top, left, 0.5em);
|
||||
@include rounded2(top, right, 0.5em);
|
||||
font-family: "Josefin Sans";
|
||||
width: 100%;
|
||||
|
||||
.control-links { padding-top: 0.6em; }
|
||||
|
||||
#fullname, #username {
|
||||
font-weight: bold;
|
||||
font-size: x-large;
|
||||
float: left;
|
||||
.fullname, .username {
|
||||
display: inline-block;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
#username {
|
||||
padding-left: 0.2em;
|
||||
color: $greyTxt;
|
||||
.fullname-input { display: none; }
|
||||
|
||||
&.fullname-edit {
|
||||
.fullname-input { display: inline-block; }
|
||||
.fullname { display: none; }
|
||||
}
|
||||
|
||||
#change-pwd { display: none; }
|
||||
ul#user-controls {
|
||||
float: right;
|
||||
list-style: none;
|
||||
|
||||
#user-info .form-submit {
|
||||
li {
|
||||
float: right;
|
||||
text-align: center;
|
||||
//margin-top: -0.5em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#timeline {
|
||||
|
||||
#timeline-name, #timeline-desc { font-weight: bold; }
|
||||
border-bottom: thin solid black;
|
||||
font-family: Arvo;
|
||||
font-size: 1.5em;
|
||||
|
||||
#timeline-desc { color: $greyTxt; }
|
||||
|
||||
.control-links { padding-top: 0.2em; }
|
||||
|
||||
}
|
||||
|
||||
#user-info, #timeline-info {
|
||||
display: none;
|
||||
width: 100%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#new-entry {
|
||||
|
||||
@include rounded2(bottom, right, 0.5em);
|
||||
@include rounded2(bottom, left, 0.5em);
|
||||
border-bottom: solid $bbor $iBorWidth;
|
||||
|
||||
margin-bottom: 1em;
|
||||
|
||||
form {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
.timeline-desc {
|
||||
display: inline-block;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
input { color: $greyTxt; }
|
||||
.timeline-id { display: inline-block; }
|
||||
|
||||
#add-notes {
|
||||
display: none;
|
||||
padding: 0.5em 0 0.5em 2em;
|
||||
}
|
||||
|
||||
#new-entry-input {
|
||||
margin-right: 1em;
|
||||
width: 15em;
|
||||
}
|
||||
}
|
||||
|
||||
.entry-bar {
|
||||
|
||||
background-color: $bbg;
|
||||
border-color: $bbor;
|
||||
border-style: solid;
|
||||
border-width: $iBorWidth;
|
||||
border-bottom-width: 0;
|
||||
padding: 0.1em 1em 0.1em 1em;
|
||||
overflow: hidden;
|
||||
|
||||
.id {
|
||||
float: left;
|
||||
@include rounded(0.5em);
|
||||
padding: 0 0.3em 0 0.3em;
|
||||
background: $obor;
|
||||
color: lighten($greyTxt, 40%);
|
||||
font-weight: bold;
|
||||
width: 2em;
|
||||
.timeline-menu {
|
||||
text-align: right;
|
||||
}
|
||||
display: inline-block;
|
||||
width: 29%;
|
||||
|
||||
.details {
|
||||
|
||||
float: left;
|
||||
|
||||
.entry-mark {
|
||||
padding-left: 0.5em;
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
font-family: Helvetica, sans-serif;
|
||||
}
|
||||
.entry-notes {
|
||||
display: none;
|
||||
padding-left: 1.5em;
|
||||
.drop-menu-items {
|
||||
text-align: right;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.entry-edit {
|
||||
#entries {
|
||||
|
||||
margin-top: 5.5em;
|
||||
padding: 0.5em 0.5em;
|
||||
|
||||
.mark-input, .timestamp-input {
|
||||
border: solid thin lighten($txtClr, 20%);
|
||||
-webkit-box-shadow: inset 0px 2px 4px #CCC;
|
||||
box-shadow: inset 0px 2px 4px #CCC;
|
||||
margin-bottom: 0.5em;
|
||||
font-family: Cantarell;
|
||||
}
|
||||
|
||||
.mark-input { width: 78%; }
|
||||
|
||||
.entry {
|
||||
|
||||
font-family: Cantarell;
|
||||
|
||||
div { display: inline-block; }
|
||||
|
||||
.timestamp, .timestamp-input { width: 10%; }
|
||||
|
||||
.duration { width: 10%; }
|
||||
|
||||
.mark { width: 78%; }
|
||||
|
||||
.mark-input, .timestamp-input { display: none; }
|
||||
|
||||
.notes { display: none; }
|
||||
|
||||
}
|
||||
|
||||
.edit-mark {
|
||||
.mark-input { display: inline-block; }
|
||||
.mark { display: none; }
|
||||
}
|
||||
|
||||
.edit-timestamp {
|
||||
.timestamp-input { display: inline-block; }
|
||||
.timestamp { display: none; }
|
||||
}
|
||||
}
|
||||
|
||||
.drop-menu {
|
||||
position: relative;
|
||||
|
||||
.drop-menu-items {
|
||||
background: white;
|
||||
display: none;
|
||||
list-style: none;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.id {
|
||||
width: 2em;
|
||||
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||
}
|
||||
&:hover .drop-menu-items, .drop-menu-items:hover {
|
||||
display: block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.footer {
|
||||
|
||||
border-top: thin solid black;
|
||||
color: lighten($txtClr, 40%);
|
||||
font-family: Bentham;
|
||||
margin: 1em 0;
|
||||
padding-top: 1em;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
a {
|
||||
color: lighten($txtClr, 40%);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover { text-decoration: underline; }
|
||||
|
||||
.entry-notes {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.top-entry {
|
||||
|
||||
@include rounded2(top, left, 0.5em);
|
||||
@include rounded2(top, right, 0.5em);
|
||||
.logo {
|
||||
font-family: Bentham;
|
||||
text-decoration: overline underline;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#login-dialog {
|
||||
font-size: small;
|
||||
|
||||
//label, input { display: block }
|
||||
input.text {
|
||||
margin-bottom: 1em;
|
||||
width: 95%;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
form fieldset {
|
||||
padding: 0; border: 0; margin: 0;
|
||||
}
|
||||
|
||||
.validate-tips { display: none; }
|
||||
}
|
||||
|
||||
#ui-dialog-title-login-dialog, .ui-dialog-buttonset {
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
.signup { display: none; }
|
||||
|
||||
#signup-checkbox { display: inline; }
|
||||
|
228
www/index.yaws
228
www/index.yaws
@ -1,11 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>TimeStamper - Simple Time Tracking</title>
|
||||
<link rel="stylesheet" media="screen" href="/css/dot-luv/jquery-ui-1.8.10.custom.css" type="text/css"/>
|
||||
<link rel="stylesheet" media="screen" href="/css/ts-screen.css" type="text/css"/>
|
||||
<!-- Needed for IE, but I'm not going to support IE with this tool. -->
|
||||
<link href='http://fonts.googleapis.com/css?family=Arvo|Bentham|Cuprum|Cantarell|Geo|Josefin+Sans' rel='stylesheet' type='text/css'>
|
||||
<link rel="stylesheet" media="screen" href="css/dot-luv/jquery-ui-1.8.10.custom.css" type="text/css"/>
|
||||
<link rel="stylesheet" media="screen" href="css/ts-screen.css" type="text/css"/>
|
||||
<!-- Needed for IE, but I'm not sure if I'm going to support IE with this tool. -->
|
||||
<!--<script type="text/javascript" src="/js/json2.js"></script>-->
|
||||
<!-- PROD -->
|
||||
<!--
|
||||
@ -13,194 +13,62 @@
|
||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js"></script>
|
||||
-->
|
||||
<!-- DEV -->
|
||||
<script type="text/javascript" src="/js/jquery-1.5.min.js"></script>
|
||||
<script type="text/javascript" src="/js/jquery-ui-1.8.10.custom.min.js"></script>
|
||||
<script type="text/javascript" src="js/jquery-1.5.min.js"></script>
|
||||
<script type="text/javascript" src="js/jquery-ui-1.8.10.custom.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/js/underscore-min.js"></script>
|
||||
<script type="text/javascript" src="/js/ICanHaz.js"></script>
|
||||
<script type="text/javascript" src="/js/ts.js"></script>
|
||||
<script type="text/javascript" src="js/underscore-min.js"></script>
|
||||
<script type="text/javascript" src="js/ICanHaz.js"></script>
|
||||
<script type="text/javascript" src="js/backbone-min.js"></script>
|
||||
<script type="text/javascript" src="js/ts.js"></script>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
|
||||
<script id="entry" type="text/html">
|
||||
<div class="entry-bar" id="entry-{{entry_id}}">
|
||||
<div class="entry-display">
|
||||
<span class="id">{{entry_id}}</span>
|
||||
<div class="details">
|
||||
<div class="entry-mark">{{mark}}</div>
|
||||
<div class="entry-notes">{{notes}}</div>
|
||||
</div>
|
||||
<div class="control-links">
|
||||
<a onclick="$('#entry-{{entry_id}} .entry-display .entry-notes').slideToggle('slow');"
|
||||
href="#">show notes</a>
|
||||
<a onclick="toggleEditEntry(event, {{entry_id}})"
|
||||
href="#">edit</a>
|
||||
<a onclick="deleteEntry(event, {{entry_id}})"
|
||||
href="#">del</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="entry-edit">
|
||||
<form action="/ts/update-entry.yaws"
|
||||
onsubmit="updateEntry(event, {{entry_id}})">
|
||||
<input type="text" id="entry-{{entry_id}}-id-input"
|
||||
class="id" value="{{entry_id}}"/>
|
||||
<div class="details">
|
||||
<input type="text" id="entry-{{entry_id}}-mark-input"
|
||||
class="entry-mark" value="{{mark}}"/></br>
|
||||
<textarea id="entry-{{entry_id}}-notes-input"
|
||||
class="entry-notes" rows="8" cols="40" >{{notes}}</textarea>
|
||||
</div>
|
||||
</form>
|
||||
<div class="control-links">
|
||||
<a onclick="$('#entry-{{entry_id}} .entry-edit .entry-notes').slideToggle('slow');"
|
||||
href="#">show notes</a>
|
||||
<a onclick="updateEntry(event, {{entry_id}})"
|
||||
href="#">save changes</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="user" class="bar">
|
||||
<span id="fullname">Not Logged In</span>
|
||||
<span id="username">- no_user</span>
|
||||
<div class="control-links">
|
||||
<a href="/ts/edit-user.yaws"
|
||||
onclick="$('#user-info').slideToggle('slow'); return false;">
|
||||
user info</a>
|
||||
<a href="/ts/logout.yaws" onclick="logout(event)">logout</a>
|
||||
<div id="top">
|
||||
<div id="timeline">
|
||||
<span class="timeline-desc">Work-related events.</span>
|
||||
<input class="timeline-desc-input" type="text"/>
|
||||
<div class="timeline-menu" class="drop-menu">
|
||||
<div class="timeline-id">( work )</div>
|
||||
<input class="timeline-id-input" type="text"/>
|
||||
<ul class="drop-menu-items">
|
||||
<li>jdb-labs</li>
|
||||
<li>timestamper</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="user-info">
|
||||
<form action="/ts/update-user.yaws" onsubmit="updateUser(event)">
|
||||
<div class="form-col">
|
||||
<label for="fullname-input"><span>name:</span>
|
||||
<input id="fullname-input" name="fullname"
|
||||
class="text-input" type="text"/>
|
||||
</label>
|
||||
<label for="email-input"><span>email:</span>
|
||||
<input id="email-input" name="email"
|
||||
class="text-input" type="text"/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-col">
|
||||
<div id="change-pwd">
|
||||
<label for="old-pwd-input"><span>password:</span>
|
||||
<input id="old-pwd-input" name="old-pwd"
|
||||
class="text-input" type="password"/>
|
||||
</label>
|
||||
<label for="new-pwd-input"><span>new pwd:</span>
|
||||
<input id="new-pwd-input" name="new-pwd"
|
||||
class="text-input" type="password"/>
|
||||
</label>
|
||||
<label for="new-pwd-conf-input"><span>confirm:</span>
|
||||
<input id="new-pwd-conf-input" name="new-pwd-conf"
|
||||
class="text-input" type="password"/>
|
||||
</label>
|
||||
</div>
|
||||
<label for="enable-pwd-change-input">
|
||||
<input name="enable-pwd-change" type="checkbox"
|
||||
id="enable-pwd-change-input"
|
||||
onclick="$('#change-pwd').slideToggle('slow');"/>
|
||||
change password
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-submit">
|
||||
<div>
|
||||
<input name="submit-user" type="submit"
|
||||
value="save changes"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="user">
|
||||
<div class="fullname">Jonathan Bernard</div>
|
||||
<input class='fullname-input' type='text'/>
|
||||
<div class="username"> - jdbernard</div>
|
||||
<ul class="user-controls">
|
||||
<li><a href="#">logout</a></li>
|
||||
<li><a href="#">user info</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="entries">
|
||||
<div id="new-entry">
|
||||
<input id="new-entry-input" class="mark-input"
|
||||
placeholder="Start a new task..." type="text" />
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark">ITHelp: Entering tickets.</div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">12:32</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">4<span class="tick-tock">:</span>03<span class="tick-tock">:</span>57</div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="timeline" class="bar">
|
||||
<span id="timeline-name">timeline |</span>
|
||||
<span id="timeline-desc">timeline description</span>
|
||||
<div class="control-links">
|
||||
<a href="/ts/edit-timeline.yaws"
|
||||
onclick="$('#timeline-info').slideToggle('slow'); return false;">
|
||||
timeline info</a>
|
||||
<a href="/ts/select-timeline.yaws"
|
||||
onclick="showTimelineMenu(event)">change timelines</a>
|
||||
</div>
|
||||
|
||||
<div id="timeline-info">
|
||||
<form action="/ts/update-timeline.yaws"
|
||||
onsubmit="updateTimeline(event); false">
|
||||
<label for="timeline-desc-input"><span>description:</span>
|
||||
<input id="timeline-desc-input" class="text-input"
|
||||
name="timeline-desc" type="text"/>
|
||||
</label>
|
||||
<div class="form-submit">
|
||||
<div><input name="submit-timeline" type="submit"
|
||||
value="save changes"/></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="new-entry" class="bar">
|
||||
<form action="/ts/new-entry.yaws" onsubmit="newEntry(event)">
|
||||
begin a new activity:
|
||||
<input name="new-entry" id="new-entry-input"
|
||||
class="text-input" type="text"/>
|
||||
<input name="submit-entry" id="submit-entry"
|
||||
class="form-submit" type="submit" value="create entry"/>
|
||||
<div class="control-links">
|
||||
<a id="show-notes" href="#"
|
||||
onclick="$('#add-notes').slideToggle('slow');">
|
||||
add notes</a>
|
||||
</div>
|
||||
<div id="add-notes" class="form-col">
|
||||
<label for="new-notes-input">notes:</label>
|
||||
<textarea name="new-notes" id="new-notes-input"
|
||||
class="text-input" rows="8" cols="40" ></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="more-entries" class="last-bar top-entry">
|
||||
<div>
|
||||
<a href="#" onclick="loadEntries(user, activeTimeline, 'old');event.preventDefault()">load more entries</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="login-dialog" title="Login">
|
||||
<form>
|
||||
<fieldset>
|
||||
<label for="login-name">Username:</label>
|
||||
<input type="text" name="login-name" id="login-name"
|
||||
class="text ui-widget-content ui-corner-all"></input>
|
||||
<div class="signup">
|
||||
<label for="signup-fullname">Full name:</label>
|
||||
<input type="text" name="signup-fullname" id="signup-fullname"
|
||||
class="text ui-widget-content ui-corner-all"></input>
|
||||
<label for="signup-email">eMail address:</label>
|
||||
<input type="text" name="signup-email" id="signup-email"
|
||||
class="text ui-widget-content ui-corner-all"></input>
|
||||
</div>
|
||||
<label for="login-password">Password:</label>
|
||||
<input type="password" name="login-password" id="login-password"
|
||||
class="text ui-widget-content ui-corner-all"></input>
|
||||
|
||||
<div class="signup">
|
||||
<label for="confirm-password">Confirm password:</label>
|
||||
<input type="password" name="confirm-password" id="confirm-password"
|
||||
class="text ui-widget-content ui-corner-all"></input>
|
||||
</div>
|
||||
<label for="signup-checkbox">
|
||||
<input type="checkbox" id="signup-checkbox" name="signup-checkbox"
|
||||
onclick="toggleSignUp(event)"/>
|
||||
I'm a new user!
|
||||
</label>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<p class="validate-tips"></p>
|
||||
<div class="footer">
|
||||
Copyright 2011 <a href="http://www.jdb-labs.com"><span class="logo">JDB Labs</span> LLC.</a>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
27
www/js/backbone-min.js
vendored
Normal file
27
www/js/backbone-min.js
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
// Backbone.js 0.3.3
|
||||
// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Backbone may be freely distributed under the MIT license.
|
||||
// For all details and documentation:
|
||||
// http://documentcloud.github.com/backbone
|
||||
(function(){var e;e=typeof exports!=="undefined"?exports:this.Backbone={};e.VERSION="0.3.3";var f=this._;if(!f&&typeof require!=="undefined")f=require("underscore")._;var h=this.jQuery||this.Zepto;e.emulateHTTP=false;e.emulateJSON=false;e.Events={bind:function(a,b){this._callbacks||(this._callbacks={});(this._callbacks[a]||(this._callbacks[a]=[])).push(b);return this},unbind:function(a,b){var c;if(a){if(c=this._callbacks)if(b){c=c[a];if(!c)return this;for(var d=0,g=c.length;d<g;d++)if(b===c[d]){c.splice(d,
|
||||
1);break}}else c[a]=[]}else this._callbacks={};return this},trigger:function(a){var b,c,d,g;if(!(c=this._callbacks))return this;if(b=c[a]){d=0;for(g=b.length;d<g;d++)b[d].apply(this,Array.prototype.slice.call(arguments,1))}if(b=c.all){d=0;for(g=b.length;d<g;d++)b[d].apply(this,arguments)}return this}};e.Model=function(a,b){a||(a={});if(this.defaults)a=f.extend({},this.defaults,a);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.set(a,{silent:true});this._previousAttributes=
|
||||
f.clone(this.attributes);if(b&&b.collection)this.collection=b.collection;this.initialize(a,b)};f.extend(e.Model.prototype,e.Events,{_previousAttributes:null,_changed:false,initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.attributes[a];return this._escapedAttributes[a]=(b==null?"":b).replace(/&(?!\w+;)/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,
|
||||
""")},set:function(a,b){b||(b={});if(!a)return this;if(a.attributes)a=a.attributes;var c=this.attributes,d=this._escapedAttributes;if(!b.silent&&this.validate&&!this._performValidation(a,b))return false;if("id"in a)this.id=a.id;for(var g in a){var i=a[g];if(!f.isEqual(c[g],i)){c[g]=i;delete d[g];if(!b.silent){this._changed=true;this.trigger("change:"+g,this,i,b)}}}!b.silent&&this._changed&&this.change(b);return this},unset:function(a,b){b||(b={});var c={};c[a]=void 0;if(!b.silent&&this.validate&&
|
||||
!this._performValidation(c,b))return false;delete this.attributes[a];delete this._escapedAttributes[a];if(!b.silent){this._changed=true;this.trigger("change:"+a,this,void 0,b);this.change(b)}return this},clear:function(a){a||(a={});var b=this.attributes,c={};for(attr in b)c[attr]=void 0;if(!a.silent&&this.validate&&!this._performValidation(c,a))return false;this.attributes={};this._escapedAttributes={};if(!a.silent){this._changed=true;for(attr in b)this.trigger("change:"+attr,this,void 0,a);this.change(a)}return this},
|
||||
fetch:function(a){a||(a={});var b=this,c=j(a.error,b,a);(this.sync||e.sync)("read",this,function(d){if(!b.set(b.parse(d),a))return false;a.success&&a.success(b,d)},c);return this},save:function(a,b){b||(b={});if(a&&!this.set(a,b))return false;var c=this,d=j(b.error,c,b),g=this.isNew()?"create":"update";(this.sync||e.sync)(g,this,function(i){if(!c.set(c.parse(i),b))return false;b.success&&b.success(c,i)},d);return this},destroy:function(a){a||(a={});var b=this,c=j(a.error,b,a);(this.sync||e.sync)("delete",
|
||||
this,function(d){b.collection&&b.collection.remove(b);a.success&&a.success(b,d)},c);return this},url:function(){var a=k(this.collection);if(this.isNew())return a;return a+(a.charAt(a.length-1)=="/"?"":"/")+this.id},parse:function(a){return a},clone:function(){return new this.constructor(this)},isNew:function(){return!this.id},change:function(a){this.trigger("change",this,a);this._previousAttributes=f.clone(this.attributes);this._changed=false},hasChanged:function(a){if(a)return this._previousAttributes[a]!=
|
||||
this.attributes[a];return this._changed},changedAttributes:function(a){a||(a=this.attributes);var b=this._previousAttributes,c=false,d;for(d in a)if(!f.isEqual(b[d],a[d])){c=c||{};c[d]=a[d]}return c},previous:function(a){if(!a||!this._previousAttributes)return null;return this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},_performValidation:function(a,b){var c=this.validate(a);if(c){b.error?b.error(this,c):this.trigger("error",this,c,b);return false}return true}});
|
||||
e.Collection=function(a,b){b||(b={});if(b.comparator){this.comparator=b.comparator;delete b.comparator}this._boundOnModelEvent=f.bind(this._onModelEvent,this);this._reset();a&&this.refresh(a,{silent:true});this.initialize(a,b)};f.extend(e.Collection.prototype,e.Events,{model:e.Model,initialize:function(){},toJSON:function(){return this.map(function(a){return a.toJSON()})},add:function(a,b){if(f.isArray(a))for(var c=0,d=a.length;c<d;c++)this._add(a[c],b);else this._add(a,b);return this},remove:function(a,
|
||||
b){if(f.isArray(a))for(var c=0,d=a.length;c<d;c++)this._remove(a[c],b);else this._remove(a,b);return this},get:function(a){if(a==null)return null;return this._byId[a.id!=null?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");this.models=this.sortBy(this.comparator);a.silent||this.trigger("refresh",this,a);return this},pluck:function(a){return f.map(this.models,
|
||||
function(b){return b.get(a)})},refresh:function(a,b){a||(a=[]);b||(b={});this._reset();this.add(a,{silent:true});b.silent||this.trigger("refresh",this,b);return this},fetch:function(a){a||(a={});var b=this,c=j(a.error,b,a);(this.sync||e.sync)("read",this,function(d){b.refresh(b.parse(d));a.success&&a.success(b,d)},c);return this},create:function(a,b){var c=this;b||(b={});if(a instanceof e.Model)a.collection=c;else a=new this.model(a,{collection:c});return a.save(null,{success:function(d,g){c.add(d);
|
||||
b.success&&b.success(d,g)},error:b.error})},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId={};this._byCid={}},_add:function(a,b){b||(b={});a instanceof e.Model||(a=new this.model(a,{collection:this}));var c=this.getByCid(a);if(c)throw Error(["Can't add the same model to a set twice",c.id]);this._byId[a.id]=a;this._byCid[a.cid]=a;a.collection=this;this.models.splice(this.comparator?this.sortedIndex(a,this.comparator):
|
||||
this.length,0,a);a.bind("all",this._boundOnModelEvent);this.length++;b.silent||a.trigger("add",a,this,b);return a},_remove:function(a,b){b||(b={});a=this.getByCid(a)||this.get(a);if(!a)return null;delete this._byId[a.id];delete this._byCid[a.cid];delete a.collection;this.models.splice(this.indexOf(a),1);this.length--;b.silent||a.trigger("remove",a,this,b);a.unbind("all",this._boundOnModelEvent);return a},_onModelEvent:function(a,b){if(a==="change:id"){delete this._byId[b.previous("id")];this._byId[b.id]=
|
||||
b}this.trigger.apply(this,arguments)}});f.each(["forEach","each","map","reduce","reduceRight","find","detect","filter","select","reject","every","all","some","any","include","invoke","max","min","sortBy","sortedIndex","toArray","size","first","rest","last","without","indexOf","lastIndexOf","isEmpty"],function(a){e.Collection.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});e.Controller=function(a){a||(a={});if(a.routes)this.routes=a.routes;this._bindRoutes();
|
||||
this.initialize(a)};var o=/:([\w\d]+)/g,p=/\*([\w\d]+)/g;f.extend(e.Controller.prototype,e.Events,{initialize:function(){},route:function(a,b,c){e.history||(e.history=new e.History);f.isRegExp(a)||(a=this._routeToRegExp(a));e.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d))},this))},saveLocation:function(a){e.history.saveLocation(a)},_bindRoutes:function(){if(this.routes)for(var a in this.routes){var b=this.routes[a];
|
||||
this.route(a,b,this[b])}},_routeToRegExp:function(a){a=a.replace(o,"([^/]*)").replace(p,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});e.History=function(){this.handlers=[];this.fragment=this.getFragment();f.bindAll(this,"checkUrl")};var l=/^#*/;f.extend(e.History.prototype,{interval:50,getFragment:function(a){return(a||window.location).hash.replace(l,"")},start:function(){var a=document.documentMode;if(a=h.browser.msie&&(!a||a<=7))this.iframe=h('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow;
|
||||
"onhashchange"in window&&!a?h(window).bind("hashchange",this.checkUrl):setInterval(this.checkUrl,this.interval);return this.loadUrl()},route:function(a,b){this.handlers.push({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();if(a==this.fragment&&this.iframe)a=this.getFragment(this.iframe.location);if(a==this.fragment||a==decodeURIComponent(this.fragment))return false;if(this.iframe)window.location.hash=this.iframe.location.hash=a;this.loadUrl()},loadUrl:function(){var a=this.fragment=
|
||||
this.getFragment();return f.any(this.handlers,function(b){if(b.route.test(a)){b.callback(a);return true}})},saveLocation:function(a){a=(a||"").replace(l,"");if(this.fragment!=a){window.location.hash=this.fragment=a;if(this.iframe&&a!=this.getFragment(this.iframe.location)){this.iframe.document.open().close();this.iframe.location.hash=a}}}});e.View=function(a){this._configure(a||{});this._ensureElement();this.delegateEvents();this.initialize(a)};var q=/^(\w+)\s*(.*)$/;f.extend(e.View.prototype,e.Events,
|
||||
{tagName:"div",$:function(a){return h(a,this.el)},initialize:function(){},render:function(){return this},remove:function(){h(this.el).remove();return this},make:function(a,b,c){a=document.createElement(a);b&&h(a).attr(b);c&&h(a).html(c);return a},delegateEvents:function(a){if(a||(a=this.events)){h(this.el).unbind();for(var b in a){var c=a[b],d=b.match(q),g=d[1];d=d[2];c=f.bind(this[c],this);d===""?h(this.el).bind(g,c):h(this.el).delegate(d,g,c)}}},_configure:function(a){if(this.options)a=f.extend({},
|
||||
this.options,a);if(a.model)this.model=a.model;if(a.collection)this.collection=a.collection;if(a.el)this.el=a.el;if(a.id)this.id=a.id;if(a.className)this.className=a.className;if(a.tagName)this.tagName=a.tagName;this.options=a},_ensureElement:function(){if(!this.el){var a={};if(this.id)a.id=this.id;if(this.className)a["class"]=this.className;this.el=this.make(this.tagName,a)}}});var m=function(a,b){var c=r(this,a,b);c.extend=m;return c};e.Model.extend=e.Collection.extend=e.Controller.extend=e.View.extend=
|
||||
m;var s={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};e.sync=function(a,b,c,d){var g=s[a];a=a==="create"||a==="update"?JSON.stringify(b.toJSON()):null;b={url:k(b),type:g,contentType:"application/json",data:a,dataType:"json",processData:false,success:c,error:d};if(e.emulateJSON){b.contentType="application/x-www-form-urlencoded";b.processData=true;b.data=a?{model:a}:{}}if(e.emulateHTTP)if(g==="PUT"||g==="DELETE"){if(e.emulateJSON)b.data._method=g;b.type="POST";b.beforeSend=function(i){i.setRequestHeader("X-HTTP-Method-Override",
|
||||
g)}}h.ajax(b)};var n=function(){},r=function(a,b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){return a.apply(this,arguments)};n.prototype=a.prototype;d.prototype=new n;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},k=function(a){if(!(a&&a.url))throw Error("A 'url' property or function must be specified");return f.isFunction(a.url)?a.url():a.url},j=function(a,b,c){return function(d){a?a(b,d):b.trigger("error",b,d,c)}}})();
|
1098
www/js/backbone.js
Normal file
1098
www/js/backbone.js
Normal file
File diff suppressed because it is too large
Load Diff
69
www/js/test.js
Normal file
69
www/js/test.js
Normal file
@ -0,0 +1,69 @@
|
||||
var Test = {};
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
Test.U = Backbone.Model.extend({
|
||||
url: '/ts_api/users'
|
||||
});
|
||||
|
||||
Test.UView = Backbone.View.extend({
|
||||
|
||||
el: $("#user"),
|
||||
model: Test.U,
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'render');
|
||||
this.model.bind('change', this.render);
|
||||
this.model.view = this;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var user = this.model.get('user');
|
||||
this.$('.fullname').text(user.name);
|
||||
this.$('.username').text(user.username);
|
||||
}
|
||||
});
|
||||
|
||||
Test.currentUser = new Test.U;
|
||||
Test.userView = new Test.UView({model: Test.currentUser});
|
||||
|
||||
// wire the login dialog using jQuery UI
|
||||
$("#login-dialog").dialog({
|
||||
autoOpen: false,
|
||||
height: 400,
|
||||
width: 400,
|
||||
modal: true,
|
||||
buttons: { Login: function(){login()} }
|
||||
});
|
||||
|
||||
$('#login-dialog').dialog('open');
|
||||
|
||||
});
|
||||
|
||||
function login() {
|
||||
// lookup the login dialog elements
|
||||
var name = $("#login-name");
|
||||
var pwd = $("#login-password");
|
||||
|
||||
// call the API via AJAX
|
||||
$.ajax({
|
||||
url: "/ts_api/login",
|
||||
processData: false,
|
||||
data: JSON.stringify({username: name.val(), password: pwd.val()}),
|
||||
type: "POST",
|
||||
|
||||
error: function(jqXHR, textStatus, error) {
|
||||
// assuming bad credentials (possible server error or bad request,
|
||||
// we should check that, FIXME
|
||||
var tips = $(".validate-tips");
|
||||
tips.text("Incorrect username/password combination.");
|
||||
tips.addClass("ui-state-error");
|
||||
tips.slideDown();
|
||||
},
|
||||
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
$("#login-dialog").dialog('close');
|
||||
Test.currentUser.set({id: name.val()}, {silent: true});
|
||||
Test.currentUser.fetch();
|
||||
}});
|
||||
}
|
542
www/js/ts.js
542
www/js/ts.js
@ -1,54 +1,214 @@
|
||||
var user = [];
|
||||
var timelines = [];
|
||||
var activeTimeline = [];
|
||||
var entries = [];
|
||||
|
||||
var newEntryBar;
|
||||
var moreEntriesbar;
|
||||
|
||||
var currentEntryOffset = 0;
|
||||
var loadLength = 20;
|
||||
|
||||
var loginTop;
|
||||
// TimeStamper namespace
|
||||
var TS = {};
|
||||
|
||||
/* Setup after the document is ready for manipulation. */
|
||||
$(document).ready(function(){
|
||||
|
||||
// looked up once and remembered
|
||||
newEntryBar = $("#new-entry");
|
||||
moreEntriesBar = $("#more-entries");
|
||||
// ======== DEFINE MODELS ========//
|
||||
|
||||
/* Entry model.
|
||||
* Attributes
|
||||
* - id
|
||||
* - mark
|
||||
* - notes
|
||||
* - start
|
||||
*/
|
||||
TS.Entry = Backbone.Model.extend({
|
||||
|
||||
// wire the login dialog using jQuery UI
|
||||
$("#login-dialog").dialog({
|
||||
autoOpen: false,
|
||||
height: 300,
|
||||
width: 300,
|
||||
modal: true,
|
||||
buttons: {
|
||||
"Sign Up": function(){signup()},
|
||||
Login: function(){login()} }
|
||||
});
|
||||
|
||||
// TODO: add a hook to AJAX requests to check for 401 unauth
|
||||
// and re-display the login dialog.
|
||||
/* Timeline model.
|
||||
* Attributes:
|
||||
* - id
|
||||
* - desc
|
||||
* - created
|
||||
*/
|
||||
TS.Timeline = Backbone.Model.extend({
|
||||
|
||||
// try to load user information for an authenticated user
|
||||
$.ajax({
|
||||
url: "/ts_api/users/",
|
||||
type: "GET",
|
||||
});
|
||||
|
||||
error: function(jqXHR, textStatus, error) {
|
||||
// assume there is no authenticated user, show login dialog
|
||||
$("#login-dialog").dialog("open"); },
|
||||
/* User model.
|
||||
* Attributes:
|
||||
* - username
|
||||
* - fullname
|
||||
* - email
|
||||
* - join_date
|
||||
*/
|
||||
TS.User = Backbone.Model.extend({
|
||||
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
// load the user information
|
||||
loadUser(data.user.username); }});
|
||||
});
|
||||
|
||||
TS.EntryList = Backbone.Collection.extend({
|
||||
model: TS.Entry,
|
||||
|
||||
comparator: function(entry) { return entry.get('timestamp'); },
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, "ur");
|
||||
},
|
||||
|
||||
url: function() {
|
||||
return "/entries/" + TS.currentUser.get('username') + "/" + this.timeline.get('id');
|
||||
}
|
||||
});
|
||||
|
||||
TS.TimelineList = Backbone.Collection.extend({
|
||||
model: TS.Timeline,
|
||||
|
||||
url: function() {
|
||||
return "/timelines/" + TS.currentUser.get('username');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// ======== DEFINE VIEWS ========//
|
||||
|
||||
/* Entry view
|
||||
*/
|
||||
TS.EntryView = Backbone.View.extend({
|
||||
|
||||
model: TS.Entry,
|
||||
|
||||
events: {
|
||||
"dblclick div.mark" : "editMark",
|
||||
"dblclick div.timestamp" : "editTimestamp",
|
||||
"keypress .mark-input" : "updateOnEnter"
|
||||
"keypress .timestamp-input" : "updateOnEnter"
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'render', 'close');
|
||||
this.model.bind('change', this.render);
|
||||
this.model.view = this;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
$(this.el).html(ich.entry(this.model.toJSON()));
|
||||
return this;
|
||||
},
|
||||
|
||||
editMark: function() {
|
||||
$(this.el).addClass('edit-mark');
|
||||
this.$('.mark-input').focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
editTimestamp: function() {
|
||||
$(this.el).addClass('edit-timestamp');
|
||||
this.$('timestamp-input').focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.model.save({
|
||||
mark: this.$('.mark-input').val(),
|
||||
timestamp: this.$('.timestamp-input').val()});
|
||||
$(this.el).removeClass('edit-mark edit-timestamp');
|
||||
},
|
||||
|
||||
updateOnEnter: function(e) {
|
||||
if(e.keyCode == 13) this.close();
|
||||
}
|
||||
});
|
||||
|
||||
TS.TimelineView = Backbone.View.extend({
|
||||
|
||||
el: $("#timeline"),
|
||||
|
||||
model: TS.Timeline,
|
||||
|
||||
events: {
|
||||
"dblclick .timeline-id" : "editId",
|
||||
"dblclick .timeline-desc" : "editDesc",
|
||||
"keypress .timeline-id-input" : "updateOnEnter",
|
||||
"keypress .timeline-desc-input" : "updateOnEnter"
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'render', 'close');
|
||||
this.model.bind('change', this.render);
|
||||
this.model.view = this;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$('.timeline-id').html('( ' +
|
||||
this.model.get('id') + ' )');
|
||||
this.$('.timeline-desc').text(this.model.get('desc'));
|
||||
return this;
|
||||
},
|
||||
|
||||
editId: function() {
|
||||
$(this.el).addClass('edit-id');
|
||||
this.$('.timeline-id-input').focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
editDesc: function() {
|
||||
$(this.el).addClass('edit-desc');
|
||||
this.$('.timeline-desc-input').focus();
|
||||
return this;
|
||||
};
|
||||
|
||||
close: function() {
|
||||
this.model.save({
|
||||
id: this.$('timeline-id-input').val(),
|
||||
desc: this.$('.timeline-desc-input').val()});
|
||||
$(this.el).removeClass('.edit-id .edit-desc');
|
||||
},
|
||||
|
||||
updateOnEnter: function(e) {
|
||||
if (e.keyCode == 13) this.close();
|
||||
}
|
||||
});
|
||||
|
||||
TS.UserView = Backbone.View.extend({
|
||||
|
||||
el: $("user"),
|
||||
|
||||
model: TS.User,
|
||||
|
||||
events: {
|
||||
'dblclick .fullname': 'editFullname',
|
||||
'keypress .fullname-input': 'updateOnEnter'
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'render', 'close');
|
||||
this.model.bind('change', this.render);
|
||||
this.model.view = this;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$('.fullname').text(this.model.get('name'));
|
||||
this.$('.username').text(this.model.get('id'));
|
||||
},
|
||||
|
||||
editFullname: function() {
|
||||
$(this.el).addClass('.edit-fullname');
|
||||
this.$('.fullname-input').focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.model.set({name: this.$('.fullname-input').val()});
|
||||
this.model.save();
|
||||
$(this.el).removeClass('.edit-fullname');
|
||||
},
|
||||
|
||||
updateOnEnter: function(e) {
|
||||
if (keyCode == 13) this.close();
|
||||
}
|
||||
});
|
||||
|
||||
TS.currentUser = {};
|
||||
TS.currentUser.model = new TS.User({
|
||||
id: $("#user .username").text(),
|
||||
name: $("#user .fullname").text() });
|
||||
|
||||
TS.currentUser.view = new TS.UserView(TS.currentUser.model);
|
||||
|
||||
})
|
||||
|
||||
/* Read the user's credentials from the login form and perform
|
||||
* an AJAX request to the API to set the session cookie. */
|
||||
function login() {
|
||||
// lookup the login dialog elements
|
||||
var name = $("#login-name");
|
||||
@ -71,310 +231,6 @@ function login() {
|
||||
},
|
||||
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
// load the user information and hide the login dialog
|
||||
loadUser(name.val());
|
||||
$("#login-dialog").dialog("close");
|
||||
TS.currentUser = new TS.User(); // TODO
|
||||
}});
|
||||
}
|
||||
|
||||
function toggleSignUp(event) {
|
||||
var signUpCB = $("#signup-checkbox");
|
||||
|
||||
if (signUpCB.attr("checked")) {
|
||||
loginTop = $("#login-dialog").dialog("widget").offset().top;
|
||||
$("#login-dialog").animate({height: 350}, 500);
|
||||
$("#login-dialog").dialog("widget").animate({top: loginTop - 200}, 500);
|
||||
$(".signup").slideDown("slow");
|
||||
} else {
|
||||
$("#login-dialog").animate({height: 180}, 500);
|
||||
$("#login-dialog").dialog("widget").animate({top: loginTop}, 500);
|
||||
$(".signup").slideUp("slow");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function signUp() {
|
||||
|
||||
}
|
||||
|
||||
/* End the current user session and expire any session credentials we
|
||||
* have aquired. */
|
||||
function logout(event) {
|
||||
alert("TODO: log user out via AJAX.");
|
||||
// TODO: wipe username, timeline, entry variables and displays
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/* Load and display the user's information and timelines. */
|
||||
function loadUser(username) {
|
||||
|
||||
// call the user_summary API function
|
||||
$.ajax({
|
||||
url: "/ts_api/app/user_summary/" + username,
|
||||
type: "GET",
|
||||
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
// set the user variable
|
||||
user = data.user;
|
||||
|
||||
// set the timelines variable
|
||||
timelines = data.timelines;
|
||||
|
||||
// update the user id display
|
||||
$("#fullname").text(user.name);
|
||||
$("#username").text("- " + user.username);
|
||||
|
||||
// pre-populate the editable user-info fields
|
||||
$("#fullname-input").val(user.name);
|
||||
$("#email-input").val(user.email);
|
||||
|
||||
// set the active timeline to the first in the list
|
||||
// TODO: implement a mechanism to remember the last used timeline
|
||||
// on the server side and respond to that here.
|
||||
activeTimeline = timelines[0];
|
||||
|
||||
// update the timeline display
|
||||
$("#timeline-name").text(activeTimeline.timeline_id + " |");
|
||||
$("#timeline-desc").text(activeTimeline.description);
|
||||
$("#timeline-desc-input").val(activeTimeline.description);
|
||||
|
||||
// TODO: populate the drop-down list for the available timeline
|
||||
// choices
|
||||
|
||||
// load the entries for this timeline
|
||||
loadEntries(user, activeTimeline, "new")
|
||||
},
|
||||
|
||||
error: function(jqXHR, textStatus, error) {
|
||||
// TODO
|
||||
alert("TODO: handle error for user load.")
|
||||
alert(jqXHR.responseText)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Read entries for a timeline. */
|
||||
function loadEntries(user, timeline, order) {
|
||||
|
||||
// call the API list_entries function via AJAX
|
||||
$.ajax({
|
||||
url: "/ts_api/entries/" + user.username + "/"
|
||||
+ timeline.timeline_id + "?order=asc&start="
|
||||
+ currentEntryOffset + "&length=" + loadLength,
|
||||
type: "GET",
|
||||
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
entries = data.entries;
|
||||
|
||||
if (order == "new") {
|
||||
// reverse the list so that the oldest are first
|
||||
entries.reverse()
|
||||
|
||||
// push the entries onto the page
|
||||
displayNewerEntries(entries)
|
||||
} else {
|
||||
displayOlderEntries(entries)
|
||||
}
|
||||
|
||||
// update our current offset
|
||||
currentEntryOffset += loadLength;
|
||||
},
|
||||
|
||||
error: function(jqXHR, textStatus, error) {
|
||||
alert(jqXHR.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Push the entries onto the top of the entry display. This function assumes
|
||||
* that the entries are sorted by id, ascending (0, 1, 2). Each new entry goes
|
||||
* on top, pushing down all existing entries. So the first entry in the
|
||||
* argument list ends up at the bottom of the new entries. */
|
||||
function displayNewerEntries(entries) {
|
||||
|
||||
// for each entry
|
||||
_.each(entries, function(entry) {
|
||||
|
||||
// remove the existing top-entry designation
|
||||
$(".top-entry").removeClass("top-entry");
|
||||
|
||||
// create the new entry from the entry template (see ICanHas.js)
|
||||
var entryElem = ich.entry(entry);
|
||||
|
||||
// mark the new entry as the current top-entry
|
||||
entryElem.addClass("top-entry");
|
||||
|
||||
// push the entry on after the new-entry bar (on top of existing
|
||||
// entries).
|
||||
newEntryBar.after(entryElem);
|
||||
|
||||
// TODO: animate? If so, may need to pause after each to allow
|
||||
// some animation to take place. Going for a continuous push-down
|
||||
// stack effect.
|
||||
});
|
||||
}
|
||||
|
||||
/* Push the entries onto the bottom of the entry display. The function assumes
|
||||
* that the entries are sorted by id, descending (2, 1, 0). Each new entry goes
|
||||
* at the bottom of the stack of entries, extending the stack downwards. So the
|
||||
* last entry in the list is at the very bottom of the stack. */
|
||||
function displayOlderEntries(entries) {
|
||||
|
||||
// for each entry
|
||||
_.each(entries, function(entry) {
|
||||
|
||||
// create the new entry from the template (ICanHas.js)
|
||||
var entryElem = ich.entry(entry);
|
||||
|
||||
// place the entry on top of the bottom of the page
|
||||
moreEntriesBar.before(entryElem);
|
||||
|
||||
// perhaps animate, see comments at the end of displayNewerEntries()
|
||||
});
|
||||
}
|
||||
|
||||
/* Update the user information based on the editable user-info panel. */
|
||||
function updateUser(event) {
|
||||
alert("TODO: update user via AJAX.");
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/* Show the change timeline menu. */
|
||||
function showTimelineMenu(event) {
|
||||
alert("TODO: show other timelines via a popup menu");
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/* Update the timeline details based on the editable timeline-info panel. */
|
||||
function updateTimeline(event) {
|
||||
var desc = $("#timeline-desc-input").val();
|
||||
|
||||
$.ajax({url: "/ts_api/timelines/" + user.username
|
||||
+ "/" + activeTimeline.timeline_id,
|
||||
type: "POST",
|
||||
data: JSON.stringify({desc: desc, created: activeTimeline.created}),
|
||||
|
||||
error: function(jqXHR, textStatus, error) {
|
||||
// TODO: better error handling
|
||||
alert("Error updating timeline: \n" + jqXHR.responseText); },
|
||||
|
||||
success: function(data, testStatus, jqXHR) {
|
||||
// TODO: check for appropriate data.status value
|
||||
|
||||
// update display
|
||||
$("#timeline-desc").text(data.timeline.description);
|
||||
}});
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/* Create a new entry based on the user's input in the new-entry panel. */
|
||||
function newEntry(event) {
|
||||
|
||||
var mark = $("#new-entry-input").val();
|
||||
var notes = $("#new-notes-input").val();
|
||||
var timestamp = getUTCTimestamp(); // timestamp is handled on client.
|
||||
// This is important. It means that the timestamps are completely in the
|
||||
// hands of the user. Timestamps from this service can not be considered
|
||||
// secure. This service is not a punch-card, but a time-tracker, voluntary
|
||||
// and editable by the user.
|
||||
|
||||
var payload = JSON.stringify(
|
||||
{ mark: mark, notes: notes, timestamp: timestamp });
|
||||
|
||||
$.ajax({url: "/ts_api/entries/" + user.username
|
||||
+ "/" + activeTimeline.timeline_id,
|
||||
type: "PUT",
|
||||
data: payload,
|
||||
|
||||
error: function(jqXHR, textStatus, error) {
|
||||
// TODO: better error handling.
|
||||
alert("Error creating entry: \n" + jqXHR.responseText); },
|
||||
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
// TODO: check that data.status == "ok"
|
||||
|
||||
// grab the entry data
|
||||
var newEntry = data.entry;
|
||||
|
||||
// push the entry on top of the stack
|
||||
displayNewerEntries([newEntry]);
|
||||
|
||||
// clear the input
|
||||
$("#new-entry-input").val("");
|
||||
$("#new-notes-input").val("");
|
||||
}});
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function toggleEditEntry(event, entryId) {
|
||||
$("#entry-" + entryId + " .entry-display").toggle();
|
||||
$("#entry-" + entryId + " .entry-edit").toggle();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function updateEntry(event, entryId) {
|
||||
|
||||
var mark = $("#entry-" + entryId + "-mark-input").val();
|
||||
var notes = $("#entry-" + entryId + "-notes-input").val();
|
||||
var timestamp = getUTCTimestamp(); // TODO: define and read from input element
|
||||
|
||||
var payload = JSON.stringify(
|
||||
{ mark: mark, notes: notes, timestamp: timestamp });
|
||||
|
||||
$.ajax({url: "/ts_api/entries/" + user.username
|
||||
+ "/" + activeTimeline.timeline_id
|
||||
+ "/" + entryId,
|
||||
type: "POST",
|
||||
data: payload,
|
||||
|
||||
error: function(jqXHR, textStatus, error) {
|
||||
// TODO: error handling
|
||||
alert("Error updating entry: \n" + jqXHR.responseText); },
|
||||
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
// TODO: check that data.status is appropriate
|
||||
|
||||
// update the entry display
|
||||
$("#entry-" + entryId + " .entry-mark").text(data.entry.mark);
|
||||
$("#entry-" + entryId + " .entry-notes").text(data.entry.notes);
|
||||
}});
|
||||
|
||||
toggleEditEntry(event, entryId);
|
||||
}
|
||||
|
||||
/* Delete an entry. */
|
||||
function deleteEntry(event, entryId) {
|
||||
$.ajax({url: "/ts_api/entries/" + user.username
|
||||
+ "/" + activeTimeline.timeline_id
|
||||
+ "/" + entryId,
|
||||
type: "DELETE",
|
||||
|
||||
error: function(jqXHR, textStatus, error) {
|
||||
// TODO: error handling
|
||||
alert("Error updating entry: \n" + jqXHR.responseText); },
|
||||
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
$("#entry-" + entryId).slideUp('slow',
|
||||
function() {$("#entry-" + entryId).remove(); });
|
||||
}});
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/* Generate a UTC timestamp string in ISO format.
|
||||
* Javascript 1.8.5 has Date.toJSON(), which is equivalent to this function,
|
||||
* but is only supported in Firefox 4. */
|
||||
function getUTCTimestamp() {
|
||||
var d = new Date();
|
||||
|
||||
function pad(n){return n<10 ? '0'+n : n}
|
||||
|
||||
return d.getUTCFullYear()+'-'
|
||||
+ pad(d.getUTCMonth()+1)+'-'
|
||||
+ pad(d.getUTCDate())+'T'
|
||||
+ pad(d.getUTCHours())+':'
|
||||
+ pad(d.getUTCMinutes())+':'
|
||||
+ pad(d.getUTCSeconds())+'Z';
|
||||
}
|
||||
|
36
www/test.html
Normal file
36
www/test.html
Normal file
@ -0,0 +1,36 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Testing Backbone.js</title>
|
||||
<link rel="stylesheet" media="screen" href="css/dot-luv/jquery-ui-1.8.10.custom.css" type="text/css"/>
|
||||
<script type="text/javascript" src="/js/jquery-1.5.min.js"></script>
|
||||
<script type="text/javascript" src="/js/jquery-ui-1.8.10.custom.min.js"></script>
|
||||
<script type="text/javascript" src="/js/underscore-min.js"></script>
|
||||
<script type="text/javascript" src="/js/ICanHaz.js"></script>
|
||||
<script type="text/javascript" src="/js/backbone-min.js"></script>
|
||||
<script type="text/javascript" src="/js/test.js"></script>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="user">
|
||||
<div class="username">username</div>
|
||||
<div class="fullname">fullname</div>
|
||||
</div>
|
||||
|
||||
<div id="login-dialog" title="Login">
|
||||
<form>
|
||||
<fieldset>
|
||||
<label for="login-name">Username:</label>
|
||||
<input type="text" name="login-name" id="login-name"
|
||||
class="text ui-widget-content ui-corner-all"></input>
|
||||
<label for="login-password">Password:</label>
|
||||
<input type="password" name="login-password" id="login-password"
|
||||
class="text ui-widget-content ui-corner-all"></input>
|
||||
</fieldset>
|
||||
</form>
|
||||
<p class="validate-tips"></p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,11 +1,11 @@
|
||||
ebin_dir = /home/jdbernard/projects/timestamper/web-app/ebin
|
||||
ebin_dir = /home/jdbernard/projects/jdb-labs/timestamper/web-app/ebin
|
||||
#include_dir = /home/jdbernard/projects/timestamper/web-app/src
|
||||
|
||||
runmod = timestamper_dev
|
||||
|
||||
<server timestamper.jdb-labs-local>
|
||||
<server timestamper-local>
|
||||
port = 8000
|
||||
listen = 127.0.0.1
|
||||
docroot = /home/jdbernard/projects/timestamper/web-app/www
|
||||
docroot = /home/jdbernard/projects/jdb-labs/timestamper/web-app/www
|
||||
appmods = ts_api
|
||||
</server>
|
||||
|
Loading…
x
Reference in New Issue
Block a user