Version 1.0: Working with TimeStamper API and GTDServlet.
This commit is contained in:
		
							
								
								
									
										14
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					build :
 | 
				
			||||||
 | 
						mkdir -p build/css
 | 
				
			||||||
 | 
						cp src/www/*.* build
 | 
				
			||||||
 | 
						cp -r src/www/js build
 | 
				
			||||||
 | 
						cp -r resources/* build/.
 | 
				
			||||||
 | 
						sass src/www/css/personal-display.scss build/css/personal-display.css
 | 
				
			||||||
 | 
						tar czf personal-display.tar.gz build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					clean :
 | 
				
			||||||
 | 
						rm -r build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local-deploy: build
 | 
				
			||||||
 | 
						cp -r build ~/temp/server
 | 
				
			||||||
 | 
						ssh jdb-server 'rm -r ~/public_html/personal-display; mv temp/build ~/public_html/personal-display'
 | 
				
			||||||
@@ -1,66 +0,0 @@
 | 
				
			|||||||
<!DOCTYPE HTML>
 | 
					 | 
				
			||||||
<html>
 | 
					 | 
				
			||||||
    <head>
 | 
					 | 
				
			||||||
        <title>What I am Doing</title>
 | 
					 | 
				
			||||||
        <link rel="stylesheet" href="css/display.css" type="text/css">
 | 
					 | 
				
			||||||
        <link
 | 
					 | 
				
			||||||
        href='http://fonts.googleapis.com/css?family=Average+Sans|Quicksand:300,400,700|Oleo+Script+Swash+Caps|Ubuntu|Rationale|Exo:200,400|Cantarell|Jura|Play|Seaweed+Script|Rosario|Bubbler+One|Spinnaker|Advent+Pro'
 | 
					 | 
				
			||||||
        rel='stylesheet' type='text/css'>
 | 
					 | 
				
			||||||
        <meta name="viewport" content="width=device-width, user-scalable=no">
 | 
					 | 
				
			||||||
    </head>
 | 
					 | 
				
			||||||
    <body>
 | 
					 | 
				
			||||||
        <section id=current-task>
 | 
					 | 
				
			||||||
            <h3>Current Activity</h3>
 | 
					 | 
				
			||||||
            <span class=task>QD Lobby Map: Meeting with JCA.</span>
 | 
					 | 
				
			||||||
            <div class=task-notes>
 | 
					 | 
				
			||||||
<p>Discussed current display procedure, plans for new display.
 | 
					 | 
				
			||||||
Thoughts for new display:</p>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<ul>
 | 
					 | 
				
			||||||
<li><p>Predefined window of time (TBD) for the orders to be shown on the map. All
 | 
					 | 
				
			||||||
orders displayed will be chosen based on when they were placed.</p></li>
 | 
					 | 
				
			||||||
<li><p>Discussed whether we wanted to have a periodic refresh, meaning we would
 | 
					 | 
				
			||||||
blank the display at the beginning of the week/month and let it fill back in
 | 
					 | 
				
			||||||
as orders come in, or keep a rolling refresh, meaning we would drop orders
 | 
					 | 
				
			||||||
from the display when they are more than a week/month old.</p>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<p>In other words, does the display show the current week/month or does it show
 | 
					 | 
				
			||||||
the last 7/30 days?</p>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<p>The decision was to expirement and see which was a more impressive display.</p></li>
 | 
					 | 
				
			||||||
<li><p>I need to investigate the latest version of KML in case there are new
 | 
					 | 
				
			||||||
features available that would be useful.</p></li>
 | 
					 | 
				
			||||||
</ul>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<p>Other notes:</p>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<ul>
 | 
					 | 
				
			||||||
<li>QD is running Google Earth 7.1 (latest Beta)</li>
 | 
					 | 
				
			||||||
<li>Chris will send me the Order Tour source code.</li>
 | 
					 | 
				
			||||||
<li>We planned to have my old workstation setup and configured with GoToMyPC to
 | 
					 | 
				
			||||||
allow access to the QD development environment.</li>
 | 
					 | 
				
			||||||
</ul>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </section>
 | 
					 | 
				
			||||||
        <section id=priorities>
 | 
					 | 
				
			||||||
            <h3>Next Actions (unsorted)</h3>
 | 
					 | 
				
			||||||
            <div class=next-action>
 | 
					 | 
				
			||||||
                <span class=action>Respond to ELance job proposal.</span>
 | 
					 | 
				
			||||||
                <span class=date>Mon, 08/05</span>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class=next-action>
 | 
					 | 
				
			||||||
                <span class=action>Create matching project folder structure in
 | 
					 | 
				
			||||||
                the <em>done</em> folder as the project folder and move action items
 | 
					 | 
				
			||||||
                appropriately.</span>
 | 
					 | 
				
			||||||
                <span class=project><a href=/project/gtd-cli">GTD CLI</a></span>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class=next-action>
 | 
					 | 
				
			||||||
                <span class=action>Implement category drill down.</span>
 | 
					 | 
				
			||||||
                <span class=project><a href="/project/time-analyzer">Time analyzer software</a></span>
 | 
					 | 
				
			||||||
                <span class=details>Double-clicking on a category should show a
 | 
					 | 
				
			||||||
                graph of sub-items in that category.</span>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </section>
 | 
					 | 
				
			||||||
    </body>
 | 
					 | 
				
			||||||
</html>
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								resources/img/loading-spinner.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/img/loading-spinner.gif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 13 KiB  | 
@@ -110,10 +110,10 @@ body {
 | 
				
			|||||||
    font-family: Advent Pro, Rosario, Jura, Average Sans, Cantarell;
 | 
					    font-family: Advent Pro, Rosario, Jura, Average Sans, Cantarell;
 | 
				
			||||||
    margin: 0.5rem 1rem; }
 | 
					    margin: 0.5rem 1rem; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
section {
 | 
					body > section {
 | 
				
			||||||
    padding: 0.2rem;
 | 
					    padding: 0.2rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    & > h3 {
 | 
					    h3 {
 | 
				
			||||||
        border-bottom: solid 2px $accent1;
 | 
					        border-bottom: solid 2px $accent1;
 | 
				
			||||||
        color: $accent2;
 | 
					        color: $accent2;
 | 
				
			||||||
        font-family: Play, Jura, Exo, Rationale, Quicksand, Average Sans, sans-serif;
 | 
					        font-family: Play, Jura, Exo, Rationale, Quicksand, Average Sans, sans-serif;
 | 
				
			||||||
@@ -131,13 +131,19 @@ section {
 | 
				
			|||||||
    .next-action {
 | 
					    .next-action {
 | 
				
			||||||
        margin-bottom: 0.5rem;
 | 
					        margin-bottom: 0.5rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        span { display: none; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .action { display: inline-block; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .date, .project {
 | 
					        .date, .project {
 | 
				
			||||||
            background: $bgColor2;
 | 
					            background: $bgColor2;
 | 
				
			||||||
            border-radius: 5px;
 | 
					            border-radius: 5px;
 | 
				
			||||||
            color: $mutedFgColor;
 | 
					            color: $mutedFgColor;
 | 
				
			||||||
 | 
					            display: inline-block;
 | 
				
			||||||
            font-size: 66%;
 | 
					            font-size: 66%;
 | 
				
			||||||
 | 
					            margin: 0 0.5rem;
 | 
				
			||||||
            max-width: 33%;
 | 
					            max-width: 33%;
 | 
				
			||||||
            padding: 0 0.5em;
 | 
					            padding: 0 0.5rem;
 | 
				
			||||||
            white-space: nowrap; }
 | 
					            white-space: nowrap; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .details {
 | 
					        .details {
 | 
				
			||||||
@@ -147,12 +153,100 @@ section {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        .project:before {
 | 
					        .project:before {
 | 
				
			||||||
            content: 'Project: ';
 | 
					            content: 'Project: ';
 | 
				
			||||||
 | 
					            display: inline-block;
 | 
				
			||||||
            font-family: Play;
 | 
					            font-family: Play;
 | 
				
			||||||
            font-variant: small-caps; }
 | 
					            font-variant: small-caps; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .date:before {
 | 
					        .date:before {
 | 
				
			||||||
            content: 'Due: ';
 | 
					            content: 'Due: ';
 | 
				
			||||||
 | 
					            display: inline-block;
 | 
				
			||||||
            font-family: Play;
 | 
					            font-family: Play;
 | 
				
			||||||
            font-variant: small-caps; }
 | 
					            font-variant: small-caps; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#config-dialog {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    background: rgba(0, 0, 0, 0.5);
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    bottom: 0;
 | 
				
			||||||
 | 
					    left: 0;
 | 
				
			||||||
 | 
					    right: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    form {
 | 
				
			||||||
 | 
					        border: solid thin $accent1;
 | 
				
			||||||
 | 
					        background: $bgColor;
 | 
				
			||||||
 | 
					        border-radius: 10px;
 | 
				
			||||||
 | 
					        margin-left: 10%;
 | 
				
			||||||
 | 
					        margin-right: 10%;
 | 
				
			||||||
 | 
					        margin-top: 1em;
 | 
				
			||||||
 | 
					        padding: 0 0.5em;
 | 
				
			||||||
 | 
					        position: relative;
 | 
				
			||||||
 | 
					        width: 80%;
 | 
				
			||||||
 | 
					        max-width: 32em; 
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        .validate-tips { display: block; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .wait-overlay {
 | 
				
			||||||
 | 
					            display: none;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            background: rgba(0, 0, 0, 0.8);
 | 
				
			||||||
 | 
					            padding: 1em 0.5em;
 | 
				
			||||||
 | 
					            position: absolute;
 | 
				
			||||||
 | 
					            text-align: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            top: 0;
 | 
				
			||||||
 | 
					            bottom:0;
 | 
				
			||||||
 | 
					            left: 0;
 | 
				
			||||||
 | 
					            right: 0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .button-panel {
 | 
				
			||||||
 | 
					            padding: 0.5em;
 | 
				
			||||||
 | 
					            text-align: right;
 | 
				
			||||||
 | 
					            width: 100%;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					            .global-config { float: left; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .save-button {
 | 
				
			||||||
 | 
					                border: $accent2 solid thin;
 | 
				
			||||||
 | 
					                border-radius: 5px;
 | 
				
			||||||
 | 
					                display: inline-block;
 | 
				
			||||||
 | 
					                padding: 0.1em 0.3em; } } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .config-section-header { color: $accent2; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    label {
 | 
				
			||||||
 | 
					        display: inline-block;
 | 
				
			||||||
 | 
					        width: 6rem; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    input, select { width: 8rem; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .timestamper-config, .gtd-config {
 | 
				
			||||||
 | 
					        vertical-align: top;
 | 
				
			||||||
 | 
					        display: inline-block;
 | 
				
			||||||
 | 
					        width: 15em; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .category-name {
 | 
				
			||||||
 | 
					        display: inline-block;
 | 
				
			||||||
 | 
					        margin-left: 0.2rem;
 | 
				
			||||||
 | 
					        width: 8rem; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .remove-button {
 | 
				
			||||||
 | 
					        color: $mutedFgColor;
 | 
				
			||||||
 | 
					        cursor: pointer;
 | 
				
			||||||
 | 
					        display: inline-block;
 | 
				
			||||||
 | 
					        margin-left: 2rem;
 | 
				
			||||||
 | 
					        width: 4rem; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ul {
 | 
				
			||||||
 | 
					        list-style: none;
 | 
				
			||||||
 | 
					        margin: 0;
 | 
				
			||||||
 | 
					        padding: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        li {
 | 
				
			||||||
 | 
					            display-style: block;
 | 
				
			||||||
 | 
					            margin: 0;
 | 
				
			||||||
 | 
					            padding: 0 } }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										73
									
								
								src/www/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/www/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE HTML>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					    <head>
 | 
				
			||||||
 | 
					        <title>What I am Doing</title>
 | 
				
			||||||
 | 
					        <link rel="stylesheet" href="css/personal-display.css" type="text/css">
 | 
				
			||||||
 | 
					        <link href='//fonts.googleapis.com/css?family=Play|Advent+Pro' rel='stylesheet' type='text/css'>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- PROD Libraries
 | 
				
			||||||
 | 
					        -->
 | 
				
			||||||
 | 
					        <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.0/jquery.min.js" defer></script>
 | 
				
			||||||
 | 
					        <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js" defer></script>
 | 
				
			||||||
 | 
					        <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js" defer></script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- DEV Libraries
 | 
				
			||||||
 | 
					        <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.0/jquery.js" defer></script>
 | 
				
			||||||
 | 
					        <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore.js" defer></script>
 | 
				
			||||||
 | 
					        <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone.js" defer></script>
 | 
				
			||||||
 | 
					        -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <script src="js/personal-display.js" type="text/javascript" defer></script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <meta name="viewport" content="width=device-width, user-scalable=no">
 | 
				
			||||||
 | 
					    </head>
 | 
				
			||||||
 | 
					    <body>
 | 
				
			||||||
 | 
					        <section id=current-task>
 | 
				
			||||||
 | 
					            <h3>Current Activity</h3>
 | 
				
			||||||
 | 
					            <span class=task>Loading...</span>
 | 
				
			||||||
 | 
					            <div class=task-notes></div>
 | 
				
			||||||
 | 
					        </section>
 | 
				
			||||||
 | 
					        <section id=priorities>
 | 
				
			||||||
 | 
					            <h3>Next Actions (unsorted)</h3>
 | 
				
			||||||
 | 
					        </section>
 | 
				
			||||||
 | 
					        <section id=config-dialog>
 | 
				
			||||||
 | 
					            <form>
 | 
				
			||||||
 | 
					                <h3>Configuration</h3>
 | 
				
			||||||
 | 
					                <span class=validate-tips></span>
 | 
				
			||||||
 | 
					                <section class=timestamper-config>
 | 
				
			||||||
 | 
					                    <span class=config-section-header>TimeStamper</span>
 | 
				
			||||||
 | 
					                    <div><label>Server Name: </label>
 | 
				
			||||||
 | 
					                         <input type=text class=host></div>
 | 
				
			||||||
 | 
					                    <div><label>Username: </label>
 | 
				
			||||||
 | 
					                         <input type=text class=username></div>
 | 
				
			||||||
 | 
					                    <div><label>Password: </label>
 | 
				
			||||||
 | 
					                         <input type=password class=password></div>
 | 
				
			||||||
 | 
					                    <div><label>Timeline: </label>
 | 
				
			||||||
 | 
					                         <select class=timeline>
 | 
				
			||||||
 | 
					                            <option value="none">None</option>
 | 
				
			||||||
 | 
					                         </select></div>
 | 
				
			||||||
 | 
					                </section>
 | 
				
			||||||
 | 
					                <section class=gtd-config>
 | 
				
			||||||
 | 
					                    <span class=config-section-header>Getting Things Done</span>
 | 
				
			||||||
 | 
					                    <div><label>Server Name: </label>
 | 
				
			||||||
 | 
					                         <input type=text class=host></div>
 | 
				
			||||||
 | 
					                    <div><label>Username: </label>
 | 
				
			||||||
 | 
					                         <input type=text class=username></div>
 | 
				
			||||||
 | 
					                    <div><label>Password: </label>
 | 
				
			||||||
 | 
					                         <input type=password class=password></div>
 | 
				
			||||||
 | 
					                    <ul> <li><label>Context: </label>
 | 
				
			||||||
 | 
					                         <select class=category>
 | 
				
			||||||
 | 
					                            <option class=default-option value="none">Add a category...</option>
 | 
				
			||||||
 | 
					                         </select></li> </ul>
 | 
				
			||||||
 | 
					                </section>
 | 
				
			||||||
 | 
					                <div class=button-panel>
 | 
				
			||||||
 | 
					                    <div class=global-config>
 | 
				
			||||||
 | 
					                        <label>Refresh (sec): </label>
 | 
				
			||||||
 | 
					                        <input type=text class=refresh></div>
 | 
				
			||||||
 | 
					                    <div class=save-button><a href="#">Save and Close</a></div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class=wait-overlay><img src="img/loading-spinner.gif"><br><span></span></div>
 | 
				
			||||||
 | 
					             </form>
 | 
				
			||||||
 | 
					        </section>
 | 
				
			||||||
 | 
					    </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										499
									
								
								src/www/js/personal-display.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										499
									
								
								src/www/js/personal-display.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,499 @@
 | 
				
			|||||||
 | 
					(function() {
 | 
				
			||||||
 | 
					    var root = this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var PD = root.PersonalDisplay = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PD.hasHTML5LocalStorage = function() {
 | 
				
			||||||
 | 
					          try {
 | 
				
			||||||
 | 
					              return 'localStorage' in window && window['localStorage'] !== null; }
 | 
				
			||||||
 | 
					          catch (e) { return false; } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// ## Models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PD.TimelineMarkModel = Backbone.Model.extend({
 | 
				
			||||||
 | 
					        initialize: function() { _.bindAll(this, "equals"); },
 | 
				
			||||||
 | 
					        equals: function(that) { return this.id == that.id; }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PD.GTDEntryModel = Backbone.Model.extend({
 | 
				
			||||||
 | 
					        initialize: function() { _.bindAll(this, "equals"); },
 | 
				
			||||||
 | 
					        equals: function(that) { return this.id == that.id; }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ## Views
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PD.CurrentActivityView = Backbone.View.extend({
 | 
				
			||||||
 | 
					        el: $("#current-task"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        initialize: function() {
 | 
				
			||||||
 | 
					            _.bindAll(this, "render");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.model.on('change', this.render, this); },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         render: function() {
 | 
				
			||||||
 | 
					            this.$el.find(".task").text(this.model.get("mark"));
 | 
				
			||||||
 | 
					            this.$el.find(".task-notes").text(this.model.get("notes"));
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					         }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PD.GTDNextActionView = Backbone.View.extend({
 | 
				
			||||||
 | 
					        className: "next-action",
 | 
				
			||||||
 | 
					        tagName: "div",
 | 
				
			||||||
 | 
					        ignoredProperties: ["action", "details", "id"],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        initialize: function() { 
 | 
				
			||||||
 | 
					            _.bindAll(this, "render");
 | 
				
			||||||
 | 
					            $("#priorities").append(this.el);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            this.model.on('change', this.render, this); },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        render: function() {
 | 
				
			||||||
 | 
					            var elements = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Look for the "action" property first
 | 
				
			||||||
 | 
					            var actionEl = $(document.createElement("span"))
 | 
				
			||||||
 | 
					                .addClass("action")
 | 
				
			||||||
 | 
					            actionEl.text(this.model.get("action").toString());
 | 
				
			||||||
 | 
					            elements.push(actionEl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Add span elements for each of the other attributes.
 | 
				
			||||||
 | 
					            _.each(this.model.attributes, function(val, key) {
 | 
				
			||||||
 | 
					                if (!_.contains(this.ignoredProperties, key)) {
 | 
				
			||||||
 | 
					                    var el = $(document.createElement("span"))
 | 
				
			||||||
 | 
					                        .addClass(key.toString());
 | 
				
			||||||
 | 
					                    el.text(val);
 | 
				
			||||||
 | 
					                    elements.push(el); } }, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Finally, look for the "details" property
 | 
				
			||||||
 | 
					            if (this.model.get("details")) {
 | 
				
			||||||
 | 
					                var detailEl = $(document.createElement("span"))
 | 
				
			||||||
 | 
					                    .addClass("details");
 | 
				
			||||||
 | 
					                detailEl.text(this.model.get("details").toString());
 | 
				
			||||||
 | 
					                elements.push(detailEl); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Clear the old data and add our new elements in order
 | 
				
			||||||
 | 
					            this.$el.empty();
 | 
				
			||||||
 | 
					            _.each(elements, function(el) { this.$el.append(el); }, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PD.GTDNextActionCollection = Backbone.Collection.extend({
 | 
				
			||||||
 | 
					        model: PD.GTDEntryModel,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        initialize: function() {
 | 
				
			||||||
 | 
					            _.bindAll(this, "addNextActionView", "removeNextActionView");
 | 
				
			||||||
 | 
					            this.views = {};
 | 
				
			||||||
 | 
					            this.on('add', this.addNextActionView);
 | 
				
			||||||
 | 
					            this.on('remove', this.removeNextActionView); },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addNextActionView: function(entryModel) {
 | 
				
			||||||
 | 
					            var view = new PD.GTDNextActionView({model: entryModel});
 | 
				
			||||||
 | 
					            view.render();
 | 
				
			||||||
 | 
					            this.views[entryModel.get("id")] = view; },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        removeNextActionView: function(entryModel) {
 | 
				
			||||||
 | 
					            var view = this.views[entryModel.get("id")];
 | 
				
			||||||
 | 
					            view.$el.remove(); }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PD.ConfigDialog = Backbone.View.extend({
 | 
				
			||||||
 | 
					        el: $("#config-dialog"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        events: {
 | 
				
			||||||
 | 
					            "blur .timestamper-config .password"    : "tsLogin",
 | 
				
			||||||
 | 
					            "blur .gtd-config .password"            : "gtdLogin",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            "change .gtd-config .category"          : "addCategory",
 | 
				
			||||||
 | 
					            "click .remove-button"                  : "removeCategory",
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            "click .save-button"                    : "saveAndClose" },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        initialize: function() {
 | 
				
			||||||
 | 
					            _.bindAll(this, "show", "hide", "tsLogin", "gtdLogin",
 | 
				
			||||||
 | 
					                "loadTsData", "loadGtdData", "addCategory", "makeCategoryItem",
 | 
				
			||||||
 | 
					                "removeCategory", "saveAndClose"); },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        show: function() {
 | 
				
			||||||
 | 
					            var $tsSection = this.$el.find(".timestamper-config");
 | 
				
			||||||
 | 
					            var $gtdSection = this.$el.find(".gtd-config");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Load TimeStamper configuration values.
 | 
				
			||||||
 | 
					            if (PD.tsCfg) {
 | 
				
			||||||
 | 
					                $tsSection.find(".username").val(PD.tsCfg.username);
 | 
				
			||||||
 | 
					                $tsSection.find(".password").val(PD.tsCfg.password);
 | 
				
			||||||
 | 
					                $tsSection.find(".host").val(PD.tsCfg.host);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (PD.tsAuth) {
 | 
				
			||||||
 | 
					                    this.loadTsData(PD.tsCfg.host, PD.tsCfg.username); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                this.$el.find('.timeline').val(PD.tsCfg.timelineId); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Or suggest a default server.
 | 
				
			||||||
 | 
					            else { $tsSection.find(".host").val("timestamper.jdb-labs.com"); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Load GTD configuration values.
 | 
				
			||||||
 | 
					            if (PD.gtdCfg) {
 | 
				
			||||||
 | 
					                $gtdSection.find(".username").val(PD.gtdCfg.username);
 | 
				
			||||||
 | 
					                $gtdSection.find(".password").val(PD.gtdCfg.password);
 | 
				
			||||||
 | 
					                $gtdSection.find(".host").val(PD.gtdCfg.host);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (PD.gtdAuth) { this.loadGtdData(PD.gtdCfg.host); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Create the items for the selected categories
 | 
				
			||||||
 | 
					                $(".category-name").parent().remove();
 | 
				
			||||||
 | 
					                _.forEach(PD.gtdCfg.categories, this.makeCategoryItem); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.$el.find('.refresh').val(
 | 
				
			||||||
 | 
					                PD.refreshPeriod ? PD.refreshPeriod / 1000 : 15);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.$el.fadeIn(); },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        hide: function() { this.$el.fadeOut(); },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tsLogin: function() {
 | 
				
			||||||
 | 
					            var username = this.$el.find(".timestamper-config .username").val();
 | 
				
			||||||
 | 
					            var password = this.$el.find(".timestamper-config .password").val();
 | 
				
			||||||
 | 
					            var host = this.$el.find(".timestamper-config .host").val();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!PD.tsCfg) { PD.tsCfg = {}; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Hide the configuration dialog.
 | 
				
			||||||
 | 
					            this.$el.find(".wait-overlay span").text("Connecting to " + host);
 | 
				
			||||||
 | 
					            this.$el.find(".wait-overlay").fadeIn();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Try to log in to the TimeStamper service.
 | 
				
			||||||
 | 
					            $.ajax({
 | 
				
			||||||
 | 
					                url: "https://" + host + "/ts_api/login",
 | 
				
			||||||
 | 
					                xhrFields: { withCredentials: true },
 | 
				
			||||||
 | 
					                processData: false,
 | 
				
			||||||
 | 
					                type: 'POST',
 | 
				
			||||||
 | 
					                async: false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                data: JSON.stringify(
 | 
				
			||||||
 | 
					                    {"username": username, "password": password}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                error: function(jqXHR, textStatus, error) {
 | 
				
			||||||
 | 
					                    if (jqXHR.status == 401) { $(".validate-tips")
 | 
				
			||||||
 | 
					                        .text("Invalid username/password combination for " +
 | 
				
			||||||
 | 
					                            "the TimeStamper service."); }
 | 
				
			||||||
 | 
					                    else { $(".validate-tips").text("There was an error " +
 | 
				
			||||||
 | 
					                        "trying to log into the TimeStamper service: " +
 | 
				
			||||||
 | 
					                        error); }
 | 
				
			||||||
 | 
					                    PD.tsAuth = false; }, 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                success: function(data, textStatus, jqXHR) {
 | 
				
			||||||
 | 
					                    PD.tsAuth = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $(".validate-tips").text("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Load the user's timelines.
 | 
				
			||||||
 | 
					                    PD.configDialog.loadTsData(host, username);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Success or failure we hide the wait overlay.
 | 
				
			||||||
 | 
					            this.$el.find(".wait-overlay").fadeOut();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }, 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gtdLogin: function() {
 | 
				
			||||||
 | 
					            var username = this.$el.find(".gtd-config .username").val();
 | 
				
			||||||
 | 
					            var password = this.$el.find(".gtd-config .password").val();
 | 
				
			||||||
 | 
					            var host = this.$el.find(".gtd-config .host").val();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!PD.gtdCfg) { PD.gtdCfg = {}; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Hide the configuration dialog.
 | 
				
			||||||
 | 
					            this.$el.find(".wait-overlay span").text("Connecting to " + host);
 | 
				
			||||||
 | 
					            this.$el.find(".wait-overlay").fadeIn();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Try to log in to the GTD service.
 | 
				
			||||||
 | 
					            $.ajax({
 | 
				
			||||||
 | 
					                url: "http://" + host + "/gtd/login",
 | 
				
			||||||
 | 
					                xhrFields: { withCredentials: true },
 | 
				
			||||||
 | 
					                processData: false,
 | 
				
			||||||
 | 
					                type: 'POST',
 | 
				
			||||||
 | 
					                async: false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                data: JSON.stringify(
 | 
				
			||||||
 | 
					                    {"username": username, "password": password}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                error: function(jqXHR, textStatus, error) {
 | 
				
			||||||
 | 
					                    if (jqXHR.status == 401) { $(".validate-tips")
 | 
				
			||||||
 | 
					                        .text("Invalid username/password combination for " +
 | 
				
			||||||
 | 
					                            "the Getting Things Done service."); }
 | 
				
			||||||
 | 
					                    else { $(".validate-tips").text("There was an error " +
 | 
				
			||||||
 | 
					                        "trying to log into the Getting Things Done service: " +
 | 
				
			||||||
 | 
					                        error); }
 | 
				
			||||||
 | 
					                    PD.gtdAuth = false; },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                success: function(data, textStatus, jqXHR) {
 | 
				
			||||||
 | 
					                    PD.gtdAuth = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $(".validate-tips").text("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    PD.configDialog.loadGtdData(host); }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.$el.find(".wait-overlay").fadeOut();
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        loadTsData: function(host, username) {
 | 
				
			||||||
 | 
					            // (Re)load the user's timelines.
 | 
				
			||||||
 | 
					            PD.tsCfg.timelines = JSON.parse($.ajax({
 | 
				
			||||||
 | 
					                url: 'https://' + host + '/ts_api/timelines/' + username,
 | 
				
			||||||
 | 
					                xhrFields: { withCredentials: true },
 | 
				
			||||||
 | 
					                async: false}).responseText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Populate the available timelines list.
 | 
				
			||||||
 | 
					            var $timelineSelectEl = this.$el.find(".timestamper-config .timeline");
 | 
				
			||||||
 | 
					            $timelineSelectEl.empty();
 | 
				
			||||||
 | 
					            _.forEach(PD.tsCfg.timelines, function(timeline) {
 | 
				
			||||||
 | 
					                var $optionEl = $(document.createElement("option"));
 | 
				
			||||||
 | 
					                $optionEl.attr("value", timeline.id);
 | 
				
			||||||
 | 
					                $optionEl.text(timeline.description);
 | 
				
			||||||
 | 
					                $timelineSelectEl.append($optionEl); }); },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        loadGtdData: function(host) {
 | 
				
			||||||
 | 
					            // Load the user's contexts
 | 
				
			||||||
 | 
					            PD.gtdCfg.contexts = JSON.parse($.ajax({
 | 
				
			||||||
 | 
					                url: 'http://' + host + '/gtd/contexts',
 | 
				
			||||||
 | 
					                xhrFields: { withCredentials: true },
 | 
				
			||||||
 | 
					                async: false }).responseText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Load the user's projects
 | 
				
			||||||
 | 
					            PD.gtdCfg.projects = JSON.parse($.ajax({
 | 
				
			||||||
 | 
					                url: 'http://' + host + '/gtd/projects',
 | 
				
			||||||
 | 
					                xhrFields: { withCredentials: true },
 | 
				
			||||||
 | 
					                async: false }).responseText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Populate the available contexts and projects drop-down.
 | 
				
			||||||
 | 
					            var $categorySelectEl = $(".gtd-config .category")
 | 
				
			||||||
 | 
					            $categorySelectEl.empty();
 | 
				
			||||||
 | 
					            $categorySelectEl.append(
 | 
				
			||||||
 | 
					                "<option class=default-option value='none'>" +
 | 
				
			||||||
 | 
					                    "Add a category...</option>");
 | 
				
			||||||
 | 
					            _.forEach(PD.gtdCfg.contexts.concat(PD.gtdCfg.projects),
 | 
				
			||||||
 | 
					              function(category) {
 | 
				
			||||||
 | 
					                var $optionEl = $(document.createElement("option"));
 | 
				
			||||||
 | 
					                $optionEl.attr("value", category.id);
 | 
				
			||||||
 | 
					                $optionEl.text(category.id);
 | 
				
			||||||
 | 
					                $categorySelectEl.append($optionEl); });
 | 
				
			||||||
 | 
					            $categorySelectEl[0].selectedIndex = 0; },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        makeCategoryItem: function(catName) {
 | 
				
			||||||
 | 
					            var $liEl = $(
 | 
				
			||||||
 | 
					                "<li class><span class=remove-button>remove</span>" +
 | 
				
			||||||
 | 
					                "<span class=category-name></span></li>");
 | 
				
			||||||
 | 
					            $liEl.find('.category-name').text(catName);
 | 
				
			||||||
 | 
					            this.$el.find(".gtd-config ul").append($liEl); },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addCategory: function(source) {
 | 
				
			||||||
 | 
					            var selectEl = source.target;
 | 
				
			||||||
 | 
					            var $selectEl = $(selectEl);
 | 
				
			||||||
 | 
					            if (selectEl.selectedIndex == 0) { return; }
 | 
				
			||||||
 | 
					            this.makeCategoryItem($selectEl.val());
 | 
				
			||||||
 | 
					            selectEl.selectedIndex = 0; },
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        removeCategory: function(source) {
 | 
				
			||||||
 | 
					            $(source.target).parent().remove(); },
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        saveAndClose: function() {
 | 
				
			||||||
 | 
					            if (!PD.tsCfg) { PD.tsCfg = {}; }
 | 
				
			||||||
 | 
					            if (!PD.gtdCfg) { PD.gtdCfg = {}; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Save TimeStamper configuration.
 | 
				
			||||||
 | 
					            var $tsEl = this.$el.find(".timestamper-config");
 | 
				
			||||||
 | 
					            PD.tsCfg.host = $tsEl.find(".host").val();
 | 
				
			||||||
 | 
					            PD.tsCfg.username = $tsEl.find(".username").val();
 | 
				
			||||||
 | 
					            PD.tsCfg.password = $tsEl.find(".password").val();
 | 
				
			||||||
 | 
					            PD.tsCfg.timelineId = $tsEl.find(".timeline").val();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Save Getting Things Done configuration.
 | 
				
			||||||
 | 
					            var $gtdEl = this.$el.find(".gtd-config");
 | 
				
			||||||
 | 
					            PD.gtdCfg.host = $gtdEl.find(".host").val();
 | 
				
			||||||
 | 
					            PD.gtdCfg.username = $gtdEl.find(".username").val();
 | 
				
			||||||
 | 
					            PD.gtdCfg.password = $gtdEl.find(".password").val();
 | 
				
			||||||
 | 
					            PD.gtdCfg.categories = _.map(
 | 
				
			||||||
 | 
					                this.$el.find(".category-name"),
 | 
				
			||||||
 | 
					                function(span) { return $(span).text(); });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Save global data
 | 
				
			||||||
 | 
					            PD.refreshPeriod = parseInt(this.$el.find(".refresh").val()) * 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (PD.hasHTML5LocalStorage()) {
 | 
				
			||||||
 | 
					                localStorage.setItem("tsCfg", JSON.stringify(PD.tsCfg));
 | 
				
			||||||
 | 
					                localStorage.setItem("gtdCfg", JSON.stringify(PD.gtdCfg));
 | 
				
			||||||
 | 
					                localStorage.setItem("refreshPeriod",
 | 
				
			||||||
 | 
					                    JSON.stringify(PD.refreshPeriod)); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.hide();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PD.Main = Backbone.View.extend({
 | 
				
			||||||
 | 
					        el: $("body"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        initialize: function() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _.bindAll(this, "refresh");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Create our config dialog view.
 | 
				
			||||||
 | 
					            PD.configDialog = new PD.ConfigDialog();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Create our initial models and views.
 | 
				
			||||||
 | 
					            PD.currentActivityModel = new PD.TimelineMarkModel({});
 | 
				
			||||||
 | 
					            PD.currentActivityView = new PD.CurrentActivityView(
 | 
				
			||||||
 | 
					                {model: PD.currentActivityModel})
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Test for localStorage support
 | 
				
			||||||
 | 
					            if (!PD.hasHTML5LocalStorage()) {
 | 
				
			||||||
 | 
					                alert("Your browser does not support HTML5 localStorage." +
 | 
				
			||||||
 | 
					                    "Without this I cannot store your preferences.");
 | 
				
			||||||
 | 
					                PD.configDialog.show(); }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                PD.tsCfg = JSON.parse(localStorage.getItem('tsCfg'));
 | 
				
			||||||
 | 
					                PD.gtdCfg = JSON.parse(localStorage.getItem('gtdCfg'));
 | 
				
			||||||
 | 
					                PD.refreshPeriod = JSON.parse(
 | 
				
			||||||
 | 
					                    localStorage.getItem('refreshPeriod')); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            PD.gtdNextActionCollection = new PD.GTDNextActionCollection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Perform the initial refresh.
 | 
				
			||||||
 | 
					            this.refresh();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Schedule future refreshes.
 | 
				
			||||||
 | 
					            setInterval(this.refresh, PD.refreshPeriod ? PD.refreshPeriod : 15000);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        refresh: function() {
 | 
				
			||||||
 | 
					            // If the dialog is still open we skip this sync to give the user
 | 
				
			||||||
 | 
					            // a chance to finish configuration.
 | 
				
			||||||
 | 
					            if ($("#config-dialog").is(":visible")) { return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Otherwise, if we do not have configuration information, open the
 | 
				
			||||||
 | 
					            // dialog so the user can enter it.
 | 
				
			||||||
 | 
					            if (!(PD.tsCfg && PD.gtdCfg)) { PD.configDialog.show(); return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Check that we are authenticated to the services we need. Try to
 | 
				
			||||||
 | 
					            // authenticate if we are not.
 | 
				
			||||||
 | 
					            if (!PD.tsAuth) {
 | 
				
			||||||
 | 
					                $.ajax({
 | 
				
			||||||
 | 
					                    url: "https://" + PD.tsCfg.host + "/ts_api/login",
 | 
				
			||||||
 | 
					                    xhrFields: { withCredentials: true },
 | 
				
			||||||
 | 
					                    processData: false,
 | 
				
			||||||
 | 
					                    type: "POST",
 | 
				
			||||||
 | 
					                    async: false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    data: JSON.stringify(
 | 
				
			||||||
 | 
					                      { "username": PD.tsCfg.username,
 | 
				
			||||||
 | 
					                        "password": PD.tsCfg.password }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    error: function(jqXHR, textStatus, error) {
 | 
				
			||||||
 | 
					                        // TODO: Handle error.
 | 
				
			||||||
 | 
					                        PD.tsAuth=false;
 | 
				
			||||||
 | 
					                        alert("Unable to authenticate to the TimeStamper " +
 | 
				
			||||||
 | 
					                            "service: " + error);
 | 
				
			||||||
 | 
					                        PD.configDialog.show(); },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    success: function(data, textStatus, jqXHR) {
 | 
				
			||||||
 | 
					                        PD.tsAuth = true; }}); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!PD.gtdAuth) {
 | 
				
			||||||
 | 
					                $.ajax({
 | 
				
			||||||
 | 
					                    url: "http://" + PD.gtdCfg.host + "/gtd/login",
 | 
				
			||||||
 | 
					                    xhrFields: { withCredentials: true },
 | 
				
			||||||
 | 
					                    processData: false,
 | 
				
			||||||
 | 
					                    type: "POST",
 | 
				
			||||||
 | 
					                    async: false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    data: JSON.stringify(
 | 
				
			||||||
 | 
					                      { "username": PD.gtdCfg.username,
 | 
				
			||||||
 | 
					                        "password": PD.gtdCfg.password }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    error: function(jqXHR, textStatus, error) {
 | 
				
			||||||
 | 
					                        // TODO: Handle error.
 | 
				
			||||||
 | 
					                        PD.gtdAuth=false;
 | 
				
			||||||
 | 
					                        alert("Unable to authenticate to the GTD service: " +
 | 
				
			||||||
 | 
					                            error);
 | 
				
			||||||
 | 
					                        PD.configDialog.show(); },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    success: function(data, textStatus, jqXHR) {
 | 
				
			||||||
 | 
					                        PD.gtdAuth = true; }}); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Check that we have successfully authenticated to both services.
 | 
				
			||||||
 | 
					            // If we are not, we will skip this refresh.
 | 
				
			||||||
 | 
					            if (!(PD.tsAuth && PD.gtdAuth)) { return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Get the latest timestamp from the TimeStamper service.
 | 
				
			||||||
 | 
					            $.ajax({
 | 
				
			||||||
 | 
					                url: "https://" + PD.tsCfg.host + "/ts_api/entries/" +
 | 
				
			||||||
 | 
					                    PD.tsCfg.username + "/" + PD.tsCfg.timelineId,
 | 
				
			||||||
 | 
					                xhrFields: { withCredentials: true },
 | 
				
			||||||
 | 
					                data: {"order": "asc" },
 | 
				
			||||||
 | 
					                dataType: 'json',
 | 
				
			||||||
 | 
					                type: 'GET',
 | 
				
			||||||
 | 
					                async: true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                error: function(jqXHR, textStatus, errorText) {
 | 
				
			||||||
 | 
					                    if (jqXHR.status == 401) { PD.tsAuth = false; }
 | 
				
			||||||
 | 
					                    else {
 | 
				
			||||||
 | 
					                        alert("Unable to retrieve current timestamp: " + errorText);
 | 
				
			||||||
 | 
					                        PD.configDialog.show(); }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                success: function(data, textStatus, jqXHR) {
 | 
				
			||||||
 | 
					                    PD.currentActivityModel.set(data[0]); } 
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Get the list of GTD entries for each of our categories.
 | 
				
			||||||
 | 
					            var categories = _.reduce(
 | 
				
			||||||
 | 
					                PD.gtdCfg.categories,
 | 
				
			||||||
 | 
					                function(acc, cat) { return acc ? acc + "," + cat : cat; }, "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $.ajax({
 | 
				
			||||||
 | 
					                url: "http://" + PD.gtdCfg.host + "/gtd/next-actions/" +
 | 
				
			||||||
 | 
					                    categories,
 | 
				
			||||||
 | 
					                xhrFields: { withCredentials: true },
 | 
				
			||||||
 | 
					                dataType: 'json',
 | 
				
			||||||
 | 
					                type: 'GET',
 | 
				
			||||||
 | 
					                async: true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                error: function(jqXHR, textStatus, errorText) {
 | 
				
			||||||
 | 
					                    if (jqXHR.status == 401) { PD.gtdAtuh = false; }
 | 
				
			||||||
 | 
					                    else if (jqXHR.status == 500) { return; }
 | 
				
			||||||
 | 
					                    else {
 | 
				
			||||||
 | 
					                        alert("Unable to retrieve next actions: " + errorText);
 | 
				
			||||||
 | 
					                        PD.configDialog.show(); }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                success: function(data, textStatus, jqXHR) {
 | 
				
			||||||
 | 
					                    var collection = PD.gtdNextActionCollection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Add all the retrieved items to the collection.
 | 
				
			||||||
 | 
					                    _.forEach(data, function(actionAttr) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Try to find this entry in out collection.
 | 
				
			||||||
 | 
					                        var model = collection.get(actionAttr.id);
 | 
				
			||||||
 | 
					                        // Update it if found
 | 
				
			||||||
 | 
					                        if (model) { model.set(actionAttr); }
 | 
				
			||||||
 | 
					                        // Insert a new model if not found.
 | 
				
			||||||
 | 
					                        else { collection.add(
 | 
				
			||||||
 | 
					                            new PD.GTDEntryModel(actionAttr)); }});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Look through our collection for entries that are no
 | 
				
			||||||
 | 
					                    // longer in our retrieved data and remove them.
 | 
				
			||||||
 | 
					                    collection.forEach(function(model) {
 | 
				
			||||||
 | 
					                        if (!_.any(data, model.equals)) {
 | 
				
			||||||
 | 
					                            collection.remove(model); }});
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PD.main = new PD.Main();
 | 
				
			||||||
 | 
					}).call(this);
 | 
				
			||||||
		Reference in New Issue
	
	Block a user