Merge branch 'client-redesign'
Conflicts: yaws.dev.conf
9
Makefile
@ -3,22 +3,23 @@ BEAMS = $(MODS:src/%.erl=ebin/%.beam)
|
||||
TEST_MODS = $(wildcard test/*.erl)
|
||||
TEST_BEAMS = $(TEST_MODS:test/%.erl=test/%.beam)
|
||||
TS_ROOT=/usr/lib/yaws/jdb-labs/timestamper
|
||||
CWD = `pwd`
|
||||
|
||||
default: compile
|
||||
|
||||
all : compile test
|
||||
|
||||
compile : $(BEAMS)
|
||||
compile : init $(BEAMS)
|
||||
|
||||
compile-test : $(TEST_BEAMS)
|
||||
compile-test : init $(TEST_BEAMS)
|
||||
|
||||
test : start-test-server run-test stop-test-server
|
||||
|
||||
test-shell : compile compile-test
|
||||
test-shell : compile compile-test config-yaws-dev
|
||||
@echo Starting an interactive YAWS shell with test paths loaded.
|
||||
@yaws -i --pa test --id test_inst
|
||||
|
||||
run-test : compile compile-test
|
||||
run-test : compile compile-test config-yaws-dev
|
||||
@erl -pa ./ebin -pa ./test -run timestamper_api_tests test -run init stop -noshell
|
||||
|
||||
start-test-server :
|
||||
|
BIN
db/test/ts_entry.DCL
Normal file
4
doc/features.todo.txt
Normal file
@ -0,0 +1,4 @@
|
||||
- Switch to local storage if unable to reach the server, sync when server is
|
||||
available.
|
||||
- Provide full-text search on timestamp marks and notes. Use Lucene in a
|
||||
seperate process? Build our own Erlang indexing code?
|
4
doc/issues/0000tn4.rst
Normal file
@ -0,0 +1,4 @@
|
||||
Refactor models and views.
|
||||
==========================
|
||||
|
||||
Try to find the behavior that is common to mobile and desktop versions.
|
2
doc/issues/desktop/0000fs5.rst
Normal file
@ -0,0 +1,2 @@
|
||||
Add UI for note taking.
|
||||
=======================
|
9
doc/issues/desktop/0001fs5.rst
Normal file
@ -0,0 +1,9 @@
|
||||
Add Markdown converter for notes.
|
||||
=================================
|
||||
|
||||
Brief description.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: 2011-05-15
|
||||
========= ==========
|
11
doc/issues/desktop/0002bs5.rst
Normal file
@ -0,0 +1,11 @@
|
||||
Duration mis-set on new entries.
|
||||
================================
|
||||
|
||||
Fix the duration bug when adding new events. Need to set the nextModel for
|
||||
the previously 'current' timestamp and set the nextModel of the new timestamp
|
||||
to 'null'
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: 2011-05-15
|
||||
========= ==========
|
10
doc/issues/desktop/0003tn3.rst
Normal file
@ -0,0 +1,10 @@
|
||||
Generate day seperators.
|
||||
========================
|
||||
|
||||
When generating EventViews in the EventListView, we need to automatically
|
||||
create and insert day seperators (see prototype).
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
10
doc/issues/desktop/0004bn5.rst
Normal file
@ -0,0 +1,10 @@
|
||||
Fix UI for tasks with a duration a day or longer.
|
||||
=================================================
|
||||
|
||||
Tasks that are extremely long-running can overflow the space set aside for
|
||||
the *Duration* column.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
14
doc/issues/desktop/0005bs2.rst
Normal file
@ -0,0 +1,14 @@
|
||||
Fix user menu UI.
|
||||
=================
|
||||
|
||||
UI for user menu does not work.
|
||||
|
||||
Resolution
|
||||
----------
|
||||
|
||||
UI menu changed to be displayed to the right of the username, in the empty black space vailable.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: 2011-06-01
|
||||
========= ==========
|
9
doc/issues/desktop/0006bn2.rst
Normal file
@ -0,0 +1,9 @@
|
||||
Fix timeline menu UI.
|
||||
=====================
|
||||
|
||||
UI for timeline menu does not work.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
9
doc/issues/desktop/0007tn2.rst
Normal file
@ -0,0 +1,9 @@
|
||||
Implement timeline selection.
|
||||
=============================
|
||||
|
||||
Allow the user to change timelines.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
15
doc/issues/desktop/0008ts2.rst
Normal file
@ -0,0 +1,15 @@
|
||||
Create UI for timeline creation.
|
||||
================================
|
||||
|
||||
There should be a way through the interface for a user to create a new
|
||||
timeline.
|
||||
|
||||
Resolution
|
||||
----------
|
||||
|
||||
Abstracted the login dialog CSS to support other dialogs of the same look and feel. Used this to create a dialog for creating new timelines.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: 2011-06-01
|
||||
========= ==========
|
9
doc/issues/desktop/0009tn3.rst
Normal file
@ -0,0 +1,9 @@
|
||||
Implement a date-picker for start time.
|
||||
=======================================
|
||||
|
||||
Use a custom jQuery UI theme?
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
18
doc/issues/desktop/0010tn4.rst
Normal file
@ -0,0 +1,18 @@
|
||||
Implement UI for entry re-order when chronological order changes.
|
||||
=================================================================
|
||||
|
||||
The UI should re-order the EntryList display when the chronological of the entries
|
||||
changes based on user input.
|
||||
|
||||
Two ways to do this spring to mind:
|
||||
|
||||
1. ``slideUp`` the EntryView at it's original position, find the new position,
|
||||
move it in the DOM and ``slideDown`` the element into view.
|
||||
2. Detach the element from the list, position it absolutely, animate it to it's new
|
||||
absolute position (based on the position of its new neighbors) and re-insert it
|
||||
into the list.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
10
doc/issues/desktop/0011fn5.rst
Normal file
@ -0,0 +1,10 @@
|
||||
Create a subtle alternating background for EntryViews
|
||||
=====================================================
|
||||
|
||||
The goal is to visually tie together elements in the same row, and subtley
|
||||
distinquish each row from its neighbors.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
9
doc/issues/desktop/0012fn6.rst
Normal file
@ -0,0 +1,9 @@
|
||||
Create tooltip/some help system.
|
||||
================================
|
||||
|
||||
Need some way to make actions discoverable and easy to understand.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
10
doc/issues/desktop/0013fn5.rst
Normal file
@ -0,0 +1,10 @@
|
||||
Create a real-time tick-tock for the current entry duration.
|
||||
============================================================
|
||||
|
||||
Have some visible indication that the display is being updated as time passes.
|
||||
Blink the field, or just the units.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
10
doc/issues/desktop/0014fn6.rst
Normal file
@ -0,0 +1,10 @@
|
||||
Automatic code highlighting.
|
||||
============================
|
||||
|
||||
Look at content in ``<pre>`` and ``<code>`` blocks and see if we can highlight it.
|
||||
Planning to use Highlight.js to do this.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
9
doc/issues/desktop/0015tn2.rst
Normal file
@ -0,0 +1,9 @@
|
||||
Create new timeline button in timeline menu.
|
||||
============================================
|
||||
|
||||
Need a button to trigger the new timeline dialog.
|
||||
|
||||
========= ==========
|
||||
Created: 2011-06-01
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
7
doc/issues/desktop/0016bn5.rst
Normal file
@ -0,0 +1,7 @@
|
||||
Duration of next previous entries are not updated when a timestamp is updated.
|
||||
==============================================================================
|
||||
|
||||
========= ==========
|
||||
Created: 2011-06-01
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
7
doc/issues/desktop/0017tn2.rst
Normal file
@ -0,0 +1,7 @@
|
||||
Implement timeline creation.
|
||||
============================
|
||||
|
||||
========= ==========
|
||||
Created: 2011-05-15
|
||||
Resolved: YYYY-MM-DD
|
||||
========= ==========
|
1
doc/issues/mobile/0000tn5.rst
Normal file
@ -0,0 +1 @@
|
||||
Prototype out mobile workflow.
|
34
doc/model.txt
Normal file
@ -0,0 +1,34 @@
|
||||
Data
|
||||
----
|
||||
UserModel
|
||||
TimelineModel
|
||||
TimelineListModel
|
||||
EntryListModel
|
||||
EntryModel
|
||||
|
||||
|
||||
Views
|
||||
-----
|
||||
EntryView
|
||||
NewEntryInput
|
||||
TimelineListView
|
||||
TimelineView
|
||||
UserView
|
||||
|
||||
|
||||
Data Dependencies
|
||||
-----------------
|
||||
UserModel: none
|
||||
TimelineModel: none
|
||||
TimelineListModel: UserModel
|
||||
EntryModel: none
|
||||
EntryListModel: TimelineModel
|
||||
|
||||
|
||||
View Dependencies
|
||||
-----------------
|
||||
UserView: UserModel
|
||||
TimelineView: TimelineModel, UserView
|
||||
TimelineListView: TimelineListModel, UserView
|
||||
EntryView: EntryModel, EntryListView
|
||||
EntryListView: EntryListModel, TimelineView
|
BIN
doc/model.xcf
Normal file
29
doc/todo.rst
Normal file
@ -0,0 +1,29 @@
|
||||
Current
|
||||
=======
|
||||
|
||||
Upcoming
|
||||
========
|
||||
- Generate day seperators
|
||||
- Fix UI for tasks with a duration a day or longer
|
||||
- Fix hover UI for user menu
|
||||
- Fix hover UI for timeline menu
|
||||
- Test (implement?) timeline selection
|
||||
- Add UI for timeline creation
|
||||
- Change the UI for editing the start-time. Use a date-picker (custom jQuery
|
||||
UI theme?)
|
||||
- Fix UI for timestamp edits which change the order of events in the timeline.
|
||||
- Create a light, alternating background for entries
|
||||
- Add hover-enabled icons for editing entries/showing notes
|
||||
- Create tooltips.
|
||||
- Create a realtime tick-tock for the duration of the current item.
|
||||
- Mobile version of the app.
|
||||
- Refactor code, seperate out reusable bits for mobile version.
|
||||
- Automatic code-highlighting (Highlight.js)
|
||||
|
||||
Done
|
||||
====
|
||||
- Add UI for note-taking
|
||||
- Add Markdown converter for notes.
|
||||
- Fix the duration bug when adding new events. Need to set the nextModel for
|
||||
the previously 'current' timestamp and set the nextModel of the new timestamp
|
||||
to 'null'
|
@ -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.
|
||||
|
155
src/ts_api.erl
@ -34,19 +34,18 @@ out(YArg) ->
|
||||
dispatch_request(YArg, _Session, []) -> make_json_404(YArg, [{see_docs, "/ts_api_doc"}]);
|
||||
|
||||
dispatch_request(YArg, Session, [H|T]) ->
|
||||
Param = path_element_to_atom(H),
|
||||
|
||||
case {Session, Param} of
|
||||
{_, login} -> do_login(YArg);
|
||||
{_, logout} -> do_logout(YArg);
|
||||
case {Session, H} of
|
||||
{_, "login"} -> do_login(YArg);
|
||||
{_, "logout"} -> do_logout(YArg);
|
||||
|
||||
{not_logged_in, _} -> make_json_401(YArg);
|
||||
{session_expired, _} -> make_json_401(YArg, [{status, "session expired"}]);
|
||||
{"not_logged_in", _} -> make_json_401(YArg);
|
||||
{"session_expired", _} -> make_json_401(YArg, [{error, "session expired"}]);
|
||||
|
||||
{_S, app} -> dispatch_app(YArg, Session, T);
|
||||
{_S, users} -> dispatch_user(YArg, Session, T);
|
||||
{_S, timelines} -> dispatch_timeline(YArg, Session, T);
|
||||
{_S, entries} -> dispatch_entry(YArg, Session, T);
|
||||
{_S, "app"} -> dispatch_app(YArg, Session, T);
|
||||
{_S, "users"} -> dispatch_user(YArg, Session, T);
|
||||
{_S, "timelines"} -> dispatch_timeline(YArg, Session, T);
|
||||
{_S, "entries"} -> dispatch_entry(YArg, Session, T);
|
||||
{_S, _Other} -> make_json_404(YArg, [{see_docs, "/ts_api_doc/"}])
|
||||
end.
|
||||
|
||||
@ -59,7 +58,7 @@ dispatch_app(YArg, Session, Params) ->
|
||||
|
||||
{'GET', ["user_summary", UsernameStr]} ->
|
||||
case {Session#ts_api_session.username,
|
||||
path_element_to_atom(UsernameStr)} of
|
||||
UsernameStr} of
|
||||
|
||||
{Username, Username} -> get_user_summary(YArg, Username);
|
||||
_ -> make_json_401(YArg)
|
||||
@ -75,10 +74,9 @@ dispatch_app(YArg, Session, Params) ->
|
||||
% -------- Dispatch for /user -------- %
|
||||
|
||||
dispatch_user(YArg, Session, []) ->
|
||||
dispatch_user(YArg, Session, [atom_to_list(Session#ts_api_session.username)]);
|
||||
dispatch_user(YArg, Session, [Session#ts_api_session.username]);
|
||||
|
||||
dispatch_user(YArg, Session, [H]) ->
|
||||
Username = path_element_to_atom(H),
|
||||
dispatch_user(YArg, Session, [Username]) ->
|
||||
HTTPMethod = (YArg#arg.req)#http_request.method,
|
||||
|
||||
% compare to the logged-in user
|
||||
@ -97,8 +95,7 @@ dispatch_user(YArg, Session, [H]) ->
|
||||
dispatch_timeline(YArg, _Session, []) ->
|
||||
make_json_404(YArg, [{see_docs, "/ts_api_doc/timelines.html"}]);
|
||||
|
||||
dispatch_timeline(YArg, Session, [UrlUsername|_T] = PathElements) ->
|
||||
Username = path_element_to_atom(UrlUsername),
|
||||
dispatch_timeline(YArg, Session, [Username|_T] = PathElements) ->
|
||||
|
||||
case Session#ts_api_session.username of
|
||||
Username -> dispatch_timeline(YArg, PathElements);
|
||||
@ -106,8 +103,7 @@ dispatch_timeline(YArg, Session, [UrlUsername|_T] = PathElements) ->
|
||||
end.
|
||||
|
||||
% just username, list timelines
|
||||
dispatch_timeline(YArg, [UrlUsername]) ->
|
||||
Username = path_element_to_atom(UrlUsername),
|
||||
dispatch_timeline(YArg, [Username]) ->
|
||||
HTTPMethod = (YArg#arg.req)#http_request.method,
|
||||
|
||||
case HTTPMethod of
|
||||
@ -115,9 +111,7 @@ dispatch_timeline(YArg, [UrlUsername]) ->
|
||||
_Other -> make_json_405(YArg, [{see_docs, "/ts_api_doc/timelines.html"}])
|
||||
end;
|
||||
|
||||
dispatch_timeline(YArg, [UrlUsername, UrlTimelineId]) ->
|
||||
Username = path_element_to_atom(UrlUsername),
|
||||
TimelineId = path_element_to_atom(UrlTimelineId),
|
||||
dispatch_timeline(YArg, [Username, TimelineId]) ->
|
||||
HTTPMethod = (YArg#arg.req)#http_request.method,
|
||||
|
||||
case HTTPMethod of
|
||||
@ -136,34 +130,29 @@ dispatch_timeline(YArg, _Other) ->
|
||||
dispatch_entry(YArg, _Session, []) ->
|
||||
make_json_404(YArg, [{see_docs, "/ts_aip_doc/entries.html"}]);
|
||||
|
||||
dispatch_entry(YArg, Session, [UrlUsername|_T] = PathElements) ->
|
||||
Username = path_element_to_atom(UrlUsername),
|
||||
dispatch_entry(YArg, Session, [Username|_T] = PathElements) ->
|
||||
|
||||
case Session#ts_api_session.username of
|
||||
Username -> dispatch_entry(YArg, PathElements);
|
||||
_Other -> make_json_404(YArg, [{see_docs, "/ts_api_doc/entries.html"}])
|
||||
end.
|
||||
|
||||
dispatch_entry(YArg, [UrlUsername, UrlTimelineId]) ->
|
||||
Username = path_element_to_atom(UrlUsername),
|
||||
TimelineId = path_element_to_atom(UrlTimelineId),
|
||||
dispatch_entry(YArg, [Username, TimelineId]) ->
|
||||
HTTPMethod = (YArg#arg.req)#http_request.method,
|
||||
|
||||
case HTTPMethod of
|
||||
'GET' -> list_entries(YArg, Username, TimelineId);
|
||||
'PUT' -> put_entry(YArg, Username, TimelineId);
|
||||
'POST' -> post_entry(YArg, Username, TimelineId);
|
||||
_Other -> make_json_405(YArg, [{see_docs, "/ts_api_doc/entries.html"}])
|
||||
end;
|
||||
|
||||
dispatch_entry(YArg, [UrlUsername, UrlTimelineId, UrlEntryId]) ->
|
||||
Username = path_element_to_atom(UrlUsername),
|
||||
TimelineId = path_element_to_atom(UrlTimelineId),
|
||||
dispatch_entry(YArg, [Username, TimelineId, UrlEntryId]) ->
|
||||
EntryId = list_to_integer(UrlEntryId), % TODO: catch non-numbers
|
||||
HTTPMethod = (YArg#arg.req)#http_request.method,
|
||||
|
||||
case HTTPMethod of
|
||||
'GET' -> get_entry(YArg, Username, TimelineId, EntryId);
|
||||
'POST' -> post_entry(YArg, Username, TimelineId, EntryId);
|
||||
'PUT' -> put_entry(YArg, Username, TimelineId, EntryId);
|
||||
'DELETE' -> delete_entry(YArg, Username, TimelineId, EntryId);
|
||||
_Other -> make_json_405(YArg, [{see_docs, "/ts_api_doc/entries.html"}])
|
||||
end;
|
||||
@ -184,8 +173,7 @@ do_login(YArg) ->
|
||||
lists:keyfind(password, 1, Fields)} of
|
||||
|
||||
% username and password found
|
||||
{{username, UnameField}, {password, Password}} ->
|
||||
Username = list_to_atom(UnameField),
|
||||
{{username, Username}, {password, Password}} ->
|
||||
|
||||
% check the uname, password
|
||||
case ts_user:check_credentials(Username, Password) of
|
||||
@ -200,7 +188,7 @@ do_login(YArg) ->
|
||||
[CookieVal])}}];
|
||||
|
||||
% they are not good
|
||||
false -> make_json_401(YArg, [{status,
|
||||
false -> make_json_401(YArg, [{error,
|
||||
"bad username/password combination"}])
|
||||
end;
|
||||
|
||||
@ -224,8 +212,7 @@ get_user_summary(YArg, Username) ->
|
||||
lists:map(fun ts_json:record_to_ejson/1, Timelines)},
|
||||
|
||||
JSONResp = json:encode({struct,
|
||||
[{status, "ok"},
|
||||
{user, EJSONUser},
|
||||
[{user, EJSONUser},
|
||||
{timelines, EJSONTimelines}
|
||||
]}),
|
||||
|
||||
@ -262,9 +249,7 @@ list_timelines(YArg, Username) ->
|
||||
EJSONTimelines = {array, lists:map(fun ts_json:record_to_ejson/1, Timelines)},
|
||||
|
||||
% create resposne
|
||||
JSONResponse = json:encode({struct, [
|
||||
{status, "ok"},
|
||||
{timelines, EJSONTimelines}]}),
|
||||
JSONResponse = json:encode(EJSONTimelines),
|
||||
|
||||
% return response
|
||||
{content, "application/json", JSONResponse}.
|
||||
@ -273,18 +258,22 @@ get_timeline(YArg, Username, TimelineId) ->
|
||||
% look for timeline
|
||||
case ts_timeline:lookup(Username, TimelineId) of
|
||||
% no such timeline, return 404
|
||||
no_record -> make_json_404(YArg, [{status, "no such timeline"}]);
|
||||
no_record -> make_json_404(YArg, [{error, "no such timeline"}]);
|
||||
% return the timeline data
|
||||
Timeline -> make_json_200(YArg, Timeline)
|
||||
end.
|
||||
|
||||
put_timeline(YArg, Username, TimelineId) ->
|
||||
post_timeline(YArg, Username, TimelineId) ->
|
||||
|
||||
% parse the request body
|
||||
EJSON = parse_json_body(YArg),
|
||||
|
||||
% parse into a Timeline record
|
||||
TR = ts_json:ejson_to_record(#ts_timeline{}, EJSON),
|
||||
TR = try ts_json:ejson_to_record(#ts_timeline{}, EJSON)
|
||||
catch _:InputError ->
|
||||
error_logger:error_report("Bad input: ~p", [InputError]),
|
||||
throw(make_json_400(YArg))
|
||||
end,
|
||||
|
||||
% set username and timeline id
|
||||
NewRecord = TR#ts_timeline{ref = {Username, TimelineId}},
|
||||
@ -297,12 +286,7 @@ put_timeline(YArg, Username, TimelineId) ->
|
||||
% will not create, record exists
|
||||
{error, {record_exists, ExistingRecord}} ->
|
||||
|
||||
EJSONRec = ts_json:record_to_ejson(ExistingRecord),
|
||||
JSONResponse = json:encode({struct, [
|
||||
{status, "ignored"},
|
||||
{timeline, EJSONRec},
|
||||
{see_docs, "/ts_api_doc/timelines.html#PUT"}
|
||||
]}),
|
||||
JSONResponse = json:encode(ts_json:record_to_ejson(ExistingRecord)),
|
||||
|
||||
{content, "application/json", JSONResponse};
|
||||
|
||||
@ -311,18 +295,22 @@ put_timeline(YArg, Username, TimelineId) ->
|
||||
make_json_500(YArg, Error)
|
||||
end.
|
||||
|
||||
post_timeline(YArg, Username, TimelineId) ->
|
||||
put_timeline(YArg, Username, TimelineId) ->
|
||||
% parse the POST data
|
||||
EJSON = parse_json_body(YArg),
|
||||
%{struct, Fields} = EJSON,
|
||||
|
||||
% parse into a timeline record
|
||||
TR = ts_json:ejson_to_record(#ts_timeline{}, EJSON),
|
||||
TR = try ts_json:ejson_to_record(#ts_timeline{}, EJSON)
|
||||
catch _:InputError ->
|
||||
error_logger:error_report("Bad input: ~p", [InputError]),
|
||||
throw(make_json_400(YArg))
|
||||
end,
|
||||
|
||||
% not supported right now, would require changing all the entry keys
|
||||
% check to see if they are changing the timeline id
|
||||
%NewTimelineId = case lists:keyfind(1, timeline_id, Fields) of
|
||||
% {timeline_id, Field} -> list_to_atom(Field);
|
||||
% {timeline_id, Field} -> Field;
|
||||
% false -> TimelineId end,
|
||||
|
||||
% set username and timeline id
|
||||
@ -332,8 +320,8 @@ post_timeline(YArg, Username, TimelineId) ->
|
||||
ok -> make_json_200(YArg, NewRecord);
|
||||
|
||||
no_record -> make_json_404(YArg,
|
||||
[{status, "no such timeline"},
|
||||
{see_docs, "/ts_api_doc/timelines.html#POST"}]);
|
||||
[{error, "no such timeline"},
|
||||
{see_docs, "/ts_api_doc/timelines.html#PUT"}]);
|
||||
|
||||
Error ->
|
||||
error_logger:error_report("Unable update timeline: ~p", [Error]),
|
||||
@ -351,7 +339,7 @@ list_entries(YArg, Username, TimelineId) ->
|
||||
lists:keyfind("byDate", 1, QueryData)} of
|
||||
|
||||
{no_record, _ByDateField} -> make_json_404(
|
||||
[{status, "no such timeline"},
|
||||
[{error, "no such timeline"},
|
||||
{see_docs, "/ts_api_doc/entries.html#LIST"}]);
|
||||
|
||||
% listing by date range
|
||||
@ -385,9 +373,7 @@ list_entries(YArg, Username, TimelineId) ->
|
||||
EJSONEntries = {array, lists:map(
|
||||
fun ts_json:record_to_ejson/1, Entries)},
|
||||
|
||||
JSONResponse = json:encode({struct, [
|
||||
{status, "ok"},
|
||||
{entries, EJSONEntries}]}),
|
||||
JSONResponse = json:encode(EJSONEntries),
|
||||
|
||||
{content, "application/json", JSONResponse};
|
||||
|
||||
@ -419,9 +405,7 @@ list_entries(YArg, Username, TimelineId) ->
|
||||
EJSONEntries = {array, lists:map(
|
||||
fun ts_json:record_to_ejson/1, Entries)},
|
||||
|
||||
JSONResponse = json:encode({struct, [
|
||||
{status, "ok"},
|
||||
{entries, EJSONEntries}]}),
|
||||
JSONResponse = json:encode(EJSONEntries),
|
||||
|
||||
{content, "application/json", JSONResponse}
|
||||
end.
|
||||
@ -429,18 +413,22 @@ list_entries(YArg, Username, TimelineId) ->
|
||||
get_entry(YArg, Username, TimelineId, EntryId) ->
|
||||
case ts_entry:lookup(Username, TimelineId, EntryId) of
|
||||
% no such entry
|
||||
no_record -> make_json_404(YArg, [{status, "no such entry"}]);
|
||||
no_record -> make_json_404(YArg, [{error, "no such entry"}]);
|
||||
% return the entry data
|
||||
Entry -> make_json_200(YArg, Entry)
|
||||
end.
|
||||
|
||||
put_entry(YArg, Username, TimelineId) ->
|
||||
post_entry(YArg, Username, TimelineId) ->
|
||||
|
||||
% parse the request body
|
||||
EJSON = parse_json_body(YArg),
|
||||
|
||||
% parse into ts_entry record
|
||||
ER = ts_json:ejson_to_record(#ts_entry{}, EJSON),
|
||||
ER = try ts_json:ejson_to_record(#ts_entry{}, EJSON)
|
||||
catch _:InputError ->
|
||||
error_logger:error_report("Bad input: ~p", [InputError]),
|
||||
throw(make_json_400(YArg))
|
||||
end,
|
||||
|
||||
% set username and timeline id
|
||||
NewRecord = ER#ts_entry{ref = {Username, TimelineId, undef}},
|
||||
@ -452,12 +440,7 @@ put_entry(YArg, Username, TimelineId) ->
|
||||
|
||||
% will not create, record exists
|
||||
{error, {record_exists, ExistingRecord}} ->
|
||||
EJSONRec = ts_json:record_to_ejson(ExistingRecord),
|
||||
JSONResponse = json:encode({struct, [
|
||||
{status, "ignored"},
|
||||
{entry, EJSONRec},
|
||||
{see_docs, "/ts_api_doc/entries.html#PUT"}
|
||||
]}),
|
||||
JSONResponse = json:encode(ts_json:record_to_ejson(ExistingRecord)),
|
||||
|
||||
{content, "application/json", JSONResponse};
|
||||
|
||||
@ -466,13 +449,17 @@ put_entry(YArg, Username, TimelineId) ->
|
||||
make_json_500(YArg, OtherError)
|
||||
end.
|
||||
|
||||
post_entry(YArg, Username, TimelineId, EntryId) ->
|
||||
put_entry(YArg, Username, TimelineId, EntryId) ->
|
||||
|
||||
% parse the POST data
|
||||
EJSON = parse_json_body(YArg),
|
||||
|
||||
% parse into ts_entry record
|
||||
ER = ts_json:ejson_to_record(#ts_entry{}, EJSON),
|
||||
ER = try ts_json:ejson_to_record(#ts_entry{}, EJSON)
|
||||
catch _:InputError ->
|
||||
error_logger:error_report("Bad input: ~p", [InputError]),
|
||||
throw(make_json_400(YArg))
|
||||
end,
|
||||
|
||||
% set uername, timeline id, and entry id
|
||||
NewRecord = ER#ts_entry{ref = {Username, TimelineId, EntryId}},
|
||||
@ -481,7 +468,7 @@ post_entry(YArg, Username, TimelineId, EntryId) ->
|
||||
ok -> make_json_200(YArg, NewRecord);
|
||||
|
||||
no_record -> make_json_404(YArg,
|
||||
[{status, "no such entry"}, {see_docs, "/ts_api_doc/entries.html#POST"}]);
|
||||
[{error, "no such entry"}, {see_docs, "/ts_api_doc/entries.html#POST"}]);
|
||||
|
||||
Error ->
|
||||
error_logger:error_report("TimeStamper: Unable to update entry: ~p", [Error]),
|
||||
@ -509,10 +496,6 @@ delete_entry(YArg, Username, TimelineId, EntryId) ->
|
||||
% ======== UTIL METHODS ======== %
|
||||
% ============================== %
|
||||
|
||||
%% Convert one path element to an atom.
|
||||
path_element_to_atom(PE) ->
|
||||
list_to_atom(re:replace(PE, "\\s", "_", [{return, list}])).
|
||||
|
||||
parse_json_body(YArg) ->
|
||||
case catch json:decode([], binary_to_list(YArg#arg.clidata)) of
|
||||
{done, {ok, EJSON}, _} -> EJSON;
|
||||
@ -524,17 +507,7 @@ parse_json_body(YArg) ->
|
||||
|
||||
%% Create a JSON 200 response.
|
||||
make_json_200(_YArg, Record) ->
|
||||
EJSONRecord = ts_json:record_to_ejson(Record),
|
||||
Tag = case element(1, Record) of
|
||||
ts_user -> user;
|
||||
ts_timeline -> timeline;
|
||||
ts_entry -> entry
|
||||
end,
|
||||
JSONResponse = json:encode({struct, [
|
||||
{status, "ok"},
|
||||
{Tag, EJSONRecord}
|
||||
]}),
|
||||
|
||||
JSONResponse = json:encode(ts_json:record_to_ejson(Record)),
|
||||
{content, "application/json", JSONResponse}.
|
||||
|
||||
make_json_400(YArg) -> make_json_400(YArg, []).
|
||||
@ -574,19 +547,19 @@ make_json_405(YArg) -> make_json_405(YArg, []).
|
||||
make_json_405(YArg, Fields) ->
|
||||
% add default status if not provided
|
||||
F1 = case lists:keyfind(status, 1, Fields) of
|
||||
false -> Fields ++ [{status, method_not_allowed}];
|
||||
false -> Fields ++ [{status, "method not allowed"}];
|
||||
_Else -> Fields
|
||||
end,
|
||||
|
||||
% add the path they requested
|
||||
F2 = F1 ++ [{path, (YArg#arg.req)#http_request.path}],
|
||||
% F2 = F1 ++ [{path, io_lib:format("~s", [(YArg#arg.req)#http_request.path])}],
|
||||
|
||||
[{status, 405}, {content, "application/json", json:encode({struct, F2})}].
|
||||
[{status, 405}, {content, "application/json", json:encode({struct, F1})}].
|
||||
|
||||
make_json_500(_YArg, Error) ->
|
||||
EJSON = {struct, [
|
||||
{status, "internal server error"},
|
||||
{error, io_lib:format("~p", [Error])}]},
|
||||
{error, io_lib:format("~s", [Error])}]},
|
||||
[{status, 500}, {content, "application/json", json:encode(EJSON)}].
|
||||
|
||||
make_json_500(_YArg) ->
|
||||
|
@ -4,8 +4,8 @@
|
||||
pwd_salt,
|
||||
name,
|
||||
email,
|
||||
join_date%,
|
||||
% ext_data % other extensible data
|
||||
join_date,
|
||||
ext_data = [] % other extensible data
|
||||
}).
|
||||
|
||||
% ts_user.ext_data can be:
|
||||
|
@ -7,10 +7,11 @@ encode_record(Record) -> lists:flatten(json:encode(record_to_ejson(Record))).
|
||||
|
||||
record_to_ejson(Record=#ts_user{}) ->
|
||||
{struct, [
|
||||
{username, atom_to_list(Record#ts_user.username)},
|
||||
{id, Record#ts_user.username},
|
||||
{name, Record#ts_user.name},
|
||||
{email, Record#ts_user.email},
|
||||
{join_date, encode_datetime(Record#ts_user.join_date)}]};
|
||||
{join_date, encode_datetime(Record#ts_user.join_date)},
|
||||
{ext_data, Record#ts_user.ext_data}]};
|
||||
|
||||
record_to_ejson(Record=#ts_timeline{}) ->
|
||||
% pull out the username and timeline id
|
||||
@ -18,8 +19,8 @@ record_to_ejson(Record=#ts_timeline{}) ->
|
||||
|
||||
% create the EJSON struct
|
||||
{struct, [
|
||||
{username, atom_to_list(Username)},
|
||||
{timeline_id, atom_to_list(TimelineId)},
|
||||
{user_id, Username},
|
||||
{id, TimelineId},
|
||||
{created, encode_datetime(Record#ts_timeline.created)},
|
||||
{description, Record#ts_timeline.desc}]};
|
||||
|
||||
@ -32,15 +33,15 @@ record_to_ejson(Record=#ts_entry{}) ->
|
||||
|
||||
% create the EJSON struct
|
||||
{struct, [
|
||||
{username, atom_to_list(Username)},
|
||||
{timeline_id, atom_to_list(TimelineId)},
|
||||
{entry_id, EntryId},
|
||||
{user_id, Username},
|
||||
{timeline_id, TimelineId},
|
||||
{id, EntryId},
|
||||
{timestamp, encode_datetime(DateTime)},
|
||||
{mark, Record#ts_entry.mark},
|
||||
{notes, Record#ts_entry.notes}]}.
|
||||
|
||||
encode_datetime({{Year, Month, Day}, {Hour, Minute, Second}}) ->
|
||||
lists:flatten(io_lib:format("~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0B~3.10.0BZ",
|
||||
lists:flatten(io_lib:format("~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0B.~3.10.0BZ",
|
||||
[Year, Month, Day, Hour, Minute, Second, 000])).
|
||||
|
||||
ejson_to_record(_Empty=#ts_timeline{}, EJSON) ->
|
||||
|
@ -2,261 +2,361 @@
|
||||
* 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: inherit; }
|
||||
|
||||
body {
|
||||
width: 50%;
|
||||
background-color: #657a8b;
|
||||
color: #222222;
|
||||
width: 75%;
|
||||
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; }
|
||||
padding: 0; }
|
||||
|
||||
@media all and (min-device-width: 480) {
|
||||
body {
|
||||
width: 50%; } }
|
||||
input {
|
||||
border: solid thin #555555;
|
||||
-webkit-box-shadow: inset 0px 2px 4px #CCC;
|
||||
box-shadow: inset 0px 2px 4px #CCC;
|
||||
margin: 0;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0;
|
||||
font-family: Cantarell; }
|
||||
|
||||
@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; }
|
||||
|
||||
#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 {
|
||||
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;
|
||||
#top {
|
||||
background: #222222;
|
||||
color: #eeeeee;
|
||||
margin: 0;
|
||||
opacity: 1;
|
||||
padding: 0.5em 0;
|
||||
padding: 0.5rem 0;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
width: 75%;
|
||||
z-index: 1; }
|
||||
#top * {
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
#new-entry input {
|
||||
color: #626150; }
|
||||
#new-entry #add-notes {
|
||||
display: none;
|
||||
padding: 0.5em 0 0.5em 2em; }
|
||||
#new-entry #new-entry-input {
|
||||
margin-right: 1em;
|
||||
width: 15em; }
|
||||
#top #fade-bar {
|
||||
background: url("img/fade.png") repeat-x;
|
||||
height: 32px;
|
||||
width: 100%; }
|
||||
|
||||
.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 {
|
||||
#timeline {
|
||||
border-bottom: thin solid #eeeeee;
|
||||
font-family: Arvo;
|
||||
font-size: 1.5em;
|
||||
padding: 0 2em;
|
||||
padding: 0 2rem; }
|
||||
#timeline .timeline-desc {
|
||||
display: inline-block;
|
||||
width: 70%; }
|
||||
#timeline .timeline-desc-input {
|
||||
width: 70%; }
|
||||
#timeline .timeline-id {
|
||||
display: inline-block; }
|
||||
#timeline .timeline-desc-input, #timeline .timeline-id-input {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: #222222;
|
||||
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; }
|
||||
#timeline.edit-id .timeline-id-input {
|
||||
display: inline-block; }
|
||||
#timeline.edit-id .timeline-id {
|
||||
display: none; }
|
||||
#timeline.edit-desc .timeline-desc-input {
|
||||
display: inline-block; }
|
||||
#timeline.edit-desc .timeline-desc {
|
||||
display: none; }
|
||||
#timeline .drop-menu {
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
width: 29%; }
|
||||
#timeline .drop-menu .drop-menu-items {
|
||||
text-align: right;
|
||||
right: 0;
|
||||
width: 172.41%; }
|
||||
#timeline .drop-menu .drop-menu-items .new-timeline-link, #timeline .drop-menu .drop-menu-items .timeline-link {
|
||||
padding-left: 0.5em;
|
||||
font-size: medium; }
|
||||
#timeline .drop-menu .drop-menu-items .new-timeline-link img, #timeline .drop-menu .drop-menu-items .timeline-link img {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
left: -4px; }
|
||||
|
||||
.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; }
|
||||
.dialog {
|
||||
background: white;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
color: #eeeeee;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100em;
|
||||
z-index: 10; }
|
||||
.dialog * {
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
.dialog div.container {
|
||||
background: #222222;
|
||||
border-radius: 10px;
|
||||
font-family: Cantarell;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 4em;
|
||||
padding: 1em;
|
||||
width: 20em; }
|
||||
.dialog div.container h2 {
|
||||
border-bottom: thin solid #eeeeee;
|
||||
font-family: Arvo;
|
||||
padding-bottom: 0.5em;
|
||||
margin-bottom: 0.5em; }
|
||||
.dialog div.container label {
|
||||
display: inline-block;
|
||||
width: 6em; }
|
||||
.dialog div.container input {
|
||||
color: #222222; }
|
||||
.dialog div.container .button-panel {
|
||||
margin-top: 0.5em;
|
||||
overflow: hidden; }
|
||||
.dialog div.container .button-panel .validate-tips {
|
||||
font-size: 1em; }
|
||||
.dialog div.container .button-panel .dialog-button {
|
||||
float: right;
|
||||
padding-left: 1em;
|
||||
font-family: Arvo;
|
||||
font-size: 1.2em; }
|
||||
.dialog div.container .button-panel .dialog-button a {
|
||||
color: #eeeeee; }
|
||||
|
||||
#login-dialog {
|
||||
font-size: small; }
|
||||
#login-dialog input.text {
|
||||
margin-bottom: 1em;
|
||||
width: 95%;
|
||||
padding: 0.4em; }
|
||||
#login-dialog form fieldset {
|
||||
#login.dialog {
|
||||
background: white;
|
||||
opacity: 1; }
|
||||
|
||||
/*#login {
|
||||
|
||||
background: white;
|
||||
color: $lightTxt;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0; }
|
||||
#login-dialog .validate-tips {
|
||||
opacity: 1;
|
||||
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
|
||||
width: 100%;
|
||||
height: 100em;
|
||||
|
||||
z-index: 10;
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.container {
|
||||
|
||||
background: $darkBg;
|
||||
border-radius: 10px;
|
||||
font-family: Cantarell;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 4em;
|
||||
padding: 1em;
|
||||
width: 20em;
|
||||
|
||||
h2 {
|
||||
border-bottom: thin solid $lightBg;
|
||||
font-family: Arvo;
|
||||
padding-bottom: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 6em;
|
||||
}
|
||||
|
||||
input {
|
||||
color: $darkTxt;
|
||||
}
|
||||
|
||||
#login-button {
|
||||
|
||||
text-align: right;
|
||||
margin-top: 0.5em;
|
||||
|
||||
font-family: Arvo;
|
||||
font-size: 1.2em;
|
||||
|
||||
.validate-tips { font-size: 1em; }
|
||||
|
||||
a { color: $lightBg; }
|
||||
}
|
||||
}
|
||||
}*/
|
||||
#user {
|
||||
font-family: "Josefin Sans";
|
||||
margin-top: -0.3em;
|
||||
padding: 0 2em;
|
||||
margin-top: -0.3rem;
|
||||
padding: 0 2rem;
|
||||
width: 100%; }
|
||||
#user .fullname, #user .username {
|
||||
display: inline-block;
|
||||
font-size: larger; }
|
||||
#user .fullname-input {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: #222222;
|
||||
display: none; }
|
||||
#user.edit-fullname .fullname-input {
|
||||
display: inline-block; }
|
||||
#user.edit-fullname .fullname {
|
||||
display: none; }
|
||||
#user .user-menu {
|
||||
display: inline-block; }
|
||||
#user .user-menu .user-menu-items {
|
||||
list-style: none;
|
||||
display: none; }
|
||||
#user .user-menu .user-menu-items li {
|
||||
display: inline-block;
|
||||
padding-left: 0.5em; }
|
||||
#user .user-menu .user-menu-items a {
|
||||
text-decoration: none; }
|
||||
#user .user-menu .user-menu-items a:hover {
|
||||
text-decoration: underline; }
|
||||
#user .user-menu:hover .user-menu-items, #user .user-menu-items:hover {
|
||||
display: inline-block; }
|
||||
|
||||
#ui-dialog-title-login-dialog, .ui-dialog-buttonset {
|
||||
font-size: medium; }
|
||||
#entry-list {
|
||||
margin: 6em 0 0 0;
|
||||
margin: 6rem 0 0 0;
|
||||
padding-bottom: 1em 0 0 0;
|
||||
padding-bottom: 1rem 0 0 0; }
|
||||
#entry-list .day-seperator {
|
||||
background: #cccccc;
|
||||
color: #222222;
|
||||
font-family: Cantarell;
|
||||
font-weight: bold;
|
||||
margin: 1em 0 0 0;
|
||||
margin: 1rem 0 0 0;
|
||||
padding: 0 2em;
|
||||
padding: 0 2rem; }
|
||||
#entry-list .day-seperator * {
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
#entry-list .day-seperator h4, #entry-list .day-seperator h5 {
|
||||
display: inline-block; }
|
||||
#entry-list .day-seperator h5 {
|
||||
color: #667; }
|
||||
#entry-list #new-entry {
|
||||
margin: 0.5em 0 0 0;
|
||||
padding: 0 2em;
|
||||
margin: 0.5rem 0 0 0;
|
||||
padding: 0 2rem; }
|
||||
#entry-list #new-entry * {
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
#entry-list .timestamp, #entry-list .timestamp-input, #entry-list .duration {
|
||||
text-align: right;
|
||||
width: 14%; }
|
||||
#entry-list .mark, #entry-list .mark-input {
|
||||
width: 70%; }
|
||||
#entry-list .entry {
|
||||
font-family: Cantarell;
|
||||
padding: 0 2em;
|
||||
padding: 0 2rem; }
|
||||
#entry-list .entry div {
|
||||
display: inline-block; }
|
||||
#entry-list .entry .mark {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative; }
|
||||
#entry-list .entry .mark * {
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
#entry-list .entry .mark img.expand-entry, #entry-list .entry .mark img.collapse-entry {
|
||||
display: none;
|
||||
left: -20px;
|
||||
position: absolute;
|
||||
top: 6px; }
|
||||
#entry-list .entry:hover .mark img.expand-entry, #entry-list .entry.show-notes img.collapse-entry {
|
||||
display: inline; }
|
||||
#entry-list .entry .mark-input, #entry-list .entry .timestamp-input, #entry-list .entry.show-notes:hover img.expand-entry {
|
||||
display: none; }
|
||||
#entry-list .entry .notes {
|
||||
display: none;
|
||||
font-family: Cantarell;
|
||||
font-size: small;
|
||||
margin: 0;
|
||||
padding: 0 0 0 1em; }
|
||||
#entry-list .entry .notes :first-child {
|
||||
margin-top: 0; }
|
||||
#entry-list .entry .notes .notes-input, #entry-list .entry .notes pre, #entry-list .entry .notes code {
|
||||
font-family: 'Anonymous Pro'; }
|
||||
#entry-list .entry .notes * {
|
||||
width: 100%; }
|
||||
#entry-list .entry.edit-mark .mark-input {
|
||||
display: inline-block; }
|
||||
#entry-list .entry.edit-mark .mark {
|
||||
display: none; }
|
||||
#entry-list .entry.edit-timestamp .timestamp-input {
|
||||
display: inline-block; }
|
||||
#entry-list .entry.edit-timestamp .timestamp {
|
||||
display: none; }
|
||||
#entry-list .entry .notes-input {
|
||||
display: none; }
|
||||
#entry-list .entry.edit-notes .notes-input {
|
||||
display: block; }
|
||||
#entry-list .entry.edit-notes .notes-text {
|
||||
display: none; }
|
||||
|
||||
.signup {
|
||||
.drop-menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative; }
|
||||
.drop-menu * {
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
.drop-menu .drop-menu-items {
|
||||
display: none;
|
||||
list-style: none;
|
||||
position: absolute; }
|
||||
.drop-menu .drop-menu-items li {
|
||||
display: inline-block;
|
||||
padding-left: 0.5em; }
|
||||
.drop-menu:hover .drop-menu-items, .drop-menu .drop-menu-items:hover {
|
||||
display: block; }
|
||||
.drop-menu a {
|
||||
display: inline-block;
|
||||
text-decoration: none; }
|
||||
.drop-menu a:hover {
|
||||
text-decoration: underline; }
|
||||
|
||||
.footer {
|
||||
background: #222222;
|
||||
color: #eeeeee;
|
||||
font-family: Bentham;
|
||||
margin: 0;
|
||||
padding: 1em 0;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
width: 100%; }
|
||||
.footer * {
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
.footer a {
|
||||
color: white;
|
||||
text-decoration: none; }
|
||||
.footer a:hover {
|
||||
text-decoration: underline; }
|
||||
|
||||
.logo {
|
||||
font-family: Bentham;
|
||||
text-decoration: overline underline;
|
||||
color: inherit; }
|
||||
|
||||
.hidden {
|
||||
display: none; }
|
||||
|
||||
#signup-checkbox {
|
||||
display: inline; }
|
||||
|
@ -3,328 +3,474 @@
|
||||
* 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;
|
||||
$darkTxt: #222;
|
||||
$lightTxt: #eee;
|
||||
$darkBg: #222;
|
||||
$lightBg: #eee;
|
||||
$medBg: #CCC;
|
||||
|
||||
* {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 50%;
|
||||
background-color: $ibg;
|
||||
color: $darkTxt;
|
||||
width: 75%;
|
||||
margin: auto;
|
||||
padding: 1em;
|
||||
border: solid $obor;
|
||||
border-top: 0;
|
||||
@include rounded2(bottom, left, 1em);
|
||||
@include rounded2(bottom, right, 1em);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media all and (min-device-width: 480) { body { width: 50%; }}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
input {
|
||||
border: solid thin lighten($darkTxt, 20%);
|
||||
-webkit-box-shadow: inset 0px 2px 4px #CCC;
|
||||
box-shadow: inset 0px 2px 4px #CCC;
|
||||
margin: 0;
|
||||
margin-bottom: 0.5em; // IE fix
|
||||
padding: 0;
|
||||
font-family: Cantarell;
|
||||
}
|
||||
|
||||
.bar {
|
||||
#top {
|
||||
background: $darkBg;
|
||||
color: $lightTxt;
|
||||
margin: 0;
|
||||
opacity: 1;
|
||||
padding: 0.5em 0; // IE Fix
|
||||
padding: 0.5rem 0;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
width: 75%;
|
||||
z-index: 1;
|
||||
|
||||
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;
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
.control-links { padding-top: 0.6em; }
|
||||
|
||||
#fullname, #username {
|
||||
font-weight: bold;
|
||||
font-size: x-large;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#username {
|
||||
padding-left: 0.2em;
|
||||
color: $greyTxt;
|
||||
}
|
||||
|
||||
#change-pwd { display: none; }
|
||||
|
||||
#user-info .form-submit {
|
||||
}
|
||||
}
|
||||
|
||||
#timeline {
|
||||
|
||||
#timeline-name, #timeline-desc { font-weight: bold; }
|
||||
border-bottom: thin solid $lightBg;
|
||||
font-family: Arvo;
|
||||
font-size: 1.5em;
|
||||
padding: 0 2em;
|
||||
padding: 0 2rem;
|
||||
|
||||
#timeline-desc { color: $greyTxt; }
|
||||
.timeline-desc {
|
||||
display: inline-block;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.control-links { padding-top: 0.2em; }
|
||||
.timeline-desc-input { width: 70% }
|
||||
|
||||
.timeline-id { display: inline-block; }
|
||||
|
||||
.timeline-desc-input, .timeline-id-input {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: $darkBg;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.edit-id {
|
||||
.timeline-id-input { display: inline-block; }
|
||||
.timeline-id { display: none; }
|
||||
}
|
||||
|
||||
&.edit-desc {
|
||||
.timeline-desc-input { display: inline-block; }
|
||||
.timeline-desc { display: none; }
|
||||
}
|
||||
|
||||
.drop-menu {
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
width: 29%;
|
||||
|
||||
.drop-menu-items {
|
||||
text-align: right;
|
||||
right: 0;
|
||||
width: 172.41%;
|
||||
|
||||
.new-timeline-link, .timeline-link {
|
||||
padding-left: 0.5em;
|
||||
font-size: medium;
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
left: -4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#user-info, #timeline-info {
|
||||
display: none;
|
||||
.dialog {
|
||||
|
||||
background: white;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
color: $lightTxt;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
|
||||
width: 100%;
|
||||
float: left;
|
||||
height: 100em;
|
||||
|
||||
z-index: 10;
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.container {
|
||||
|
||||
background: $darkBg;
|
||||
border-radius: 10px;
|
||||
font-family: Cantarell;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 4em;
|
||||
padding: 1em;
|
||||
width: 20em;
|
||||
|
||||
h2 {
|
||||
border-bottom: thin solid $lightBg;
|
||||
font-family: Arvo;
|
||||
padding-bottom: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 6em;
|
||||
}
|
||||
|
||||
input {
|
||||
color: $darkTxt;
|
||||
}
|
||||
|
||||
.button-panel {
|
||||
margin-top: 0.5em;
|
||||
overflow: hidden;
|
||||
|
||||
.validate-tips { font-size: 1em; }
|
||||
|
||||
.dialog-button {
|
||||
|
||||
float: right;
|
||||
padding-left: 1em;
|
||||
|
||||
font-family: Arvo;
|
||||
font-size: 1.2em;
|
||||
|
||||
a { color: $lightBg; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#new-entry {
|
||||
#login.dialog {
|
||||
background: white;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@include rounded2(bottom, right, 0.5em);
|
||||
@include rounded2(bottom, left, 0.5em);
|
||||
border-bottom: solid $bbor $iBorWidth;
|
||||
/*#login {
|
||||
|
||||
margin-bottom: 1em;
|
||||
background: white;
|
||||
color: $lightTxt;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
opacity: 1;
|
||||
|
||||
form {
|
||||
border: 0;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
|
||||
width: 100%;
|
||||
height: 100em;
|
||||
|
||||
z-index: 10;
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.container {
|
||||
|
||||
background: $darkBg;
|
||||
border-radius: 10px;
|
||||
font-family: Cantarell;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 4em;
|
||||
padding: 1em;
|
||||
width: 20em;
|
||||
|
||||
h2 {
|
||||
border-bottom: thin solid $lightBg;
|
||||
font-family: Arvo;
|
||||
padding-bottom: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 6em;
|
||||
}
|
||||
|
||||
input {
|
||||
color: $darkTxt;
|
||||
}
|
||||
|
||||
#login-button {
|
||||
|
||||
text-align: right;
|
||||
margin-top: 0.5em;
|
||||
|
||||
font-family: Arvo;
|
||||
font-size: 1.2em;
|
||||
|
||||
.validate-tips { font-size: 1em; }
|
||||
|
||||
a { color: $lightBg; }
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
#user {
|
||||
|
||||
font-family: "Josefin Sans";
|
||||
margin-top: -0.3em; // IE fix
|
||||
padding: 0 2em;
|
||||
margin-top: -0.3rem;
|
||||
padding: 0 2rem;
|
||||
width: 100%;
|
||||
|
||||
.fullname, .username {
|
||||
display: inline-block;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.fullname-input {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: $darkBg;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.edit-fullname{
|
||||
.fullname-input { display: inline-block; }
|
||||
.fullname { display: none; }
|
||||
}
|
||||
|
||||
.user-menu { display: inline-block; }
|
||||
|
||||
.user-menu .user-menu-items {
|
||||
list-style: none;
|
||||
display: none;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
a { text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
}
|
||||
|
||||
.user-menu:hover .user-menu-items, .user-menu-items:hover { display: inline-block; }
|
||||
|
||||
}
|
||||
|
||||
#entry-list {
|
||||
|
||||
margin: 6em 0 0 0;
|
||||
margin: 6rem 0 0 0;
|
||||
padding-bottom: 1em 0 0 0;
|
||||
padding-bottom: 1rem 0 0 0;
|
||||
|
||||
.day-seperator {
|
||||
|
||||
background: $medBg;
|
||||
color: $darkBg;
|
||||
font-family: Cantarell;
|
||||
font-weight: bold;
|
||||
margin: 1em 0 0 0;
|
||||
margin: 1rem 0 0 0;
|
||||
padding: 0 2em;
|
||||
padding: 0 2rem;
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h4, h5 { display: inline-block; }
|
||||
|
||||
h5 { color: #667; }
|
||||
}
|
||||
|
||||
#new-entry {
|
||||
margin: 0.5em 0 0 0;
|
||||
padding: 0 2em;
|
||||
margin: 0.5rem 0 0 0;
|
||||
padding: 0 2rem;
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.timestamp, .timestamp-input, .duration {
|
||||
text-align: right;
|
||||
width: 14%;
|
||||
}
|
||||
|
||||
.mark, .mark-input { width: 70%; }
|
||||
|
||||
.entry {
|
||||
|
||||
font-family: Cantarell;
|
||||
padding: 0 2em;
|
||||
padding: 0 2rem;
|
||||
|
||||
div { display: inline-block; }
|
||||
|
||||
.mark {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
img.expand-entry, img.collapse-entry {
|
||||
display: none;
|
||||
left: -20px;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .mark img.expand-entry, &.show-notes img.collapse-entry { display: inline; }
|
||||
|
||||
.mark-input, .timestamp-input,
|
||||
&.show-notes:hover img.expand-entry { display: none; }
|
||||
|
||||
.notes {
|
||||
display: none;
|
||||
font-family: Cantarell;
|
||||
font-size: small;
|
||||
margin: 0;
|
||||
padding: 0 0 0 1em;
|
||||
|
||||
:first-child { margin-top: 0; }
|
||||
|
||||
.notes-input, pre, code { font-family: 'Anonymous Pro'; }
|
||||
}
|
||||
|
||||
.notes * { width: 100%; }
|
||||
|
||||
&.edit-mark {
|
||||
.mark-input { display: inline-block; }
|
||||
.mark { display: none; }
|
||||
}
|
||||
|
||||
&.edit-timestamp {
|
||||
.timestamp-input { display: inline-block; }
|
||||
.timestamp { display: none; }
|
||||
}
|
||||
|
||||
.notes-input { display: none; }
|
||||
|
||||
&.edit-notes .notes-input { display: block; }
|
||||
|
||||
&.edit-notes .notes-text { display: none; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.drop-menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input { color: $greyTxt; }
|
||||
|
||||
#add-notes {
|
||||
.drop-menu-items {
|
||||
display: none;
|
||||
padding: 0.5em 0 0.5em 2em;
|
||||
}
|
||||
list-style: none;
|
||||
position: absolute;
|
||||
|
||||
#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;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.details {
|
||||
|
||||
float: left;
|
||||
|
||||
.entry-mark {
|
||||
li {
|
||||
display: inline-block;
|
||||
padding-left: 0.5em;
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
font-family: Helvetica, sans-serif;
|
||||
}
|
||||
.entry-notes {
|
||||
display: none;
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.entry-edit {
|
||||
display: none;
|
||||
&:hover .drop-menu-items, .drop-menu-items:hover { display: block; }
|
||||
|
||||
.id {
|
||||
width: 2em;
|
||||
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||
}
|
||||
a {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
|
||||
.entry-notes {
|
||||
padding: 0;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.top-entry {
|
||||
.footer {
|
||||
|
||||
@include rounded2(top, left, 0.5em);
|
||||
@include rounded2(top, right, 0.5em);
|
||||
}
|
||||
background: $darkBg;
|
||||
color: $lightTxt;
|
||||
font-family: Bentham;
|
||||
margin: 0;
|
||||
padding: 1em 0;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
#login-dialog {
|
||||
font-size: small;
|
||||
|
||||
//label, input { display: block }
|
||||
input.text {
|
||||
margin-bottom: 1em;
|
||||
width: 95%;
|
||||
padding: 0.4em;
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
form fieldset {
|
||||
padding: 0; border: 0; margin: 0;
|
||||
a {
|
||||
color: lighten($lightTxt, 20%);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover { text-decoration: underline; }
|
||||
|
||||
}
|
||||
|
||||
.validate-tips { display: none; }
|
||||
}
|
||||
|
||||
#ui-dialog-title-login-dialog, .ui-dialog-buttonset {
|
||||
font-size: medium;
|
||||
.logo {
|
||||
font-family: Bentham;
|
||||
text-decoration: overline underline;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.signup { display: none; }
|
||||
|
||||
#signup-checkbox { display: inline; }
|
||||
.hidden { display: none; }
|
||||
|
BIN
www/img/br_down_icon_16.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
www/img/br_down_icon_24.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
www/img/br_down_icon_32.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
www/img/br_down_icon_48.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
www/img/br_up_icon_16.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
www/img/br_up_icon_24.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
www/img/br_up_icon_32.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
www/img/br_up_icon_48.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
www/img/gentleface-wireframe-icons.zip
Normal file
BIN
www/img/notepad_2_icon_16.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
www/img/notepad_2_icon_24.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
www/img/notepad_2_icon_32.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
www/img/notepad_2_icon_48.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
www/img/pencil_icon&48.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
www/img/pencil_icon_16.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
www/img/pencil_icon_24.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
www/img/pencil_icon_32.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
www/img/round_delete_icon_16.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
www/img/round_delete_icon_24.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
www/img/round_delete_icon_32.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
www/img/round_delete_icon_48.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
www/img/round_plus_icon_16.png
Normal file
After Width: | Height: | Size: 665 B |
BIN
www/img/round_plus_icon_24.png
Normal file
After Width: | Height: | Size: 832 B |
BIN
www/img/round_plus_icon_32.png
Normal file
After Width: | Height: | Size: 902 B |
BIN
www/img/round_plus_icon_48.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
314
www/index.yaws
@ -1,11 +1,10 @@
|
||||
<!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 href='http://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 going to support IE with this tool. -->
|
||||
<!-- 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,195 +12,156 @@
|
||||
<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.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/js/underscore-min.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>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
<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),
|
||||
|
||||
<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>
|
||||
% get the user
|
||||
{content, _, UserJSON} = ts_api:get_user(YArg, Username),
|
||||
UserRecord = ts_user:lookup(Username),
|
||||
|
||||
% get the timelines
|
||||
{content, _, TimelineListJSON} = ts_api:list_timelines(YArg, Username),
|
||||
|
||||
% get the selected timeline
|
||||
SelectedTimeline = case lists:keyfind(
|
||||
selected_timeline, 1, element(8, UserRecord)) of
|
||||
false -> ts_timeline:list(Username, 0, 1);
|
||||
T -> T
|
||||
end,
|
||||
|
||||
% get entries for this timeline
|
||||
{content, _, EntryListJSON} =
|
||||
ts_api:list_entries(YArg, Username, SelectedTimeline),
|
||||
|
||||
{html, f(
|
||||
"function bootstrap() {~n"
|
||||
" var data = {};~n"
|
||||
" data.user = ~p;~n"
|
||||
" data.timelines = ~p;~n"
|
||||
" data.initialTimelineId = ~p;~n"
|
||||
" data.entries = ~p;~n"
|
||||
" return data;~n"
|
||||
"};",
|
||||
[UserJSON, TimelineListJSON, SelectedTimeline, 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>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
</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>
|
||||
|
||||
<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>
|
||||
</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>
|
||||
<!-- == 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 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>
|
||||
<!-- == 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>
|
||||
<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>
|
||||
</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 class="day-seperator">
|
||||
<h4 class="mark">Today</h4>
|
||||
<h5 class="timestamp">start</h5>
|
||||
<h5 class="duration">duration</h5>
|
||||
</div>
|
||||
<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>
|
||||
|
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
419
www/js/showdown-min.js
vendored
Normal file
@ -0,0 +1,419 @@
|
||||
/*
|
||||
A A L Source code at:
|
||||
T C A <http://www.attacklab.net/>
|
||||
T K B
|
||||
*/
|
||||
|
||||
var Showdown={};
|
||||
Showdown.converter=function(){
|
||||
var _1;
|
||||
var _2;
|
||||
var _3;
|
||||
var _4=0;
|
||||
this.makeHtml=function(_5){
|
||||
_1=new Array();
|
||||
_2=new Array();
|
||||
_3=new Array();
|
||||
_5=_5.replace(/~/g,"~T");
|
||||
_5=_5.replace(/\$/g,"~D");
|
||||
_5=_5.replace(/\r\n/g,"\n");
|
||||
_5=_5.replace(/\r/g,"\n");
|
||||
_5="\n\n"+_5+"\n\n";
|
||||
_5=_6(_5);
|
||||
_5=_5.replace(/^[ \t]+$/mg,"");
|
||||
_5=_7(_5);
|
||||
_5=_8(_5);
|
||||
_5=_9(_5);
|
||||
_5=_a(_5);
|
||||
_5=_5.replace(/~D/g,"$$");
|
||||
_5=_5.replace(/~T/g,"~");
|
||||
return _5;
|
||||
};
|
||||
var _8=function(_b){
|
||||
var _b=_b.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,function(_c,m1,m2,m3,m4){
|
||||
m1=m1.toLowerCase();
|
||||
_1[m1]=_11(m2);
|
||||
if(m3){
|
||||
return m3+m4;
|
||||
}else{
|
||||
if(m4){
|
||||
_2[m1]=m4.replace(/"/g,""");
|
||||
}
|
||||
}
|
||||
return "";
|
||||
});
|
||||
return _b;
|
||||
};
|
||||
var _7=function(_12){
|
||||
_12=_12.replace(/\n/g,"\n\n");
|
||||
var _13="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del";
|
||||
var _14="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math";
|
||||
_12=_12.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,_15);
|
||||
_12=_12.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,_15);
|
||||
_12=_12.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,_15);
|
||||
_12=_12.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,_15);
|
||||
_12=_12.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,_15);
|
||||
_12=_12.replace(/\n\n/g,"\n");
|
||||
return _12;
|
||||
};
|
||||
var _15=function(_16,m1){
|
||||
var _18=m1;
|
||||
_18=_18.replace(/\n\n/g,"\n");
|
||||
_18=_18.replace(/^\n/,"");
|
||||
_18=_18.replace(/\n+$/g,"");
|
||||
_18="\n\n~K"+(_3.push(_18)-1)+"K\n\n";
|
||||
return _18;
|
||||
};
|
||||
var _9=function(_19){
|
||||
_19=_1a(_19);
|
||||
var key=_1c("<hr />");
|
||||
_19=_19.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
|
||||
_19=_19.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
|
||||
_19=_19.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);
|
||||
_19=_1d(_19);
|
||||
_19=_1e(_19);
|
||||
_19=_1f(_19);
|
||||
_19=_7(_19);
|
||||
_19=_20(_19);
|
||||
return _19;
|
||||
};
|
||||
var _21=function(_22){
|
||||
_22=_23(_22);
|
||||
_22=_24(_22);
|
||||
_22=_25(_22);
|
||||
_22=_26(_22);
|
||||
_22=_27(_22);
|
||||
_22=_28(_22);
|
||||
_22=_11(_22);
|
||||
_22=_29(_22);
|
||||
_22=_22.replace(/ +\n/g," <br />\n");
|
||||
return _22;
|
||||
};
|
||||
var _24=function(_2a){
|
||||
var _2b=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
|
||||
_2a=_2a.replace(_2b,function(_2c){
|
||||
var tag=_2c.replace(/(.)<\/?code>(?=.)/g,"$1`");
|
||||
tag=_2e(tag,"\\`*_");
|
||||
return tag;
|
||||
});
|
||||
return _2a;
|
||||
};
|
||||
var _27=function(_2f){
|
||||
_2f=_2f.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,_30);
|
||||
_2f=_2f.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,_30);
|
||||
_2f=_2f.replace(/(\[([^\[\]]+)\])()()()()()/g,_30);
|
||||
return _2f;
|
||||
};
|
||||
var _30=function(_31,m1,m2,m3,m4,m5,m6,m7){
|
||||
if(m7==undefined){
|
||||
m7="";
|
||||
}
|
||||
var _39=m1;
|
||||
var _3a=m2;
|
||||
var _3b=m3.toLowerCase();
|
||||
var url=m4;
|
||||
var _3d=m7;
|
||||
if(url==""){
|
||||
if(_3b==""){
|
||||
_3b=_3a.toLowerCase().replace(/ ?\n/g," ");
|
||||
}
|
||||
url="#"+_3b;
|
||||
if(_1[_3b]!=undefined){
|
||||
url=_1[_3b];
|
||||
if(_2[_3b]!=undefined){
|
||||
_3d=_2[_3b];
|
||||
}
|
||||
}else{
|
||||
if(_39.search(/\(\s*\)$/m)>-1){
|
||||
url="";
|
||||
}else{
|
||||
return _39;
|
||||
}
|
||||
}
|
||||
}
|
||||
url=_2e(url,"*_");
|
||||
var _3e="<a href=\""+url+"\"";
|
||||
if(_3d!=""){
|
||||
_3d=_3d.replace(/"/g,""");
|
||||
_3d=_2e(_3d,"*_");
|
||||
_3e+=" title=\""+_3d+"\"";
|
||||
}
|
||||
_3e+=">"+_3a+"</a>";
|
||||
return _3e;
|
||||
};
|
||||
var _26=function(_3f){
|
||||
_3f=_3f.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,_40);
|
||||
_3f=_3f.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,_40);
|
||||
return _3f;
|
||||
};
|
||||
var _40=function(_41,m1,m2,m3,m4,m5,m6,m7){
|
||||
var _49=m1;
|
||||
var _4a=m2;
|
||||
var _4b=m3.toLowerCase();
|
||||
var url=m4;
|
||||
var _4d=m7;
|
||||
if(!_4d){
|
||||
_4d="";
|
||||
}
|
||||
if(url==""){
|
||||
if(_4b==""){
|
||||
_4b=_4a.toLowerCase().replace(/ ?\n/g," ");
|
||||
}
|
||||
url="#"+_4b;
|
||||
if(_1[_4b]!=undefined){
|
||||
url=_1[_4b];
|
||||
if(_2[_4b]!=undefined){
|
||||
_4d=_2[_4b];
|
||||
}
|
||||
}else{
|
||||
return _49;
|
||||
}
|
||||
}
|
||||
_4a=_4a.replace(/"/g,""");
|
||||
url=_2e(url,"*_");
|
||||
var _4e="<img src=\""+url+"\" alt=\""+_4a+"\"";
|
||||
_4d=_4d.replace(/"/g,""");
|
||||
_4d=_2e(_4d,"*_");
|
||||
_4e+=" title=\""+_4d+"\"";
|
||||
_4e+=" />";
|
||||
return _4e;
|
||||
};
|
||||
var _1a=function(_4f){
|
||||
_4f=_4f.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,function(_50,m1){
|
||||
return _1c("<h1>"+_21(m1)+"</h1>");
|
||||
});
|
||||
_4f=_4f.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,function(_52,m1){
|
||||
return _1c("<h2>"+_21(m1)+"</h2>");
|
||||
});
|
||||
_4f=_4f.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,function(_54,m1,m2){
|
||||
var _57=m1.length;
|
||||
return _1c("<h"+_57+">"+_21(m2)+"</h"+_57+">");
|
||||
});
|
||||
return _4f;
|
||||
};
|
||||
var _58;
|
||||
var _1d=function(_59){
|
||||
_59+="~0";
|
||||
var _5a=/^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
|
||||
if(_4){
|
||||
_59=_59.replace(_5a,function(_5b,m1,m2){
|
||||
var _5e=m1;
|
||||
var _5f=(m2.search(/[*+-]/g)>-1)?"ul":"ol";
|
||||
_5e=_5e.replace(/\n{2,}/g,"\n\n\n");
|
||||
var _60=_58(_5e);
|
||||
_60=_60.replace(/\s+$/,"");
|
||||
_60="<"+_5f+">"+_60+"</"+_5f+">\n";
|
||||
return _60;
|
||||
});
|
||||
}else{
|
||||
_5a=/(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
|
||||
_59=_59.replace(_5a,function(_61,m1,m2,m3){
|
||||
var _65=m1;
|
||||
var _66=m2;
|
||||
var _67=(m3.search(/[*+-]/g)>-1)?"ul":"ol";
|
||||
var _66=_66.replace(/\n{2,}/g,"\n\n\n");
|
||||
var _68=_58(_66);
|
||||
_68=_65+"<"+_67+">\n"+_68+"</"+_67+">\n";
|
||||
return _68;
|
||||
});
|
||||
}
|
||||
_59=_59.replace(/~0/,"");
|
||||
return _59;
|
||||
};
|
||||
_58=function(_69){
|
||||
_4++;
|
||||
_69=_69.replace(/\n{2,}$/,"\n");
|
||||
_69+="~0";
|
||||
_69=_69.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,function(_6a,m1,m2,m3,m4){
|
||||
var _6f=m4;
|
||||
var _70=m1;
|
||||
var _71=m2;
|
||||
if(_70||(_6f.search(/\n{2,}/)>-1)){
|
||||
_6f=_9(_72(_6f));
|
||||
}else{
|
||||
_6f=_1d(_72(_6f));
|
||||
_6f=_6f.replace(/\n$/,"");
|
||||
_6f=_21(_6f);
|
||||
}
|
||||
return "<li>"+_6f+"</li>\n";
|
||||
});
|
||||
_69=_69.replace(/~0/g,"");
|
||||
_4--;
|
||||
return _69;
|
||||
};
|
||||
var _1e=function(_73){
|
||||
_73+="~0";
|
||||
_73=_73.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,function(_74,m1,m2){
|
||||
var _77=m1;
|
||||
var _78=m2;
|
||||
_77=_79(_72(_77));
|
||||
_77=_6(_77);
|
||||
_77=_77.replace(/^\n+/g,"");
|
||||
_77=_77.replace(/\n+$/g,"");
|
||||
_77="<pre><code>"+_77+"\n</code></pre>";
|
||||
return _1c(_77)+_78;
|
||||
});
|
||||
_73=_73.replace(/~0/,"");
|
||||
return _73;
|
||||
};
|
||||
var _1c=function(_7a){
|
||||
_7a=_7a.replace(/(^\n+|\n+$)/g,"");
|
||||
return "\n\n~K"+(_3.push(_7a)-1)+"K\n\n";
|
||||
};
|
||||
var _23=function(_7b){
|
||||
_7b=_7b.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(_7c,m1,m2,m3,m4){
|
||||
var c=m3;
|
||||
c=c.replace(/^([ \t]*)/g,"");
|
||||
c=c.replace(/[ \t]*$/g,"");
|
||||
c=_79(c);
|
||||
return m1+"<code>"+c+"</code>";
|
||||
});
|
||||
return _7b;
|
||||
};
|
||||
var _79=function(_82){
|
||||
_82=_82.replace(/&/g,"&");
|
||||
_82=_82.replace(/</g,"<");
|
||||
_82=_82.replace(/>/g,">");
|
||||
_82=_2e(_82,"*_{}[]\\",false);
|
||||
return _82;
|
||||
};
|
||||
var _29=function(_83){
|
||||
_83=_83.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,"<strong>$2</strong>");
|
||||
_83=_83.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,"<em>$2</em>");
|
||||
return _83;
|
||||
};
|
||||
var _1f=function(_84){
|
||||
_84=_84.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(_85,m1){
|
||||
var bq=m1;
|
||||
bq=bq.replace(/^[ \t]*>[ \t]?/gm,"~0");
|
||||
bq=bq.replace(/~0/g,"");
|
||||
bq=bq.replace(/^[ \t]+$/gm,"");
|
||||
bq=_9(bq);
|
||||
bq=bq.replace(/(^|\n)/g,"$1 ");
|
||||
bq=bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm,function(_88,m1){
|
||||
var pre=m1;
|
||||
pre=pre.replace(/^ /mg,"~0");
|
||||
pre=pre.replace(/~0/g,"");
|
||||
return pre;
|
||||
});
|
||||
return _1c("<blockquote>\n"+bq+"\n</blockquote>");
|
||||
});
|
||||
return _84;
|
||||
};
|
||||
var _20=function(_8b){
|
||||
_8b=_8b.replace(/^\n+/g,"");
|
||||
_8b=_8b.replace(/\n+$/g,"");
|
||||
var _8c=_8b.split(/\n{2,}/g);
|
||||
var _8d=new Array();
|
||||
var end=_8c.length;
|
||||
for(var i=0;i<end;i++){
|
||||
var str=_8c[i];
|
||||
if(str.search(/~K(\d+)K/g)>=0){
|
||||
_8d.push(str);
|
||||
}else{
|
||||
if(str.search(/\S/)>=0){
|
||||
str=_21(str);
|
||||
str=str.replace(/^([ \t]*)/g,"<p>");
|
||||
str+="</p>";
|
||||
_8d.push(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
end=_8d.length;
|
||||
for(var i=0;i<end;i++){
|
||||
while(_8d[i].search(/~K(\d+)K/)>=0){
|
||||
var _91=_3[RegExp.$1];
|
||||
_91=_91.replace(/\$/g,"$$$$");
|
||||
_8d[i]=_8d[i].replace(/~K\d+K/,_91);
|
||||
}
|
||||
}
|
||||
return _8d.join("\n\n");
|
||||
};
|
||||
var _11=function(_92){
|
||||
_92=_92.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&");
|
||||
_92=_92.replace(/<(?![a-z\/?\$!])/gi,"<");
|
||||
return _92;
|
||||
};
|
||||
var _25=function(_93){
|
||||
_93=_93.replace(/\\(\\)/g,_94);
|
||||
_93=_93.replace(/\\([`*_{}\[\]()>#+-.!])/g,_94);
|
||||
return _93;
|
||||
};
|
||||
var _28=function(_95){
|
||||
_95=_95.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");
|
||||
_95=_95.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,function(_96,m1){
|
||||
return _98(_a(m1));
|
||||
});
|
||||
return _95;
|
||||
};
|
||||
var _98=function(_99){
|
||||
function char2hex(ch){
|
||||
var _9b="0123456789ABCDEF";
|
||||
var dec=ch.charCodeAt(0);
|
||||
return (_9b.charAt(dec>>4)+_9b.charAt(dec&15));
|
||||
}
|
||||
var _9d=[function(ch){
|
||||
return "&#"+ch.charCodeAt(0)+";";
|
||||
},function(ch){
|
||||
return "&#x"+char2hex(ch)+";";
|
||||
},function(ch){
|
||||
return ch;
|
||||
}];
|
||||
_99="mailto:"+_99;
|
||||
_99=_99.replace(/./g,function(ch){
|
||||
if(ch=="@"){
|
||||
ch=_9d[Math.floor(Math.random()*2)](ch);
|
||||
}else{
|
||||
if(ch!=":"){
|
||||
var r=Math.random();
|
||||
ch=(r>0.9?_9d[2](ch):r>0.45?_9d[1](ch):_9d[0](ch));
|
||||
}
|
||||
}
|
||||
return ch;
|
||||
});
|
||||
_99="<a href=\""+_99+"\">"+_99+"</a>";
|
||||
_99=_99.replace(/">.+:/g,"\">");
|
||||
return _99;
|
||||
};
|
||||
var _a=function(_a3){
|
||||
_a3=_a3.replace(/~E(\d+)E/g,function(_a4,m1){
|
||||
var _a6=parseInt(m1);
|
||||
return String.fromCharCode(_a6);
|
||||
});
|
||||
return _a3;
|
||||
};
|
||||
var _72=function(_a7){
|
||||
_a7=_a7.replace(/^(\t|[ ]{1,4})/gm,"~0");
|
||||
_a7=_a7.replace(/~0/g,"");
|
||||
return _a7;
|
||||
};
|
||||
var _6=function(_a8){
|
||||
_a8=_a8.replace(/\t(?=\t)/g," ");
|
||||
_a8=_a8.replace(/\t/g,"~A~B");
|
||||
_a8=_a8.replace(/~B(.+?)~A/g,function(_a9,m1,m2){
|
||||
var _ac=m1;
|
||||
var _ad=4-_ac.length%4;
|
||||
for(var i=0;i<_ad;i++){
|
||||
_ac+=" ";
|
||||
}
|
||||
return _ac;
|
||||
});
|
||||
_a8=_a8.replace(/~A/g," ");
|
||||
_a8=_a8.replace(/~B/g,"");
|
||||
return _a8;
|
||||
};
|
||||
var _2e=function(_af,_b0,_b1){
|
||||
var _b2="(["+_b0.replace(/([\[\]\\])/g,"\\$1")+"])";
|
||||
if(_b1){
|
||||
_b2="\\\\"+_b2;
|
||||
}
|
||||
var _b3=new RegExp(_b2,"g");
|
||||
_af=_af.replace(_b3,_94);
|
||||
return _af;
|
||||
};
|
||||
var _94=function(_b4,m1){
|
||||
var _b6=m1.charCodeAt(0);
|
||||
return "~E"+_b6+"E";
|
||||
};
|
||||
};
|
||||
if(typeof exports!='undefined')exports.Showdown=Showdown;
|
2663
www/js/showdown.js
131
www/js/test.js
Normal file
@ -0,0 +1,131 @@
|
||||
var Test = {};
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
Test.U = Backbone.Model.extend({
|
||||
url: '/ts_api/users',
|
||||
|
||||
initialize: function() {
|
||||
this.timelines = {};
|
||||
}
|
||||
});
|
||||
|
||||
Test.E = Backbone.Model.extend({
|
||||
url: '/ts_api/entries/jdbernard/work'
|
||||
});
|
||||
|
||||
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() {
|
||||
this.$('.fullname').text(this.model.get('name'));
|
||||
this.$('.username').text(this.model.get('id'));
|
||||
}
|
||||
});
|
||||
|
||||
Test.EList = Backbone.Collection.extend({
|
||||
model: Test.E,
|
||||
|
||||
url: '/ts_api/entries/jdbernard/work',
|
||||
|
||||
initalize: function(models, options) {
|
||||
this.user = options.user;
|
||||
},
|
||||
|
||||
comparator: function(entry) {
|
||||
return entry.get('timestamp');
|
||||
}
|
||||
});
|
||||
|
||||
Test.EView = Backbone.View.extend({
|
||||
|
||||
model: Test.E,
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'render');
|
||||
this.model.bind('change', this.render);
|
||||
this.model.view = this;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
$(this.el).html("<span class='entry-id'>" + this.model.get('id') + "<span class='mark'>" + this.model.get('mark') + "</span>");
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
Test.EListView = Backbone.View.extend({
|
||||
el: $("#entry-list"),
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'render', 'refresh', 'addOne');
|
||||
|
||||
this.collection.bind('add', this.addOne);
|
||||
this.collection.bind('refresh', this.refresh);
|
||||
},
|
||||
|
||||
addOne: function(entry) {
|
||||
var view = new Test.EView({model: entry});
|
||||
$(this.el).append(view.render().el);
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
$(this.el).empty();
|
||||
var thisRef = this;
|
||||
this.collection.each(function(entry) {thisRef.addOne(entry)});
|
||||
}
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
Test.currentEntryList = Test.currentUser.timelines['work'] = new Test.EList([], {user: Test.currentUser});
|
||||
new Test.EListView({collection: Test.currentEntryList});
|
||||
Test.currentEntryList.fetch();
|
||||
}});
|
||||
}
|
993
www/js/ts.js
247
www/prototype.html
Normal file
@ -0,0 +1,247 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>TimeStamper - Simple Time Tracking</title>
|
||||
<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/ts-screen.css" type="text/css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.5.js"></script>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="top">
|
||||
<div id="timeline">
|
||||
<span class="timeline-desc">Work-related activities.</span>
|
||||
<input class="timeline-desc-input" type="text"/>
|
||||
<div class="drop-menu">
|
||||
<div class="timeline-id">( work )</div>
|
||||
<input class="timeline-id-input" type="text"/>
|
||||
<ul class="drop-menu-items">
|
||||
<li class="timeline-link"><a href="#">jdb-labs</a></li>
|
||||
<li class="timeline-link"><a href="#">personal</a></li>
|
||||
<li class="timeline-link"><a href="#">vbs-suite</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="user">
|
||||
<div class="fullname">Jonathan Bernard</div>
|
||||
<input class='fullname-input' type='text'/>
|
||||
<div class='drop-menu'>
|
||||
<div class="username"> - jdbernard</div>
|
||||
<ul class="drop-menu-items">
|
||||
<li><a href="#">logout</a></li>
|
||||
<li><a href="#">user info</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="entry-list">
|
||||
<div class="day-seperator">
|
||||
<h4 class='mark'>Today</h4>
|
||||
<h5 class='timestamp'>start</h5>
|
||||
<h5 class='duration'>duration</h5>
|
||||
</div>
|
||||
<div id="new-entry">
|
||||
<input id="new-entry-input" class="mark-input"
|
||||
placeholder="Start a new task..." type="text" />
|
||||
</div>
|
||||
|
||||
<div id="entries">
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp: Entering tickets.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></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">hr </span>3<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp: Helping Steve wth WR Updates.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">9:56</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">1<span class="tick-tock">hr </span>15<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>Email</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">9:10</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">47<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="day-seperator">
|
||||
<h4 class='mark'>Yesterday</h4>
|
||||
<h5 class='timestamp'>start</h5>
|
||||
<h5 class='duration'>duration</h5>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp: Working #7801.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">3:12 pm</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">12<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>Lunch.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">11:47 am</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">3<span class="tick-tock">hr </span>25<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp: Entering tickets.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">9:20 am</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">2<span class="tick-tock">hr </span>27<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp: Reproducing #7796.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">9:11 am</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">9<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="day-seperator">
|
||||
<h4 class='mark'>Monday</h4>
|
||||
<h5 class='timestamp'>start</h5>
|
||||
<h5 class='duration'>duration</h5>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp: Working #7733.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">16:41</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">1<span class="tick-tock">hr </span>8<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>Zend Training: Building Security Into Your PHP Applications</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">10:30</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">4<span class="tick-tock">hr </span>11<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">8:40</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">1<span class="tick-tock">hr </span>50<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="day-seperator">
|
||||
<h4 class='mark'>Friday, April 29th</h4>
|
||||
<h5 class='timestamp'>start</h5>
|
||||
<h5 class='duration'>duration</h5>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>Training Steve: Databases and SQL</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">16:09</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">55<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>Preparing Instructional Material: Database Basics</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">15:12</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">57<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp: Working #7729.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">15:09</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">3<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">14:44</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">25<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp: Working #7728.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">14:41</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">3<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">14:00</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">41<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>Lunch.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">13:05</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">55<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp: Working #7725.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">12:40</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">20<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>Zend Training: Building Security Into You PHP Applications.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">10:30</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">2<span class="tick-tock">hr </span>10<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>Zend Training: Preparing for security training.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">09:25</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">1<span class="tick-tock">hr </span>5<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<div class="mark"><img src="img/round_delete_icon&16.png" class="delete-icon"/><span>ITHelp: Working #7700.</span><img src="img/notepad_2_icon&16.png" class="notes-icon"/></div>
|
||||
<input class="mark-input" type="text"/>
|
||||
<div class="timestamp">09:17</div>
|
||||
<input class="timestamp-input" type="text"/>
|
||||
<div class="duration">8<span class="tick-tock">m </span></div>
|
||||
<div class="notes">Some notes should go here, but they should be hidden by default</div>
|
||||
</div>
|
||||
</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>
|
38
www/test.html
Normal file
@ -0,0 +1,38 @@
|
||||
<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="entry-list"></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>
|
@ -3,7 +3,7 @@ ebin_dir = /home/jdbernard/projects/jdb-labs/timestamper/web-app/ebin
|
||||
|
||||
runmod = timestamper_dev
|
||||
|
||||
<server timestamper.jdb-labs-local>
|
||||
<server timestamper-local>
|
||||
port = 8000
|
||||
listen = 127.0.0.1
|
||||
docroot = /home/jdbernard/projects/jdb-labs/timestamper/web-app/www
|
||||
|