* Changed the cookie Path value to allow the cookie to be reused for the domain, not just ths `/ts_api` path. This allows the user to refresh the page and reuse their existing session as long as it is not stale. * Fixed a bug in the `ts_json:ejson_to_record_strict/2` function. It was expecting a record out of `ts_json:ejson_to_record/2` but that function returns a tuple with the record and extended data. Because of the way `ejson_to_record_strict` uses case statements to check for specific values it was still passing the parsed record and data through, but all the checks were being bypassed. * Fixed bugs in the `index.yaws` bootstrap code for the case where the user already has a valid session. * Added `urlRoot` functions to the Backbone model definitions. * Changed the behavior of the new entry creation method. We were trying to fetch just updated attributes from the server for the model we created, but we were pulling all the entries due to the URL backbone was using. This led to the new client-side model having all the previous entry models as attributes. Ideally we would fix the fetch so that only the one model is requested from the server, but we run into a catch-22 because the lookup id is not know by the client as it is generated on the server-side. For now I have changed this behavior so that we still pull all entries, but we pull them into the collection. The collection is then smart enough to update the entries that have changed (namely the new one). The server returns the newly created entry attributes in response to the POST request that the client makes initially, so when I have more time to work on this I plan to do away with the fetch after create, and just pull in the data from the server's response. * Changed formatting.
176 lines
7.2 KiB
Plaintext
176 lines
7.2 KiB
Plaintext
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>TimeStamper - Simple Time Tracking</title>
|
|
<link href='//fonts.googleapis.com/css?family=Anonymous+Pro|Arvo|Bentham|Cantarell|Josefin+Sans' rel='stylesheet' 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 -->
|
|
<!--
|
|
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
|
|
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js"></script>
|
|
-->
|
|
<!-- DEV -->
|
|
<script type="text/javascript" src="js/jquery-1.5.js"></script>
|
|
|
|
<script type="text/javascript" src="js/showdown.js"></script>
|
|
<script type="text/javascript" src="js/underscore.js"></script>
|
|
<script type="text/javascript" src="js/ICanHaz.js"></script>
|
|
<script type="text/javascript" src="js/backbone.js"></script>
|
|
<script type="text/javascript" src="js/ts.js"></script>
|
|
<script type="text/javascript">
|
|
<erl>
|
|
out(YArg) ->
|
|
Session = ts_api_session:get_session(YArg),
|
|
case Session of not_logged_in -> {html, "//not logged in"}; session_expired -> {html, "//session expired"};
|
|
_S ->
|
|
Username = element(2, Session),
|
|
|
|
% get the user
|
|
UserResp = ts_api:get_user(YArg, Username),
|
|
{content, _, UserJSON} = lists:keyfind(content, 1, UserResp),
|
|
|
|
UserRecord = ts_user:lookup(Username),
|
|
|
|
% get the timelines
|
|
TimelineResp = ts_api:list_timelines(YArg, Username),
|
|
{content, _, TimelineListJSON} = lists:keyfind(content, 1, TimelineResp),
|
|
|
|
% get the last used timeline if there is one.
|
|
SelectedTimeline = case ts_ext_data:get_property(Username, last_timeline) of
|
|
|
|
not_set -> ts_timeline:list(Username, 0, 1);
|
|
T -> T
|
|
end,
|
|
|
|
% get entries for this timeline
|
|
EntriesResp =
|
|
ts_api:list_entries(YArg, Username, SelectedTimeline),
|
|
{content, _, EntryListJSON} = lists:keyfind(content, 1, EntriesResp),
|
|
|
|
{html, f(
|
|
"function bootstrap() {~n"
|
|
" var data = {};~n"
|
|
" data.user = ~s;~n"
|
|
" data.timelines = ~s;~n"
|
|
" data.initialTimelineId = ~p;~n"
|
|
" data.entries = ~s;~n"
|
|
" return data;~n"
|
|
"};",
|
|
[lists:flatten(UserJSON), lists:flatten(TimelineListJSON),
|
|
SelectedTimeline, lists:flatten(EntryListJSON)])}
|
|
end.
|
|
</erl>
|
|
</script>
|
|
<script type="text/html" id="userTemplate">
|
|
<div class="fullname">{{name}}</div>
|
|
<input class="fullname-input" type="text" value="{{name}}"/></div>
|
|
<div class="user-menu">
|
|
<div class="username"> - {{id}}</div>
|
|
<ul class="user-menu-items">
|
|
<li><a href="#">logout</a></li>
|
|
<li><a href="#">user info</a></li>
|
|
</ul>
|
|
</div>
|
|
</script>
|
|
<script type="text/html" id="timelineTemplate">
|
|
<span class="timeline-desc">{{description}}</span>
|
|
<input class="timeline-desc-input" type="text" value='{{description}}'/>
|
|
<div class="drop-menu">
|
|
<div class="timeline-id">( {{id}} )</div>
|
|
<input class="timeline-id-input" type="text" value='{{id}}'/>
|
|
<ul class="drop-menu-items">
|
|
<li class="new-timeline-link"><a href="#">
|
|
<img src="img/round_plus_icon_16.png"/>new</a></li>
|
|
</ul>
|
|
</div>
|
|
</script>
|
|
<script type="text/html" id="timelineLinkTemplate">
|
|
<li class="timeline-link"><a href="#">{{id}}</a></li>
|
|
</script>
|
|
<script type="text/html" id="entryTemplate">
|
|
<div class="mark">
|
|
<img class="expand-entry" src="img/br_down_icon_16.png"/>
|
|
<img class="collapse-entry" src="img/br_up_icon_16.png"/>
|
|
<span>{{mark}}</span>
|
|
</div>
|
|
<input class="mark-input" type="text" value="{{mark}}"/>
|
|
<div class="timestamp">{{start}}</div>
|
|
<input class="timestamp-input" type="text" value="{{timestamp}}"/>
|
|
<div class="duration">{{duration}}</div>
|
|
<div class="notes">
|
|
<div class="notes-text">{{notes}}</div>
|
|
<textarea class="notes-input" rows="10">{{notes}}</textarea>
|
|
</div>
|
|
</script>
|
|
<script type="text/html" id="daySeparatorTemplate">
|
|
<div class="day-seperator">
|
|
<h4 class="mark">{{separatorLabel}}</h4>
|
|
<h5 class="timestamp">start</h5>
|
|
<h5 class="duration">duration</h5>
|
|
</div>
|
|
</script>
|
|
|
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<!-- == LOGIN FORM == -->
|
|
<div id="login" class="hidden dialog">
|
|
<div class="container">
|
|
<h2>Login</h2>
|
|
<div><label>Username: </label><input type="text" id="username-input"></input></div>
|
|
<div><label>Password: </label><input type="password" id="password-input"></input></div>
|
|
<div class="button-panel">
|
|
<span class='validate-tips'></span>
|
|
<div id="login-button" class="dialog-button"><a href="#">login</a></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- == NEW TIMELINE FORM == -->
|
|
<div id="new-timeline" class="hidden dialog">
|
|
<div class="container">
|
|
<h2>Create a new timeline:</h2>
|
|
<div><label>Timeline ID: </label><input type="text" id="new-timeline-id"></input></div>
|
|
<div><label>Description: </label><input type="text" id="new-timeline-desc"></input></div>
|
|
<div class="button-panel">
|
|
<span class='validate-tips'></span>
|
|
<div id="new-timeline-create" class="dialog-button"><a href="#">create</a></div>
|
|
<div id="new-timeline-cancel" class="dialog-button"><a href="#">cancel</a></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="top">
|
|
|
|
<!-- == TIMELINE == -->
|
|
<div id="timeline"><!-- replaced on login by app -->
|
|
<div class="timeline-desc">Login</div>
|
|
</div>
|
|
|
|
<!-- == USER == -->
|
|
<div id="user"><!-- replaced on login by app -->
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div id="entry-list">
|
|
<div id="new-entry">
|
|
<input id="new-entry-input" class="mark-input"
|
|
placeholder="Start a new task..." type="text" />
|
|
</div>
|
|
|
|
<div id="entries"></div>
|
|
</div>
|
|
|
|
<div class="footer">
|
|
Copyright 2011 <a href="http://www.jdb-labs.com"><span class="logo">JDB Labs</span> LLC.</a>
|
|
</div>
|
|
|
|
</body>
|
|
|
|
</html>
|