├── favicon.ico ├── dropbox16.png ├── todotxttdi128.png ├── todotxttdi16.png ├── todotxttdi256.png ├── todotxttdi32.png ├── todotxttdi64.png ├── cute_ball_help.png ├── dropbox_stop32.png ├── cute_ball_stop_help.png ├── js └── GO GET THESE LIBRARIES.md ├── .gitattributes ├── todotxttdi_key.js_SAMPLE ├── .gitignore ├── README.md ├── index.html └── todotxttdi.js /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidPratten/todotxttdi/HEAD/favicon.ico -------------------------------------------------------------------------------- /dropbox16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidPratten/todotxttdi/HEAD/dropbox16.png -------------------------------------------------------------------------------- /todotxttdi128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidPratten/todotxttdi/HEAD/todotxttdi128.png -------------------------------------------------------------------------------- /todotxttdi16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidPratten/todotxttdi/HEAD/todotxttdi16.png -------------------------------------------------------------------------------- /todotxttdi256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidPratten/todotxttdi/HEAD/todotxttdi256.png -------------------------------------------------------------------------------- /todotxttdi32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidPratten/todotxttdi/HEAD/todotxttdi32.png -------------------------------------------------------------------------------- /todotxttdi64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidPratten/todotxttdi/HEAD/todotxttdi64.png -------------------------------------------------------------------------------- /cute_ball_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidPratten/todotxttdi/HEAD/cute_ball_help.png -------------------------------------------------------------------------------- /dropbox_stop32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidPratten/todotxttdi/HEAD/dropbox_stop32.png -------------------------------------------------------------------------------- /cute_ball_stop_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidPratten/todotxttdi/HEAD/cute_ball_stop_help.png -------------------------------------------------------------------------------- /js/GO GET THESE LIBRARIES.md: -------------------------------------------------------------------------------- 1 | #The /js folder needs to contain: 2 | 3 | + 1.8.2.jquery.min.js from [http://jquery.com](http://jquery.com) 4 | + date-en-AU.js from [http://www.datejs.com/](http://www.datejs.com/) 5 | + NB. Update index.html if you choose a different locale than AU 6 | + dropbox.min.js from [https://github.com/dropbox/dropbox-js](https://github.com/dropbox/dropbox-js) 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /todotxttdi_key.js_SAMPLE: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (C) 2013 David Pratten 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | 25 | todotxttdi_key = 'REPLACE THIS MESSAGE WITH YOUR DROPBOX KEY AND RENAME THE FILE todotxttdi_key.js'; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | 66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 67 | #packages/ 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper* 82 | 83 | # Installshield output folder 84 | [Ee]xpress 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish 98 | 99 | # Others 100 | [Bb]in 101 | [Oo]bj 102 | sql 103 | TestResults 104 | *.Cache 105 | ClientBin 106 | stylecop.* 107 | ~$* 108 | *.dbmdl 109 | Generated_Code #added for RIA/Silverlight projects 110 | 111 | # Backup & report files from converting an old project file to a newer 112 | # Visual Studio version. Backup files are not needed, because we have git ;-) 113 | _UpgradeReport_Files/ 114 | Backup*/ 115 | UpgradeLog*.XML 116 | 117 | 118 | 119 | ############ 120 | ## Windows 121 | ############ 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | 130 | ############# 131 | ## Python 132 | ############# 133 | 134 | *.py[co] 135 | 136 | # Packages 137 | *.egg 138 | *.egg-info 139 | dist 140 | build 141 | eggs 142 | parts 143 | bin 144 | var 145 | sdist 146 | develop-eggs 147 | .installed.cfg 148 | 149 | # Installer logs 150 | pip-log.txt 151 | 152 | # Unit test / coverage reports 153 | .coverage 154 | .tox 155 | 156 | #Translations 157 | *.mo 158 | 159 | #Mr Developer 160 | .mr.developer.cfg 161 | 162 | # Mac crap 163 | .DS_Store 164 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### todotxttdi Dropbox application 2 | 3 | todotxttdi (Todo.txt Text Driven Interface) is a self-contained html5 [Todo.txt](http://todotxt.com) client hosted entirely in your Chrome, Firefox, Safari, or Opera browser. When authorised by you, todotxttdi creates a Todo.txt file in your Dropbox, and automatically saves your changes. todotxttdi supports the full Todo.txt [specification](https://github.com/ginatrapani/todo.txt-cli/wiki/The-Todo.txt-Format), with some useful extensions. 4 | 5 | You may use this client at any time by visiting [http://todotxttdi.com/](http://todotxttdi.com/) 6 | 7 | ###Standing on the shoulders of giants 8 | 9 | Todotxttdi uses the following libraries. 10 | 11 | * [jQuery](http://jquery.com) 12 | * [dropbox.js](https://github.com/dropbox/dropbox-js) 13 | * [date-js](http://www.datejs.com/) 14 | 15 | ### The "Zen" of todotxttdi 16 | 17 | todotxttdi is guided by the following principles: 18 | 19 | + The [Todo.txt specification](https://github.com/ginatrapani/todo.txt-cli/wiki/The-Todo.txt-Format) is fully supported 20 | + The user interface is driven by the user's text. (Text Driven Interface). Changing the content of your Todo.txt file may update the user interface so that relevant capabilities are available. 21 | + The Todo.txt file is **always** just a text file and filtering should not hide part of it. 22 | +The Todo.txt file is available to be accessed from multiple locations, computers and browsers with automatic handover from one to the next. Reloading the Todo.txt file will return you to where you left off. 23 | 24 | ###Authorisation with Dropbox 25 | 26 | Once the application page is loaded, Dropbox will ask you if you authorise the application. If you authorise todotxttdi, Dropbox gives your browser access to just one folder: `/Apps/todotxttdi` in your Dropbox. You may close your browser and reload the application without having to reauthorise it. To revoke authorisation for your browser, click the "Disconnect" button. 27 | 28 | The todotxtapi application will only work if your browser has an authorised and active connection with dropbox.com. 29 | 30 | ###Todo.txt editor 31 | 32 | ####Sorting 33 | 34 | todotxttdi sorts lines according to the Todo.txt specification. By default, todotxttdi provides buttons to sort alphabetically (A-Z), by creation date, by priority, by context code, and project code. You may also sort by hashtags if you use them. If a line contains multiple codes, the sort is based on the first code found. 35 | 36 | todotxttdi also supports the sorting by extension tags such as `due:` . When an extension tag is detected, a sort button is added for your use. 37 | 38 | Sorting the Todo.txt file does not trigger a save to Dropbox. This is because each line in a Todo.txt file is independent, and sorting does not change the information that you have entered. 39 | 40 | ####Filtering 41 | 42 | todotxttdi places the lines that match a filter at the top of the Todo.txt file and rather than hiding the non-matching lines, it shows them after two blank lines. This preserves the spirit of Todo.txt - that it is always just a text file. 43 | 44 | todotxttdi allows you to enter up to three independent filters. Filters are applied simply by clicking in the filter field. The currently applied filter has a green background. Filters are remembered by your browser and are not stored in Dropbox. 45 | 46 | Each filter consists of one or more space separated items. For a Todo.txt line to match a filter, it must match every space separated filter item. If a filter item is preceded by a "-" sign, this means a line will match if it doesn't contain this item. If a filter item contains multiple sub-items separated by `|` then a line will match if it matches any one of the sub-items. 47 | 48 | You may use a `~` prefix to temporarily disable a filter item so you don't need to retype it later. 49 | 50 | If you wish to use one of the following characters in a filter you will need to precede them with a "`\`" character. Use `\+` instead of `+`, `\*` instead of `*`, ditto for `\.`, `\(`, and `\)`. And yes, this means, if you care, you may use regular expressions in the filters. 51 | 52 | Prior to applying each filter, todotxttdi analyses all `due:yyyy-mm-dd` dates and adds a hashtag: ` #past`, ` #today`, or "` #tomorrow`" as appropriate. These may be filtered against. For example: the filter "`#past|#today`" will filter for all current Todo.txt lines. 53 | 54 | Filtering the Todo.txt file does not trigger a save to Dropbox. This is because each line in a Todo.txt file is independent, and filtering (like sorting) does not change the information that you have entered. 55 | 56 | ####Editing 57 | 58 | Typing changes into the text area will cause them to be saved to the Todo.txt file the next time you pause your typing. 59 | 60 | todotxttdi simplifies entering dates. The entry of both task completion:` x yyyy-mm-dd`" and due dates: `due:yyyy-mm-dd` are simplified. Dates may be entered as text (e.g. `due:today;`, `due:tomorrow;`, `due:+4;`, `due:+4d;`, `due:monday;`), in each context the dates will be interpreted as soon as the ';' is typed. For example a completed task can be entered as x yesterday; and a reminder set as `due:next thursday;` The kinds of dates that are understood is defined here [http://www.datejs.com/](http://www.datejs.com/) 61 | 62 | ####Task dependency (precedence) 63 | 64 | todotxtdti will alert you when a task is 'ready to run'. 65 | 66 | A task that can't be started until another task is complete may include an tag of `after:xxxx` where the task on which this task depends contains a corresponding `id:xxxx` tag. 67 | 68 | Every `id:` tag must be unique. 69 | 70 | As soon as all tasks on which a task depends are deleted or marked as completed, the now 'ready to run' task will be highlighted in the user interface. 71 | 72 | ###License 73 | 74 | This project/software is licensed under the MIT License. 75 | 76 | Copyright (C) 2013 David Pratten 77 | 78 | Permission is hereby granted, free of charge, to any person obtaining 79 | a copy of this software and associated documentation files (the 80 | "Software"), to deal in the Software without restriction, including 81 | without limitation the rights to use, copy, modify, merge, publish, 82 | distribute, sublicense, and/or sell copies of the Software, and to 83 | permit persons to whom the Software is furnished to do so, subject to 84 | the following conditions: 85 | 86 | The above copyright notice and this permission notice shall be 87 | included in all copies or substantial portions of the Software. 88 | 89 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 90 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 91 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 92 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 93 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 94 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 95 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 96 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | todo.txt Text Driven Interface for Dropbox (Beta) 10 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 126 | 127 | 130 | 131 | 134 | 135 | 144 | 145 | 146 | 147 |
148 |
149 |
150 |

152 | 154 | 155 |

156 |
157 | 158 |
159 |

  

162 |
163 | 164 |
165 | 169 |
170 |
171 | 173 |
174 | 175 |
176 |

Oops, can't connect to Dropbox

177 |

178 |

Reload this page to reconnect!

179 |
180 | 181 |
182 | 183 |

Welcome

184 |

185 | Todo.txt Text Driven Interface for Dropbox (Beta)

Your Todo.txt synchronised on your browsers (except IE) at home and work. 186 | Todo.txt is just a text file, yet you can sort and filter to your heart's content. Sort on your extension tags. Filter on tasks due today or earlier. Enter due dates like due:fri; or due:+7;. Even without a due date, be reminded when when all a task's predecessor tasks are completed.

Click the button below to ask Dropbox to authorise this browser to create and/or 187 | update a Todo.txt file in a specially created /Apps/todotxttdi folder in your Dropbox



Visit Dropbox.com
More information about Todo.txt
More information about App folder authorisation using Dropbox's Core API
Visit the author's blog davidpratten.com

190 | 191 |
192 | 193 | 194 |
195 |

todotxttdi Dropbox application

196 | 197 |

todotxttdi (Todo.txt Text Driven Interface) is a self-contained html5 application 198 | hosted entirely in your Chrome, Firefox, Safari, or Opera browser. When authorised by you, 199 | todotxttdi creates a Todo.txt file in your Dropbox, 200 | and automatically saves your changes. todotxttdi supports the full Todo.txt specification, 202 | with some useful extensions.

203 | 204 |

The "Zen" of todotxttdi

205 | 206 |

todotxttdi is guided by the following principles:

207 | 208 | 222 | 223 |

Authorisation with Dropbox

224 | 225 |

Once the application page is loaded, Dropbox will ask you if you authorise the 226 | application. If you authorise todotxttdi, Dropbox gives your browser access to just 227 | one folder: /Apps/todotxttdi in your Dropbox. You may close 228 | your browser and reload the application without having to reauthorise it. To revoke 229 | authorisation for your browser, click the "Disconnect" button.

230 | 231 |

The todotxtapi application will only work if your browser has an authorised and 232 | active connection with dropbox.com.

233 | 234 |

Todo.txt editor

235 | 236 |

Sorting

237 | 238 |

todotxttdi sorts lines according to the Todo.txt specification. By default, 239 | todotxttdi provides buttons to sort alphabetically (A-Z), by creation date, by 240 | priority, by context code, and project code. You may also sort by hashtags if you use 241 | them. If a line contains multiple codes, the sort is based on the first code 242 | found.

243 | 244 |

todotxttdi also supports the sorting by extension tags such as "due:". When an extension tag is detected, a sort button is added for 246 | your use.

247 | 248 |

Sorting the Todo.txt file does not trigger a save to Dropbox. This is because each 249 | line in a Todo.txt file is independent, and sorting does not change the information 250 | that you have entered.

251 | 252 |

Filtering

253 | 254 |

todotxttdi places the lines that match a filter at the top of the Todo.txt file 255 | and rather than hiding the non-matching lines, it shows them after two blank lines. 256 | This preserves the spirit of Todo.txt - that it is always just a text file.

257 | 258 |

todotxttdi allows you to enter up to three independent filters. Filters are 259 | applied simply by clicking in the filter field. The currently applied filter has a 260 | green background. Filters are remembered by your browser and are not stored in 261 | Dropbox.

262 | 263 |

Each filter consists of one or more space separated items. For a Todo.txt line to 264 | match a filter, it must match every space separated filter item. If a filter item is 265 | preceded by a "-" sign, this means a line will match if it doesn't contain this item. 266 | If a filter item contains multiple sub-items separated by "|" then a line will match 267 | if it matches any one of the sub-items.

268 | 269 |

You may use a "~" prefix to temporarily disable a filter item so you don't need to 270 | retype it later.

271 | 272 |

If you wish to use one of the following characters in a filter you will need to 273 | precede them with a "\" character. Use "\+" instead of "+", "\*" instead of "*", ditto for "\.", "\(", and "\)". And yes, this means, if you care, you may use regular expressions 278 | in the filters.

279 | 280 |

Prior to applying each filter, todotxttdi analyses all due:yyyy-mm-dd dates and adds a hashtag: " #past", " #today", or 283 | " #tomorrow" as appropriate. These may be filtered 284 | against. For example: the filter "#past|#today" will filter 285 | for all current Todo.txt lines.

286 | 287 |

Filtering the Todo.txt file does not trigger a save to Dropbox. This is because 288 | each line in a Todo.txt file is independent, and filtering (like sorting) does not 289 | change the information that you have entered.

290 | 291 |

Editing

292 | 293 |

Typing changes into the text area will cause it to be saved to the Todo.txt file 294 | the next time you pause your typing.

295 | 296 |

todotxttdi greatly simplifies entering dates. The entry of both "task completion" 297 | x yyyy-mm-dd and "due dates" due:yyyy-mm-dd are simplified. Dates may be entered as text (e.g. 299 | due:today;, due:tomorrow;, 300 | due:+4;, due:+4d;, due:monday;), in each context the dates will be interpreted as soon as 302 | the ';' is typed. For example a completed task can be 303 | entered as x yesterday; and a reminder set as due:next thursday; The kinds of dates that are understood is defined 305 | here http://www.datejs.com/

306 | 307 |

Task dependency (precedence)

308 | 309 |

A task that can't be started until another task is complete may include an tag of 310 | after:xxxx where the task on which this task depends 311 | contains a corresponding id:xxxx tag.

312 | 313 |

Every id: tag must be unique.

314 | 315 |

As soon as all tasks on which a task depends are deleted or marked as completed, 316 | the now 'ready to run' task will be highlighted in the user interface.

317 | 318 |

Privacy Policy

319 | 320 |

The only information that is recorded by the todotxttdi.com website is the fact 321 | that the (empty) application page was requested. After your browser loads the (empty) 322 | todotxttdi application page from the todotxttdi.com website, the browser's only 323 | connection is with dropbox.com. Your browser retrieves your Todo.txt and saves it 324 | using a secure (https) connection with Dropbox.com. While editting, your data remains 325 | entirely in your browser.

326 |
327 | 335 | 336 | 337 | -------------------------------------------------------------------------------- /todotxttdi.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (C) 2013 David Pratten 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | 25 | $(document).ready(function () { 26 | "use strict"; 27 | // object to hold all app-wide state 28 | var todotxttdi = {}; 29 | todotxttdi.resizetimer = null; 30 | todotxttdi.reviewtimer = null; 31 | todotxttdi.savertimer = null; 32 | todotxttdi.filtertimer = null; 33 | todotxttdi.helpon = false; 34 | todotxttdi.isDirty = false; 35 | todotxttdi.currFilter = "1"; 36 | todotxttdi.prevwindowMode = null; 37 | todotxttdi.windowMode = null; 38 | todotxttdi.client = null; 39 | todotxttdi.versionTag = null; 40 | todotxttdi.nondirtykeys = [16, 17, 18, 19, 20, 27, 33, 34, 35, 36, 37, 38, 39, 40, 45, 91, 92, 93, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 144, 145, 233]; //see http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes 41 | todotxttdi.alerthashtag = / #(today|past|tomorrow)\b/g; 42 | 43 | $.fn.setCursor = function (newPosition) { 44 | return this.each(function () { 45 | this.focus(); 46 | this.setSelectionRange(newPosition, newPosition); 47 | }); 48 | }; 49 | 50 | Date.prototype.dropboxDateTolocalDateString = function () { 51 | var localDate = new Date(), 52 | work = new Date(); 53 | work.setTime(this.getTime()); 54 | function pad(n) { return n < 10 ? "0" + n : n; } 55 | work.setMinutes(work.getMinutes() - localDate.getTimezoneOffset()); //hack to return dropbox UTC date to local date 56 | return work.getFullYear() + "-" 57 | + pad(work.getMonth() + 1) + "-" 58 | + pad(work.getDate()) + " " 59 | + pad(work.getHours()) + ":" 60 | + pad(work.getMinutes()) + ":" 61 | + pad(work.getSeconds()); 62 | }; 63 | 64 | Array.prototype.getUnique = function () { 65 | var o = {}, 66 | a = [], 67 | i = 0, 68 | e; 69 | e = this[i]; 70 | while (e) { 71 | o[e] = 1; 72 | i += 1; 73 | e = this[i]; 74 | } 75 | for (e in o) { 76 | if (o.hasOwnProperty(e)) { 77 | a.push(e); 78 | } 79 | } 80 | return a; 81 | }; 82 | 83 | function resizet1() { 84 | var t1windowdiff = 172; //$(window).height()-$("#t1").height(); 85 | $("#t1").height($(window).height() - t1windowdiff); 86 | } 87 | 88 | function compareByRegex(a, b, SortRegEx, capture) { 89 | capture = capture !== undefined ? capture : 1; // default to compare regexes first capture 90 | 91 | // any task will sort higher than a completed task 92 | if ((a.substring(0, 2) === "x " && b.substring(0, 2) !== "x ") || (a.substring(0, 2) !== "x " && b.substring(0, 2) === "x ")) { 93 | if (a.substring(0, 2) === "x ") { 94 | return 1; 95 | } 96 | return -1; 97 | } 98 | var aval = a.match(SortRegEx), 99 | bval = b.match(SortRegEx); 100 | 101 | if (aval !== null && bval !== null) { 102 | if (aval[capture] === bval[capture]) { 103 | return 0; 104 | } 105 | if (aval[capture] > bval[capture]) { 106 | return 1; 107 | } 108 | return -1; 109 | } 110 | if (aval === null && bval === null) { 111 | return 0; 112 | } 113 | if (aval !== null) { 114 | return -1; 115 | } 116 | return 1; 117 | } 118 | 119 | function checkalerts(inString) { 120 | var myString = inString, 121 | myRegexp = todotxttdi.alerthashtag, 122 | todaystr = Date.parse("today").toString("yyyy-MM-dd"), 123 | tomorrowstr = Date.parse("tomorrow").toString("yyyy-MM-dd"), 124 | ContentArr = null, 125 | idresults = [], 126 | OutArr = [], 127 | newReady = false; 128 | myString = myString.replace(myRegexp, ""); 129 | // add in alerts based on due dates 130 | myRegexp = /due\:(\d{4}\-\d{2}\-\d{2})/g; //[\-\.a-zA-Z0-9_]+ 131 | 132 | myString = myString.replace(myRegexp, 133 | 134 | function (str, p1, offset, s) { 135 | //console.log(todaystr); 136 | var newAlert = null; 137 | if (todaystr > p1) { 138 | newAlert = " #past"; 139 | } else if (todaystr === p1) { 140 | newAlert = " #today"; 141 | } else if (tomorrowstr === p1) { 142 | newAlert = " #tomorrow"; 143 | } 144 | return str + (newAlert || ""); 145 | }); 146 | 147 | // check for lines that have all the after links cleared 148 | myRegexp = / #ready\b/g; //[\-\.a-zA-Z0-9_]+ 149 | myString = myString.replace(myRegexp, ""); 150 | ContentArr = myString.split(/[\f\n\r]+/); 151 | 152 | // capture all active id: s 153 | myRegexp = /id\:(\S*[a-z0-9_])/i; 154 | ContentArr.forEach(function (x) { 155 | var match = myRegexp.exec(x); 156 | if (match && x.substring(0, 2) !== "x ") { 157 | if (!idresults[match[1]]) { 158 | idresults[match[1]] = true; 159 | } 160 | } 161 | }); 162 | 163 | myRegexp = /after\:(\S*[a-z0-9_])/g; //[\-\.a-zA-Z0-9_]+ 164 | ContentArr.forEach(function (x) { 165 | var found = false, 166 | hasafters = false, 167 | match = null; 168 | match = myRegexp.exec(x); 169 | while (match) { 170 | hasafters = true; 171 | if (idresults[match[1]]) { // always keep completed items in the set not meeting the filter 172 | found = true; 173 | break; 174 | } 175 | match = myRegexp.exec(x); 176 | } 177 | OutArr.push(x + (hasafters && !found ? " #ready" : "")); 178 | if (hasafters && !found) {newReady = true; } 179 | }); 180 | 181 | if (newReady) { 182 | $("#after_msg").html("#ready items are at the top of your list."); 183 | } else { 184 | $("#after_msg").text(""); 185 | } 186 | return OutArr.join("\r\n"); 187 | 188 | } 189 | 190 | function filtert1() { 191 | // check alerts before filtering 192 | var myString = checkalerts($("#t1").val()), 193 | ContentArr = null, 194 | FilterString = null, 195 | FilterStringArr = [], 196 | FilterRegEx = [], 197 | InArr = [], 198 | OutArr = [], 199 | PreArr = []; 200 | 201 | if (myString.length === 0) {return; } 202 | 203 | ContentArr = myString.split(/[\f\n\r]+/); 204 | FilterString = $("#filter" + todotxttdi.currFilter).val(); 205 | 206 | // reset all backgrounds 207 | $("#filter1").css("background-color", "white"); 208 | $("#filter2").css("background-color", "white"); 209 | $("#filter3").css("background-color", "white"); 210 | $("#filter" + todotxttdi.currFilter).css("background-color", "#f0fff0"); 211 | 212 | FilterStringArr = FilterString.split(" "); 213 | 214 | FilterStringArr.forEach(function (x) { 215 | var plainx = "", 216 | negation = false; 217 | try { 218 | if (x.substring(0, 1) === "-") { 219 | if (x.length === 1) {throw "can't negate air"; } 220 | plainx = x.substring(1, 1000); 221 | negation = true; 222 | } else if (x.substring(0, 1) === "~") { 223 | throw "ignore this one"; 224 | } else { 225 | plainx = x; 226 | negation = false; 227 | } 228 | FilterRegEx.push([new RegExp(plainx, "i"), negation]); 229 | $("filter_error_msg").text(""); 230 | } catch (e) { 231 | $("filter_error_msg").text("Filter is not valid"); 232 | return; 233 | } 234 | }); 235 | 236 | localStorage.setItem("filter1", $("#filter1").val()); 237 | localStorage.setItem("filter2", $("#filter2").val()); 238 | localStorage.setItem("filter3", $("#filter3").val()); 239 | localStorage.setItem("currFilter", todotxttdi.currFilter); 240 | ContentArr.forEach(function (x) { 241 | if (/ #ready/.test(x)) { 242 | PreArr.push(x); 243 | } else if (x.substring(0, 2) === "x ") { // completed always outArr 244 | if (x.length > 0) {OutArr.push(x); } 245 | } else { 246 | if (FilterRegEx.length === 0) { 247 | if (x.length > 0) {InArr.push(x); } 248 | } else { 249 | var matched = true; 250 | FilterRegEx.forEach(function (regex) { 251 | //console.log(regex); 252 | if (regex[1]) { // negated 253 | matched = matched && !(regex[0].test(x)); // 254 | } else { 255 | matched = matched && regex[0].test(x); // 256 | } 257 | }); 258 | if (matched) { 259 | InArr.push(x); 260 | } else { 261 | if (x.length > 0) {OutArr.push(x); } 262 | } 263 | } 264 | } 265 | }); 266 | if (PreArr.length + InArr.length + OutArr.length > 0) {$("#t1").val((PreArr.length > 0 ? PreArr.join("\r\n") + "\r\n\r\n" : "") + InArr.join("\r\n") + "\r\n\r\n\r\n" + OutArr.join("\r\n")); } 267 | } 268 | 269 | function reviewt1() { 270 | var myString = $("#t1").val(), 271 | match = null, 272 | myRegexp = /due\:([\-\+a-z 0-9]*);/i, 273 | newDate = null, 274 | dupl = false, 275 | idresults = {}, 276 | results = [], 277 | uniqueresults = null; 278 | 279 | // if there is a due:date to format then reformat it and return without other processing 280 | match = myRegexp.exec(myString); 281 | if (match) { 282 | newDate = Date.parse(match[1] === "" ? "today" : match[1]); // default the empty date to today 283 | if (newDate) { // pass the date literal to datejs from http://www.datejs.com/ to attempt to interpret. 284 | $("#t1").val(myString.replace(myRegexp, "due:" + newDate.toString("yyyy-MM-dd"))).setCursor(match.index + 14); 285 | return; 286 | } 287 | } 288 | 289 | // if there is a x date to format then reformat it and return without other processing 290 | myRegexp = /^x ([\-\+a-z 0-9]*);/im; //[\-\.a-zA-Z0-9_]+ 291 | newDate = null; 292 | match = myRegexp.exec(myString); 293 | if (match) { 294 | newDate = Date.parse(match[1] === "" ? "today" : match[1]); // pass the date literal to datejs from http://www.datejs.com/ to attempt to interpret. 295 | if (newDate) { 296 | $("#t1").val(myString.replace(myRegexp, "x " + newDate.toString("yyyy-MM-dd"))).setCursor(match.index + 12); 297 | return; 298 | } 299 | } 300 | 301 | // check all id:s are unique 302 | myRegexp = /id\:(\S*[a-z0-9_])/g; 303 | match = myRegexp.exec(myString); 304 | while (match) { 305 | if (!idresults[match[1]]) { 306 | idresults[match[1]] = true; 307 | } else { 308 | dupl = true; 309 | break; 310 | } 311 | match = myRegexp.exec(myString); 312 | } 313 | if (dupl) { 314 | $("#duplid_error_msg").text("\"id:" + match[1] + "\" is duplicated."); 315 | } else { 316 | $("#duplid_error_msg").text(""); 317 | } 318 | 319 | // add remove sort buttons 320 | myRegexp = /(^| )([a-zA-Z][a-zA-Z0-9_]+)\:[\-\+a-z0-9_]/gm; 321 | match = myRegexp.exec(myString); 322 | while (match) { 323 | results.push(match[2]); 324 | match = myRegexp.exec(myString); 325 | } 326 | 327 | uniqueresults = results.getUnique(); 328 | 329 | // remove unnecessary sort buttons 330 | $("#sortbuttons button").each(function () { 331 | var extname = null; 332 | if ($(this).attr("id")) {extname = $(this).attr("id").match(/sortbyext(\w+)/); } 333 | if (extname) { 334 | if ($.inArray(extname[1], uniqueresults) === -1) { 335 | $("#sortbyext" + extname[1]).remove(); 336 | } 337 | } 338 | }); 339 | 340 | // add required sort buttons 341 | uniqueresults.forEach(function (x) { 342 | if (!$("#sortbyext" + x).length) { 343 | //create button 344 | $("#sortbuttons").append(""); 345 | // hook to sorting function 346 | $("#sortbyext" + x).click(function (event) { 347 | var myString = $("#t1").val(), 348 | ContentArr = myString.split(/[\f\n\r]+/), 349 | SortRegEx = new RegExp("(^| )" + x + ":([\\-\\+a-z0-9_]+)\\b", "i"); // \ needs to be double escaped 350 | ContentArr.sort( 351 | function (a, b) { 352 | return compareByRegex(a, b, SortRegEx, 2); 353 | } 354 | ); 355 | $("#t1").val(ContentArr.join("\r\n")); 356 | filtert1(); 357 | event.preventDefault(); 358 | }); 359 | } 360 | }); 361 | 362 | } 363 | 364 | function switchWindowMode(mode, message) { 365 | todotxttdi.prevwindowMode = todotxttdi.windowMode; 366 | todotxttdi.windowMode = mode; 367 | if (mode === "welcome") { 368 | $("#welcome").show(); 369 | $("#todotxt").hide(); 370 | $("#helptxt").hide(); 371 | $("#helplink").hide(); 372 | $("#saving_status_msg").text(""); 373 | $("#saving_status_msg").show(); 374 | $("#logout").hide(); 375 | $("#dropboxfail").hide(); 376 | $("#footer").hide(); 377 | } else if (mode === "help") { 378 | $("#welcome").hide(); 379 | $("#todotxt").hide(); 380 | $("#helptxt").show(); 381 | $("#helplink").attr("src", "cute_ball_stop_help.png"); 382 | $("#helplink").attr("title", "Return to Todo.txt"); 383 | $("#helplink").show(); 384 | $("#saving_status_msg").hide(); 385 | $("#logout").hide(); 386 | $("#dropboxfail").hide(); 387 | $("#footer").hide(); 388 | } else if (mode === "error") { 389 | $("#welcome").hide(); 390 | $("#todotxt").hide(); 391 | $("#helptxt").hide(); 392 | $("#helplink").hide(); 393 | $("#saving_status_msg").hide(); 394 | $("#logout").hide(); 395 | $("#reportedmsg").text(message); 396 | $("#dropboxfail").show(); 397 | $("#footer").hide(); 398 | } else if (mode === "use") { 399 | $("#welcome").hide(); 400 | $("#todotxt").show(); 401 | $("#helptxt").hide(); 402 | $("#helplink").attr("src", "cute_ball_help.png"); 403 | $("#helplink").attr("title", "Show Help"); 404 | $("#helplink").show(); 405 | $("#saving_status_msg").text(""); 406 | $("#saving_status_msg").show(); 407 | $("#logout").show(); 408 | $("#dropboxfail").hide(); 409 | $("#footer").show(); 410 | } 411 | } 412 | 413 | function dropBoxFailError(error) { 414 | var errormsg; 415 | if (error.response) { 416 | errormsg = error.response.error; 417 | } else { 418 | errormsg = error.responseText; 419 | } 420 | todotxttdi.isDirty = false; // we abandon the few keystrokes that are unsaved. 421 | switchWindowMode("error", errormsg); 422 | } 423 | 424 | function checkonline(msg) { 425 | todotxttdi.client.stat("todo.txt", function (error, stat) { 426 | if (error) { 427 | dropBoxFailError(error); 428 | return; 429 | } 430 | }); 431 | } 432 | 433 | function loadit() { 434 | todotxttdi.client.readFile("todo.txt", null, function (error, data, stat) { 435 | if (error) { 436 | if (error.response.error === "File has been deleted" || error.response.error === "File not found") { 437 | todotxttdi.client.writeFile("todo.txt", "", function (error, stat) { 438 | if (error) { 439 | dropBoxFailError(error); 440 | return; 441 | } 442 | switchWindowMode("use"); 443 | todotxttdi.isDirty = false; 444 | $("#saving_status_msg").text("Created todo.txt in the /Apps/todotxttdi folder"); 445 | todotxttdi.versionTag = stat.versionTag; 446 | }); 447 | } else { 448 | dropBoxFailError(error); 449 | return; 450 | } 451 | } else { 452 | switchWindowMode("use"); 453 | if (data) { 454 | $("#t1").val(data); 455 | todotxttdi.isDirty = false; 456 | todotxttdi.versionTag = stat.versionTag; 457 | reviewt1(); 458 | filtert1(); 459 | $("#saving_status_msg").text("Latest (" + stat.modifiedAt.dropboxDateTolocalDateString() + ") version retrieved from Dropbox."); 460 | 461 | } 462 | } 463 | }); 464 | 465 | } 466 | 467 | 468 | function authandload() { 469 | todotxttdi.client = new Dropbox.Client({ 470 | key: todotxttdi_key, 471 | sandbox: true 472 | }); 473 | todotxttdi.client.authDriver(new Dropbox.Drivers.Redirect({ 474 | rememberUser: true 475 | })); 476 | 477 | todotxttdi.client.authenticate(function (error, client) { 478 | if (error) { 479 | dropBoxFailError(error); 480 | return; 481 | } 482 | if (!(client.isAuthenticated())) { // an interactive authentication is required. 483 | switchWindowMode("welcome"); 484 | return; 485 | } 486 | loadit(); 487 | $("#logout").append(''); 488 | $("#logoutbutton").click( 489 | function (event) { 490 | if (todotxttdi.isDirty) { 491 | if (!confirm("You have unsaved changes in your todo list. Do you wish to disconnect from Dropbox?")) { 492 | return; 493 | } 494 | } 495 | todotxttdi.isDirty = false; // prevent the window from also catching this. 496 | todotxttdi.client.signOff(function () { 497 | switchWindowMode("welcome"); 498 | localStorage.setItem("userRequestedAuthentication", "false"); 499 | }); 500 | event.preventDefault(); 501 | } 502 | ); 503 | }); 504 | } 505 | 506 | function saveit() { 507 | // save to dropbox 508 | if ($("#t1").val() !== "" && todotxttdi.client.isAuthenticated()) { 509 | todotxttdi.client.stat("todo.txt", function (error, stat) { 510 | if (error) { 511 | dropBoxFailError(error); 512 | return; 513 | } 514 | if (stat.versionTag === todotxttdi.versionTag) { 515 | // save after removing the alert hashtags 516 | todotxttdi.client.writeFile("todo.txt", $("#t1").val().replace(todotxttdi.alerthashtag, ""), function (error, stat) { 517 | if (error) { 518 | dropBoxFailError(error); 519 | return; 520 | } 521 | todotxttdi.isDirty = false; 522 | $("#saving_status_msg").text("All changes saved in Dropbox (" + stat.modifiedAt.dropboxDateTolocalDateString() + ")"); 523 | todotxttdi.versionTag = stat.versionTag; 524 | }); 525 | } else { 526 | $("#saving_status_msg").text("Retrieving..."); 527 | alert("Your todo.txt has been changed using another browser. Retrieving most recent version ... (" + stat.modifiedAt.dropboxDateTolocalDateString() + ")"); 528 | loadit(); 529 | } 530 | }); 531 | } 532 | 533 | } 534 | 535 | window.onbeforeunload = function () { 536 | if (todotxttdi.isDirty) { 537 | return "You have unsaved changes in your todo list."; 538 | } 539 | }; 540 | 541 | // resize window and set up resizer 542 | resizet1(); 543 | $(window).resize(function () { 544 | clearTimeout(todotxttdi.resizetimer); 545 | todotxttdi.resizetimer = setTimeout(resizet1, 250); 546 | }); 547 | 548 | // restore filters from local storage 549 | if (localStorage.getItem("filter1")) {$("#filter1").val(localStorage.getItem("filter1")); } 550 | if (localStorage.getItem("filter2")) {$("#filter2").val(localStorage.getItem("filter2")); } 551 | if (localStorage.getItem("filter3")) {$("#filter3").val(localStorage.getItem("filter3")); } 552 | if (localStorage.getItem("currFilter")) {todotxttdi.currFilter = localStorage.getItem("currFilter"); } 553 | 554 | //auth to dropbox 555 | $("#connecttodropbox").click(function (event) { 556 | localStorage.setItem("userRequestedAuthentication", "true"); 557 | authandload(); 558 | event.preventDefault(); 559 | }); 560 | if (localStorage.getItem("userRequestedAuthentication") === "true") { 561 | authandload(); 562 | } else { 563 | switchWindowMode("welcome"); 564 | } 565 | 566 | $("#t1").keydown(function (e) { 567 | if ($.inArray(e.which, todotxttdi.nondirtykeys) !== -1) { return; } 568 | todotxttdi.isDirty = true; 569 | $("#saving_status_msg").text("Saving..."); 570 | clearTimeout(todotxttdi.reviewtimer); 571 | todotxttdi.reviewtimer = setTimeout(reviewt1, 500); 572 | clearTimeout(todotxttdi.savertimer); 573 | todotxttdi.savertimer = setTimeout(saveit, 5000); 574 | }); 575 | 576 | $("#filter1").keydown(function (e) { 577 | if ($.inArray(e.which, todotxttdi.nondirtykeys) !== -1) { return; } 578 | clearTimeout(todotxttdi.filtertimer); 579 | todotxttdi.currFilter = "1"; 580 | todotxttdi.filtertimer = setTimeout(filtert1, 500); 581 | }); 582 | $("#filter1").focus(function () { 583 | clearTimeout(todotxttdi.filtertimer); 584 | todotxttdi.currFilter = "1"; 585 | todotxttdi.filtertimer = setTimeout(filtert1, 500); 586 | }); 587 | 588 | $("#filter2").keydown(function (e) { 589 | if ($.inArray(e.which, todotxttdi.nondirtykeys) !== -1) { return; } 590 | clearTimeout(todotxttdi.filtertimer); 591 | todotxttdi.currFilter = "2"; 592 | todotxttdi.filtertimer = setTimeout(filtert1, 500); 593 | }); 594 | $("#filter2").focus(function () { 595 | clearTimeout(todotxttdi.filtertimer); 596 | todotxttdi.currFilter = "2"; 597 | todotxttdi.filtertimer = setTimeout(filtert1, 500); 598 | }); 599 | 600 | $("#filter3").keydown(function (e) { 601 | if ($.inArray(e.which, todotxttdi.nondirtykeys) !== -1) { return; } 602 | clearTimeout(todotxttdi.filtertimer); 603 | todotxttdi.currFilter = "3"; 604 | todotxttdi.filtertimer = setTimeout(filtert1, 500); 605 | 606 | }); 607 | $("#filter3").focus(function () { 608 | clearTimeout(todotxttdi.filtertimer); 609 | todotxttdi.currFilter = "3"; 610 | todotxttdi.filtertimer = setTimeout(filtert1, 500); 611 | }); 612 | 613 | $(window).focus(function () { 614 | if (todotxttdi.windowMode === "use") {checkonline(""); } 615 | }); 616 | 617 | $("#helplink").click(function (event) { 618 | if (todotxttdi.helpon) { 619 | switchWindowMode("use"); 620 | todotxttdi.helpon = false; 621 | } else { 622 | switchWindowMode("help"); 623 | todotxttdi.helpon = true; 624 | } 625 | event.preventDefault(); 626 | }); 627 | 628 | $("#sortalphanumeric").click(function (event) { 629 | var myString = $("#t1").val(), 630 | ContentArr = myString.split(/[\f\n\r]+/); 631 | //alert(ContentArr[3]); 632 | ContentArr.sort(); 633 | $("#t1").val(ContentArr.join("\r\n")); 634 | filtert1(); 635 | event.preventDefault(); 636 | }); 637 | 638 | $("#sortbyproject").click(function (event) { 639 | var myString = $("#t1").val(), 640 | ContentArr = myString.split(/[\f\n\r]+/), 641 | SortRegEx = /(^| )\+(\S*[a-z0-9_])\b/i; 642 | ContentArr.sort(function (a, b) { 643 | return compareByRegex(a, b, SortRegEx, 2); 644 | }); 645 | $("#t1").val(ContentArr.join("\r\n")); 646 | filtert1(); 647 | event.preventDefault(); 648 | }); 649 | 650 | $("#sortbypriority").click(function (event) { 651 | var myString = $("#t1").val(), 652 | ContentArr = myString.split(/[\f\n\r]+/), 653 | SortRegEx = /^\(([A-Z])\) /; 654 | ContentArr.sort(function (a, b) { 655 | return compareByRegex(a, b, SortRegEx); 656 | }); 657 | $("#t1").val(ContentArr.join("\r\n")); 658 | filtert1(); 659 | event.preventDefault(); 660 | }); 661 | 662 | $("#sortbycreation").click(function (event) { 663 | var myString = $("#t1").val(), 664 | ContentArr = myString.split(/[\f\n\r]+/), 665 | 666 | //matches the `` creation dates below 667 | // x 2012-09-01 `2011-09-08` @gp checkup on blood sugars due:2012-10-03 668 | // `2011-09-08` @gp checkup on blood sugars due:2012-10-03 669 | // (D) `2011-09-08` @gp checkup on blood sugars due:2012-10-03 670 | 671 | 672 | SortRegEx = /^(?:\([A-Z]\) )?(?:x \d{4}\-\d{2}\-\d{2} )?(\d{4}\-\d{2}\-\d{2})/; 673 | ContentArr.sort(function (a, b) { 674 | return compareByRegex(a, b, SortRegEx); 675 | }); 676 | $("#t1").val(ContentArr.join("\r\n")); 677 | filtert1(); 678 | event.preventDefault(); 679 | }); 680 | 681 | $("#sortbycontext").click(function (event) { 682 | var myString = $("#t1").val(), 683 | ContentArr = myString.split(/[\f\n\r]+/), 684 | SortRegEx = /(^| )@(\S*[a-z0-9_])\b/i; 685 | ContentArr.sort(function (a, b) { 686 | return compareByRegex(a, b, SortRegEx, 2); 687 | }); 688 | $("#t1").val(ContentArr.join("\r\n")); 689 | filtert1(); 690 | event.preventDefault(); 691 | }); 692 | 693 | $("#sortbyhashtag").click(function (event) { 694 | var myString = $("#t1").val(), 695 | ContentArr = myString.split(/[\f\n\r]+/), 696 | SortRegEx = /(^| )#(\S*[a-z0-9_])\b/i; 697 | ContentArr.sort(function (a, b) { 698 | return compareByRegex(a, b, SortRegEx, 2); 699 | }); 700 | $("#t1").val(ContentArr.join("\r\n")); 701 | filtert1(); 702 | event.preventDefault(); 703 | }); 704 | }); --------------------------------------------------------------------------------